I've renamed a variable in my code, so I'm going through and replacing all the instances that used the variable as well. This would be a straightforward find and replace, but the original variable is a word that also happens to be in a number of my comments. That is, something like this:
//Old: var input = getUserInput();
var userinput = getUserInput();
input = parseInt(input, 1);
doSomething(input); //Does something with the user's input
doSomethingElse(input); //Does something else with the user's input
I want to replace the instances of input outside of the comments, so that I get this:
//Old: var input = getUserInput();
var userinput = getUserInput();
userinput = parseInt(userinput, 1);
doSomething(userinput); //Does something with the user's input
doSomethingElse(userinput); //Does something else with the user's input
Is this possible?
Related
I hit a wall here with this script. I am trying to get the body of a PayPal email that tells me I have a new subscription. I need the email address of the new subscriber. So...
I get the thread
I get the body. It's full of CSS and code, I don't see any info on the use. On the web page in the source it's all code it seems.
When I output it to a spreadsheet Show modal dialog it's looks perfect. I see the email address I am trying to get.
Is there a way to get that text? Then I can get the email address and the rest is EASY for me :-).
I hope I'm explaining things right.
This is far as I get with trying to get the text from this.
Thanks for any help you can spare. Maybe this is way over my head in which case, I'll drop it. But I just need the email address from this!
function getEmailFromFolder() {
// Log the subject lines of the threads labeled with MyLabel
var label = GmailApp.getUserLabelByName("NewVWMember");
var threads = label.getThreads();
var message = threads[0].getMessages()[0]; // Get first message
var body = message.getBody();
var output = HtmlService.createHtmlOutput(body);
//var n = body.search("mailTo");
var ui = SpreadsheetApp.getUi();
ui.showModalDialog(output, 'I want this!');
}
Answer:
You can use a regular expression to extract all emails out of a string.
Regular Expression:
There are multiple diffent ways of doing this, an example would be the following:
/([a-zA-Z0-9._-]+#[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)/g
Rundown of this regular expression:
[a-zA-Z0-9._-]+: matches the username - any number of characters from [A-Z], [a-z], [0-9], . and _
# matches the literal character #
[a-zA-Z0-9._-]+: matches the domain - again like the username, any number of characters from [A-Z], [a-z], [0-9], . and _
\.: macthes the literal character .
[a-zA-Z0-9._-]+: matches the top-level domain, as beforethis can be any number of characters from [A-Z], [a-z], [0-9], . and _.
g: returns globally, so will not return after the first email has been found.
You can test out the regular expression on RegEx101 here.
Email Extraction:
With the regular expression, you can extract an array of email addresses from the message body with just a few extra lines of code:
function getEmailFromFolder() {
var regex = /([a-zA-Z0-9._-]+#[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)/g;
var label = GmailApp.getUserLabelByName("NewVWMember");
var threads = label.getThreads();
var message = threads[0].getMessages()[0];
var body = message.getBody();
var output = HtmlService.createHtmlOutput(body);
var emails = output.getContent().match(regex).toString();
var html = HtmlService.createHtmlOutput(emails);
var ui = SpreadsheetApp.getUi();
ui.showModalDialog(html, 'I want this!');
}
Here, output is a string of all the email addresses in the message. It needs to be converted to string to display in the modal dialog, but you can do with this as you see fit.
References:
Regular Expression - Wikipedia
RegEx 101
I'm not a big fan of google forms so I made the form for my user input in the html service. I found a way to push the data out of the form and into google sheets using all of my variables in the html file like this:
<textarea type="text" name="Special Instructions" id="instructions"></textarea>
...
var instructions = document.getElementById("instructions").value;
...
google.script.run
.formsubmit (instructions,...)
google.script.host.close()}
in combination with the following in the code file:
function formsubmit(instructions,...)
var ss = SpreadsheetApp.getActive().getSheetByName("Sheet1");
ss.getRange(ss.getLastRow(),7,1,1).setValue(instructions);
...
The problem is, not only is the code very slow to output results, but if I have more than 37 or so variables defined, it glitches out and rather than closing the dialog box and recording the values in a spreadsheet, it opens a blank web page.
I know there has to be better (and more efficient) way, but I'm afraid I don't know it.
On the "client side", put all of your variables into a JSON object or an array, the stringify it, and send that string to the server.
var objectOfData;
variableOne = "one";
variable2 = "two";
objectOfData = {};
objectOfData['varOne'] = variableOne;//Create a new element in the object
objectOfData['var2'] = variable2;//key name is in the brackets
objectOfData = JSON.stringify(objectOfData);//Convert object to string
google.script.run
.formsubmit(objectOfData);
And then convert the object as a string back to a real object:
function formsubmit(o) {
var arrayOfValues,k,myData,outerArray;
myData = JSON.parse(o);//Convert string back to object
var ss = SpreadsheetApp.getActive().getSheetByName("Sheet1");
arrayOfValues = [];
for (k in myData) {//Loop through every property in the object
thisValue = myData[k];//
Logger.log('thisValue: ' + thisValue);//VIEW the LOGS to see print out
arrayOfValues.push(thisValue);
}
outerArray = [];
outerArray.push(arrayOfValues);
ss.getRange(ss.getLastRow() + 1,7,1,arrayOfValues.length).setValue(outerArray);
...
Note that the last parameter of getRange('start row', start column, number of rows, number of columns) uses the length of the inner array named arrayOfValues. This insures that the parameter value will always be correct regardless of how the array is constructed.
I am trying to find text within a google doc and replace with a subscript notation - replace "a3" with a3 but with the 3 now formatted as a subscript.
based on the answer here
I wrote some code that is working but only replaces the 1st instance of any occurrence (some are repeated).
I wrote the following:
for (var k=0; k<subscriptsReplace.length; k++) {
subscript = ' a'+subscriptsReplace[k];
find = ' a'+subscriptsReplace[k]+' ';
Logger.log(find)
var element = body.findText(find);
if(element){ // if found a match
var start = element.getStartOffset();
var text = element.getElement().asText();
text.replaceText(find, subscript);
text.setTextAlignment(start+2, start+2, DocumentApp.TextAlignment.SUBSCRIPT);
Logger.log("found one");
} // else do nothing
}
note that subscriptsReplace is an array that contains all the numbers of the subscripts throughout the document.
I cannot figure out why it's not getting the repeats, by looking at the logs, I know that it's not running the conditional on the repeats - so it's not re-replacing the same subscript it already replaced.
can someone see what's going on?
THank you!
Ultimately the issue was that using replaceText() was replacing all the occurences of the text throughout the document and therefor, it wasn't available to find and replace the formatting after the 1st iteration.
Here's the code that replaced all occurences:
for (var k=0; k<subscriptsReplace.length; k++) {
find = 'a'+subscriptsReplace[k]+'_';
var element = body.findText(find);
if(element){ // if found a match
var start = element.getStartOffset();
var text = element.getElement().asText();
text.setTextAlignment(start+1, start+1, DocumentApp.TextAlignment.SUBSCRIPT);
text.deleteText(start+2, start+2);
} // else do nothing
}
you'll see that rather than replacing, I added a special character "_" as a marker to find and then used deleteText() to get rid of them 1 at a time as I reformatted into subscripts
You can replace everything in the entire body with this:
function testReplace() {
var docBody = DocumentApp.getActiveDocument().getBody();
docBody.replaceText(searchPattern, replacement);
};
Google Documentation - Replace Text
I want to be able to add an "Ignore List" with the results being saved on the users browser.
The Ignored List is saved as a JSON array and looks like this:
[{"username":"test_user","date_added":"19/08/13","description":"Don't like this person."},{"username":"test_user_2","date_added":"19/08/13","description":"Don't like this person."}]
And the function required to add the users look like this:
function add_to_ignore_list()
{
var ignored_users = localStorage.getItem("ignore_list"); // returns ignore list
var username = return_current_username(); // returns test_user3
var date = return_current_date(); // returns 19/08/13
var description = prompt("Why do you want to ignore this user?"); // returns desc
add_to_list = {
"username" : username,
"date_added" : date,
"description" : description
};
ignored_users.push(add_to_list);
localStorage["ignore_list"] = JSON.stringify(ignored_users);
$(".user_wrapper").css("background-color","#B40404");
}
For some reason it isn't working and I can't see why Please help.
ignored_users is stored as a string.
When you retrieve it from localStorage, you need to parse it before you use it.
change:
var ignored_users = localStorage.getItem("ignore_list");
to (assumes it had previously been stored):
var ignored_users = JSON.parse(localStorage.getItem("ignore_list"));
In a mail merge application I use the .replace() method to replace field identifiers by custom values and also in a reverse process to get the identifiers back.
The first way works every time since the replace first argument is a pretty normal string that I have chosen on purpose... but when I reverse the process it happens sometimes that the string contains incorrect regular expression characters.
This happens mainly on phone numbers in the form +32 2 345 345 or even with some accentuated characters.
Given I can't prevent this from happening and that I have little hope that my endusers won't use this phone number format I was wondering if someone could suggest a workaround to escape illegal characters when they come up ? note : it can be at any place in the string.
below is the code for both functions.
... (partial code)
var newField = ChampSpecial(curData,realIdx,fctSpe);// returns the value from the database
if(newField!=''){replacements.push(newField+'∏'+'#ch'+(n+1)+'#')};
//Logger.log('value in '+n+'='+realIdx+' >> '+Headers[realIdx]+' = '+ChampSpecial(curData,realIdx,fctSpe))
app.getElementById('textField'+(n+1)).setHTML(ChampSpecial(curData,realIdx,fctSpe));
if(e.parameter.source=='insertInText'){
body.replaceText('#ch'+(n+1)+'#',newField);
}
}
UserProperties.setProperty('replacements',replacements.join('|'));
cloakOn();
colorize('#ffff44');
return app;
}
function fieldsInDoc(e){
cloakOff();// remet d'abord les champs vides
var replacements = UserProperties.getProperty('replacements').split('|');
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
for(var n=0;n<replacements.length;++n){
var field = replacements[n].split('∏')[1];
var testVal = replacements[n].split('∏')[0];
body.replaceText(testVal,field);
}
colorize('#ffff44');
}
In the reverse process you are using the fieldvalues provided that can include regex special characters. you have to escape them before replacing:
body.replaceText(field.replace(/[[\]{}()*-+?.,\\^$|#\s]/, '\\$&'), '#ch'+(n+1)+'#');
This said, the "replace back the markers" a bad idea. What happens if two fields of the mail merge have the same value or the replacement text is already present in the document template...
One possible solution was to prevent the example fields in the doc from containing regex special characters so the replace had to occur in the forward process, not in the reverse (as suggested in the other answer).
Escaping these character in the fields values didn't work* so I ended up with a simple replacement by a hyphen (which make sense in most cases to replace a slash or a '+').
(*) the reverse process uses the value kept in memory so the escape sign was disturbing the replace in that function, preventing it to work properly.
the final working code goes simply like this :
//(in the first function)
var newField = ChampSpecial(curData,realIdx,fctSpe).replace(/([*+?^=!:${}()|\[\]\/\\])/g, "-");// replace every occurrence of *+?^... by '-' (global search)
About the comment stating that this approach is a bad idea I can only say that I'm afraid there is not really other ways to get that behavior and that the probability to get errors if finally quite low since the main usage of mail merge is to insert proper names, adresses, emails and phone numbers that are rarely in the template itself.
As for the field indicators they will never have the same name since they are numerically indexed (#chXX#).
EDIT : following Taras's comment I'll try another solution, will update later if it works as expected.
EDIT June 19 , Yesssss... found it.
I finally found a far better solution that doesn't use regular expression so I'm not forced to escape special characters ... the .find() method accepts any string.
The code is a bit more complex but the results is worth the pain :-))
here is the full code in 2 functions if ever someone looks for something similar.
function valuesInDoc(e){
var lock = LockService.getPrivateLock(); // just in case one clicks the second button before this one ends
var success = lock.tryLock(5000);
if (!success) {
Logger.log('tryLock failed to get the lock');
return
}
colorize('#ffffff');// this function removes the color tags on the field marlers
var app = UiApp.getActiveApplication();
var listVal = UserProperties.getProperty('listSel').split(',');
var replacements = [];
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var find = body.findText('#ch');
if(find == null){return app };
var curData = UserProperties.getProperty('selItem').split('|');
var Headers = [];
var OriHeaders = UserProperties.getProperty('Headers').split('|');
for(n=0;n<OriHeaders.length;++n){
Headers.push('#'+OriHeaders[n]+'#');
}
var fctSpe = 0 ;
for(var i in Headers){if(Headers[i].indexOf('SS')>-1){fctSpe = i}}
for(var n=0;n<listVal.length;++n){
var realIdx = Number(listVal[n]);
Logger.log(n);
var newField = ChampSpecial(curData,realIdx,fctSpe);
//Logger.log(newField);
app.getElementById('textField'+(n+1)).setHTML(ChampSpecial(curData,realIdx,fctSpe));
if(e.parameter.source=='insertInText'){
var found = body.findText('#ch'+(n+1)+'#');// look for every field markers in the whole doc
while(found!=null){
var elemTxt = found.getElement().asText();
var startOffset = found.getStartOffset();
var len = ('#ch'+(n+1)+'#').length;
elemTxt.deleteText(startOffset, found.getEndOffsetInclusive())
elemTxt.insertText(startOffset,newField);// remove the marker and write the sample value in place
Logger.log('n='+n+' newField = '+newField+' for '+'#ch'+(n+1)+'#'+' at position '+startOffset)
replacements.push(newField+'∏'+'#ch'+(n+1)+'#'+'∏'+startOffset);// memorize the change that just occured
found = body.findText('#ch'+(n+1)+'#',found); //loop until all markers are replaced
}
}
}
UserProperties.setProperty('replacements',replacements.join('|'));
cloakOn();
colorize('#ffff44');// colorize the markers if ever one is left but it shouldn't happen
lock.releaseLock();
return app;
}
function fieldsInDoc(e){
var lock = LockService.getPrivateLock();
var success = lock.tryLock(5000);
if (!success) {
Logger.log('tryLock failed to get the lock');
return
}
cloakOff();// remet d'abord les champs vides > shows the hidden fields (markers that had no sample velue in the first function
var replacements = UserProperties.getProperty('replacements').split('|');// recover replacement data as an array
Logger.log(replacements)
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
for(var n=replacements.length-1;n>=0;n--){ // for each replacement find the data in doc and write a field marker in place
var testVal = replacements[n].split('∏')[0]; // [0] is the sample value
if(body.findText(testVal)==null){break};// this is only to handle the case one click on the wrong button trying to place markers again when they are already there ;-)
var field = replacements[n].split('∏')[1];
var testValLength = testVal.length;
var found = body.findText(testVal);
var startOffset = found.getStartOffset();
Logger.log(testVal+' = '+field+' / start: '+startOffset+' / Length: '+ testValLength)
var elemTxt = found.getElement().asText();
elemTxt.deleteText(startOffset, startOffset+testValLength-1);// remove the text
// elemTxt.deleteText(startOffset, found.getEndOffsetInclusive() )
elemTxt.insertText(startOffset,field);// and write the marker
}
colorize('#ffff44'); // colorize the marker
lock.releaseLock();// and release the lock
}