Pasting Bold text into a doc after being taken form Spreadsheet - google-apps-script

So I've been working through a problem of pulling structured data from a spreadsheet and them using App Script to insert it into a Template Google Doc.
I have it working simply as concatenated strings, but I'm trying to do it with the BODY class so if I want to put the end product into Gmail it could be easier. Or if I want to retain table structure....
So, everything is fine and dandy, except for this one bit of code. I'm struggling with setBold. It's a weird syntax in that it's a boolean operation, right?
So here is what I have and it's pretty easy to grok I think:
for(var i = 0; i < num; i++) {
var songName = String(dataArray[i][1]);
var sWs = String(dataArray[i][2]);
var pub = String(dataArray[i][3]);
newText.editAsText().appendText('SONG NAME:'+ nLi).setBold(true);
newText.editAsText().appendText(songName + brk).setBold(false);
newText.editAsText().appendText('SONGWRITER(S):' + nLi);
But it's coming out as:
SONG NAME:
I have also tried this code:
newText.editAsText().setBold(true);
newText.editAsText().appendText('SONG NAME:'+ nLi);
newText.editAsText().setBold(true);
newText.editAsText().appendText(songName + brk);
newText.editAsText().appendText('SONGWRITER(S):' + nLi);
Thinking of the setBold as setting then unsetting a flag.
Neither worked.

I prefer using styles:
function addboldtext() {
const doc=DocumentApp.getActiveDocument();
const body=doc.getBody();
const style1={};
style1[DocumentApp.Attribute.BOLD]=true;
style1[DocumentApp.Attribute.FOREGROUND_COLOR]='#000000';//you can add all of the attributes that you wish
body.appendParagraph("This is text").setAttributes(style1)
}

Related

Adding two javascript functions(depending on input)

I have put together a calculator what calculates a price, depending on user input. It works fine with one input, but now I have to scale it a little with a second user input. But here's the catch: the user might not want to put anything to the field, so it will be empty. And that's the thing that brakes my code. I could duplicate the calculator function and return the values and add those two together in a third function, but it will not work when there's an empty value.
Just for the sake of it, some trivial HTML code:
//When I only calculate with this user input, its easy
<input type="text" id="rocktext"><br>
// But how to consider this and do the same exact calculations like with the
//first one and add those two result together?
<input type="text" id="rocktext2"><br>
The code in the end should look like:
Take first user input, calculate the price(like in code below)
IF(!!) there is a second user input, calculate the price and add it to
the first one
Am I being a moron to try it with JS or just a moron in the firstplace?
Hope to hear from You, guys!
J.
The initial JS code is as follows:
function priceCalc() {
var inputs = document.getElementById("rocktext").value;
var length = inputs.length;
var accept = 6;
var initPrice = 8;
if (inputs<=accept){
// Since the code is much simpler right now i just put the result in HTML as follows:
document.getElementById("rockpricetotal").innerHTML = initPrice + " dollars";
//I can also return the the value calculated here like so:
//retVal = initPrice;
}
else {
var intLength = parseInt(length, 10);
var lengthGap = intLength - accept;
var totals = lengthGap * 0.8 + initPrice;
var prec = totals.toPrecision(3);
// Since the code is much simpler right now i just put the result in HTML as follows:
document.getElementById("rockpricetotal").innerHTML = prec + " dollars";
// Here also the return clause can be possible with the calculation result like so:
//retVal = prec;
}
// And the final return as an alternative to the innerHTML :
// return retVal;
}
Making it scalable, you can add a class to all the inputs which may be in the function (something like calcInput), so you iterate all of them and if the value isn't empty (and if it's a valid number), you put it in the calculation.
Or you can just verify if the second input is empty, if so, calls functionOne, if not, calls functionTwo:
function twoDifferentWays() {
var valueOne = document.querySelector("#rocktext").value;
var valueTwo = document.querySelector("#rocktext2").value;
if (!!valueTwo && !isNaN(valueTwo)) {
callsFunctionOne(valueOne, valueTwo);
} else {
callsFunctionTwo(valueOne, valueTwo);
}
}

KendoUI export to excel not rending footer properly

I have a column on my kendoUi grid that has anchor tags for the entire column including the footer, here is my code for that:
columns.Bound(p => p.NonFlagged).Title("Non Flagged").Width(100)
.ClientTemplate(
"<a onclick='ShowPatientGapDetailsModal(" + "#=MeasureId#" + ")' href='\\#'>#=NonFlagged#</a>")
.ClientFooterTemplate("<a onclick='ShowPatientGapDetailsModal()' href='\\#'>#=sum#</a>");
It works fine for all the rows except the footer, which shows the full html anchor tag as you can see here:
Has anyone seen this before or have any suggestions?
it is because grid exports only data. Not templates. For usage templates into excel export you have to use ExcelExport event.
Here is small code snippet which I hope helps you.
Anyway I didn't work with footers and excel yet but I am sure there will be same rules like with normal data. Probably it will not work as you need but can kick you right direction.
I have met this behaviour when I had checkboxes in cells or datefields etc.
excelExport: function (e) {
var sheet = e.workbook.sheets[0];
var data = [];
for (var i = 1; i < sheet.rows.length; i++) {
var dataItem = {
FieldWithMyTemplate: e.data[i].FieldWithMyTemplate, // In e.data are data from row in grid
};
var row = sheet.rows[i];
for (var j = 0; j < row.cells.length; j++) {
var template = kendo.template(this.columns[j].template);
row.cells[j].value = template(dataItem);
}
}
};
Edit: Forgot to mention that in e.Data at first index ([0]) are data from headers. So on the last one will be your footer data.

Character encoding issue when using Google Apps Script to extract data from web page

I have written a script using Google Apps Script to extract text from a web page into Google Sheets. I only need this script to work with a specific web page, so it does not need to be versatile. The script works almost exactly as I want it to except that I have run into a character encoding problem. I am extracting both Hebrew and English text. The meta tag in the HTML has charset=Windows-1255. The English extracts perfectly, but the Hebrew displays as black diamonds containing a question mark.
I found this question that says to pass the data into a blob then use the getDataAsString method to convert to another encoding. I tried converting to different encodings and got different results. UTF-8 displays the black diamonds with question marks, UTF-16 displays Korean, ISO 8859-8 returns an error and says it's not a valid parameter, and the original Windows-1255 displays one Hebrew character but a bunch of other gibberish.
However, I am able to copy and paste the Hebrew text into Google Sheets manually and it displays correctly.
I have even tested passing Hebrew directly from Google Apps Script code like so:
function passHebrew() {
return "וַיְדַבֵּר";
}
This displays the Hebrew text properly on Google Sheets.
My code is as follows:
function parseText(book, chapter) {
//var bk = book;
//var ch = chapter;
var bk = '04'; //hard-coded for testing purposes
var ch = '01'; //hard-coded for testing purposes
var url = 'http://www.mechon-mamre.org/p/pt/pt' + bk + ch + '.htm';
var xml = UrlFetchApp.fetch(url).getContentText();
//I had to "fix" these xml errors for XmlService.parse(xml) below
//to function.
xml = xml.replace('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">', '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "">');
xml = xml.replace('<LINK REL="stylesheet" HREF="p.css" TYPE="text/css">', '<LINK REL="stylesheet" HREF="p.css" TYPE="text/css"></LINK>');
xml = xml.replace('<meta http-equiv="Content-Type" content="text/html; charset=Windows-1255">', '<meta http-equiv="Content-Type" content="text/html; charset=Windows-1255"></meta>');
xml = xml.replace(/ALIGN=CENTER/gi, 'ALIGN="CENTER"');
xml = xml.replace(/<BR>/gi, '<BR></BR>');
xml = xml.replace(/class=h/gi, 'class="h"');
//This section is the specific route to the table in the page I want
var document = XmlService.parse(xml);
var body = document.getRootElement().getChildren("BODY");
var maintable = body[0].getChildren("TABLE");
var maintablechildren = maintable[0].getChildren();
//This creates a two-dimensional array so that I can store the Hebrew
//in the first column and the English in the second column
var array = new Array(maintablechildren.length);
for (var i = 0; i < maintablechildren.length; i++) {
array[i] = new Array(2);
}
//This is where the table gets parsed into the array
for (var i = 0; i < maintablechildren.length; i++) {
var verse = maintablechildren[i].getChildren();
//This is where the encoding problem occurs.
//I originally tried verse[0].getText() but it didn't work.
array[i][0] = Utilities.newBlob(verse[0].getText()).getDataAsString('UTF-8');
//This array receives the English text and works fine.
array[i][1] = verse[1].getText();
}
return array;
}
What am I overlooking, misunderstanding, or doing wrong? I don't have a very good understanding of how encoding works so I don't understand why converting it to UTF-8 isn't working.
Your problem occurs before the lines you've commented as an encoding problem: because the default encoding for UrlFetchApp is munging the unicode text from the start.
You should use the variation of the .getContentText() method that Returns the content of an HTTP response encoded as a string of the given charset. For your case:
var xml = UrlFetchApp.fetch(url).getContentText("Windows-1255");
That should be all you need to change, although the blob() work-around is no longer needed. (It's harmless, though.) Other comments:
The logical OR operator (||) is very helpful for setting default values. I've tweaked the first few lines to enable testing but still let the function operate normally with arguments.
The way you're setting up an empty array before populating it with strings is Bad JavaScript; it's complex code that isn't needed, so toss it. Instead, we'll declare the array Array, then push() rows onto it.
The .replace() functions can be reduced with more clever RegExp use; I've included the URLs for demos of the really tricky ones.
There were \n newline characters in the text which I guessed were unnecessary for your purposes, so added a replace() for them as well.
Here's what you're left with:
function parseText(book, chapter) {
var bk = book || '04'; //hard-coded for testing purposes
var ch = chapter || '01'; //hard-coded for testing purposes
var url = 'http://www.mechon-mamre.org/p/pt/pt' + bk + ch + '.htm';
var xml = UrlFetchApp.fetch(url).getContentText("Windows-1255");
//I had to "fix" these xml errors for XmlService.parse(xml) below
//to function.
xml = xml.replace(/(<!DOCTYPE.*EN")>/gi, '$1 "">')
.replace(/(<(LINK|meta).*>)/gi,'$1</$2>') // https://regex101.com/r/nH3pU8/1
.replace(/(<.*?=)([^"']*?)([ >])/gi,'$1"$2"$3') // https://regex101.com/r/eP7wO7/1
.replace(/<BR>/gi, '<BR/>')
.replace(/\n/g, '')
//This section is the specific route to the table in the page I want
var document = XmlService.parse(xml);
var body = document.getRootElement().getChildren("BODY");
var maintable = body[0].getChildren("TABLE");
var maintablechildren = maintable[0].getChildren();
//This is where the table gets parsed into the array
var array = [];
for (var i = 0; i < maintablechildren.length; i++) {
var verse = maintablechildren[i].getChildren();
//I originally tried verse[0].getText() but it didn't work.** It does now!
var hebrew = verse[0].getText();
//This array receives the English text and works fine.
var english = verse[1].getText();
array.push([hebrew,english]);
}
return array;
}
Results
[
[
"  וַיְדַבֵּר יְהוָה אֶל-מֹשֶׁה בְּמִדְבַּר סִינַי, בְּאֹהֶל מוֹעֵד:  בְּאֶחָד לַחֹדֶשׁ הַשֵּׁנִי בַּשָּׁנָה הַשֵּׁנִית, לְצֵאתָם מֵאֶרֶץ מִצְרַיִם--לֵאמֹר.",
" And the LORD spoke unto Moses in the wilderness of Sinai, in the tent of meeting, on the first day of the second month, in the second year after they were come out of the land of Egypt, saying:"
],
[
"  שְׂאוּ, אֶת-רֹאשׁ כָּל-עֲדַת בְּנֵי-יִשְׂרָאֵל, לְמִשְׁפְּחֹתָם, לְבֵית אֲבֹתָם--בְּמִסְפַּר שֵׁמוֹת, כָּל-זָכָר לְגֻלְגְּלֹתָם.",
" 'Take ye the sum of all the congregation of the children of Israel, by their families, by their fathers' houses, according to the number of names, every male, by their polls;"
],
[
"  מִבֶּן עֶשְׂרִים שָׁנָה וָמַעְלָה, כָּל-יֹצֵא צָבָא בְּיִשְׂרָאֵל--תִּפְקְדוּ אֹתָם לְצִבְאֹתָם, אַתָּה וְאַהֲרֹן.",
" from twenty years old and upward, all that are able to go forth to war in Israel: ye shall number them by their hosts, even thou and Aaron."
],
...

How do I find the page number/number of pages in a document?

I want to create a new document based on a template and need to know when my insertion or append results in a new page in the final printed output is there any property/attribute eg number of pages that can be used for this?
I've search this a lot in the past and I don't think there's any property or any other way to know page info.
The solution I use is to insert page breaks on my template or via the script, using my own knowledge of how my template works, i.e. how much space it takes as I iterate, etc.
And then I know which page I am by counting the page breaks.
Anyway, you could an enhancement request on the issue tracker.
One way to get total number of pages:
function countPages() {
var blob = DocumentApp.getActiveDocument().getAs("application/pdf");
var data = blob.getDataAsString();
var re = /Pages\/Count (\d+)/g;
var match;
var pages = 0;
while(match = re.exec(data)) {
Logger.log("MATCH = " + match[1]);
var value = parseInt(match[1]);
if (value > pages) {
pages = value;
}
}
Logger.log("pages = " + pages);
return pages;
}

Losing leading 0s when string converts to array

I have a textInput control that sends .txt value to an array collection. The array collection is a collection of US zip codes so I use a regular expression to ensure I only get digits from the textInput.
private function addSingle(stringLoader:ArrayCollection):ArrayCollection {
arrayString += (txtSingle.text) + '';
var re:RegExp = /\D/;
var newArray:Array = arrayString.split(re);
The US zip codes start at 00501. Following the debugger, after the zip is submitted, the variable 'arrayString' is 00501. But once 'newArray' is assigned a vaule, it removes the first two 0s and leaves me with 501. Is this my regular expression doing something I'm not expecting? Could it be the array changing the value? I wrote a regexp test in javascript.
<script type="text/javascript">
var str="00501";
var patt1=/\D/;
document.write(str.match(patt1));
</script>
and i get null, which leads me to believe the regexp Im using is fine. In the help docs on the split method, I dont see any reference to leading 0s being a problem.
**I have removed the regular expression from my code completely and the same problem is still happening. Which means it is not the regular expression where the problem is coming from.
Running this simplified case:
var arrayString:String = '00501';
var re:RegExp = /\D/;
var newArray:Array = arrayString.split(re);
trace(newArray);
Yields '00501' as expected. There's nothing in the code you've posted that would strip leading zeros. You may want to dig around a bit more.
This smells suspiciously like Number coercion: Number('00501') yields 501. Read through the docs for implicit conversions and check if any pop up in your code.
What about this ?
/^\d+$/
You can also specify exactly 5 numbers like this :
/^\d{5}$/
I recommend just getting the zip codes instead of splitting on non-digits (especially if 'arrayString' might have multiple zip codes):
var newArray:Array = [];
var pattern:RegExp = /(\d+)/g;
var zipObject:Object;
while ((zipObject = pattern.exec(arrayString)) != null)
{
newArray.push(zipObject[1]);
}
for (var i:int = 0; i < newArray.length; i++)
{
trace("zip code " + i + " is: " + newArray[i]);
}