Google script error: "Error encountered: An unexpected error occurred" - google-apps-script

I am getting an intermittent error "Error encountered: An unexpected error occurred" while running the Google script below. It happens after the first clickHandler "Next" is used. I cannot determine why it happens and what the pattern is. After I click "OK" it continues to the second part of the code and finishes fine. The script is usable but the error message is annoying. Could someone please help?
function updateRecord() {
var app = UiApp.createApplication().setTitle('Sheet 1').setHeight(1000).setWidth(300);
var panel = app.createVerticalPanel().setWidth(300).setHeight(900);
var lb = app.createListBox(true).setHeight(800).setWidth(300).setId('myId').setName('myLbName');
//add items to ListBox
lb.setVisibleItemCount(3);
var ss = SpreadsheetApp.openById('xxx');
var list = ss.getSheetByName('Sheet 2');
var stnum = list.getRange(2,11,1,1).getValues()
var values = list.getRange(2,1,stnum,1).getValues().sort();
for (var i=0; i<values.length; i++){
lb.addItem(values[i][0].toString());
}
panel.add(lb);
var button = app.createButton('Next').setStyleAttribute('font-weight','bold');
var handler = app.createServerClickHandler('next').addCallbackElement(panel);
button.addClickHandler(handler);
panel.add(button);
app.add(panel);
SpreadsheetApp.getUi().showSidebar(app);
return app;
}
function next(eventInfo) {
var ss = SpreadsheetApp.openById('xxx');
var sheet = ss.getSheetByName('Sheet 3');
//Create menu items
var mnum = sheet.getRange(2,9,1,1).getValues();
var matvalues = sheet.getRange(2,1,mnum,1).getValues().sort();
var pnum = sheet.getRange(2,10,1,1).getValues();
var platvalues = sheet.getRange(2,2,pnum,1).getValues().sort();
//Create panels, scroll panels, button, and grid
var app = UiApp.createApplication().setTitle('Enter Additional Information').setHeight(600).setWidth(500).setStyleAttribute("background-color","light blue").setStyleAttribute('padding','20');
var scrollPanlel = app.createScrollPanel().setSize('300', '1000');
var panel = app.createVerticalPanel();
var buttonPanel = app.createHorizontalPanel();
var grid = app.createGrid(6, 1);
var button = app.createButton('Submit').setStyleAttribute('font-weight','bold');//.setStyleAttribute('color','red');
var submitHandler = app.createServerClickHandler('update');
submitHandler.addCallbackElement(grid);
button.addClickHandler(submitHandler);
buttonPanel.add(button);
//Add list boxes to the grid
grid.setWidget(1, 0, app.createLabel('Value 1:').setStyleAttribute('font-weight','bold').setStyleAttribute('color','purple'));
var matrix = app.createListBox(true).setHeight(100).setWidth(200).setId('myId1').setName('myLbName1');
grid.setWidget(2, 0, matrix);
for (var i in matvalues){
matrix.addItem(matvalues[i][0].toString());
}
grid.setWidget(3, 0, app.createLabel('Value 2:').setStyleAttribute('font-weight','bold').setStyleAttribute('color','purple'));
var platform = app.createListBox(true).setHeight(100).setWidth(200).setId('myId4').setName('myLbName4');
grid.setWidget(4, 0, platform);
for (var i in platvalues){
platform.addItem(platvalues[i][0].toString());
}
//Add all elements to the dialog
app.add(scrollPanlel);
scrollPanlel.add(panel);
panel.add(grid);
panel.add(buttonPanel);
//Show the dialog window
SpreadsheetApp.getUi().showSidebar(app);
return app;
}

This line:
var stnum = list.getRange(2,11,1,1).getValues()
Should be:
var stnum = list.getRange(2,11,1,1).getValues()[0][0];
The getRange() method returns a two dimensional array. But that line only gets the value of one cell. It's only getting one row, and one column. So, the indexes of the one value in the two dimensional array are both zero.
I'm not saying that is your main problem, but I'd fix that.
You can convert to HTML Service by using the following code: It's not a complete refactoring of your code, but it would give you a start.
gs Code
function showSidebar() {
var html = HtmlService.createTemplateFromFile('Page')
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setTitle('My custom sidebar')
.setWidth(300);
SpreadsheetApp.getUi()
.showSidebar(html);
}
Get items for the List Box
function getListBoxItems() {
var ss = SpreadsheetApp.openById('Your ID');
var list = ss.getSheetByName('Sheet 2');
var allSheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var stnum = list.getRange(2,11,1,1).getValues()[0][0];
var values = list.getRange(2,1,stnum,1).getValues().sort();
var htmlSelectOptions = "";
var thisListItem = "";
for (var i=0; i<values.length; i++){
thisListItem = values[i].toString();
htmlSelectOptions += '<option value="' + thisListItem + '">' + thisListItem + '</option>';
}
return htmlSelectOptions;
};
HTML File: Named Page
<div>
<br>Text Here:
<br>
<select id="myId" name="myLbName">
<?!= getListBoxItems(); ?>
</select>
<br>
<br>
<input type="button" value="Next" id="idCopyBtn" onclick="google.script.run.next()">
</div>

Related

forEach function in my apps script says undefined: trying to autocomplete form dropdown

I am trying to autocomplete a dropdown so it generates the list when triggered, but I get the mistake in the forEach section. The part where i left the text was to work out other issues. The forEach section says it is undifined but I have made sure to complete all brackets and semicolons... maybe i missed something.
var ssID = "1sQGIXhtH_EqWCmMckUMDP1JjlBdRRJ--UJePtJ7T99c";
var formID = "1hv53EFDAkFJ5qEA23UA68c76uHdyXHkanBmIjCpRWL4";
var wsData = SpreadsheetApp.openById(ssID).getSheetByName("MAESTROS");
var form = FormApp.openById(formID);
function main(){
var labels = wsData.getRange(1, 1, 1, wsData.getLastColumn()).getValues[0];
labels.forEach(function(label,i){
Logger.log(label);
var options = wsData
.getRange(2,i+1,wsData.getLastRow()-1,1)
.getValues()
.map(function(o){ return o[0]})
.filter(function(o){ return o !==""});
Logger.log(options)
});
}
function updateDropdownUsingTitle(title,values) {
//var item = form.getItemById (1449588801);
//Logger.log(item.getTitle());
var items = form.getItems();
//Logger.log(items[0].getId().toString());
var titles = items.map(function(item){
return item.getTitle();
});
var pos =titles.indexOf(title);
if(pos !== -1){
var item = items[pos];
var itemID=item.getId();
updateDropdown(itemID,values);
}
}
var labels = wsData.getRange(1, 1, 1, wsData.getLastColumn()).getValues[0];
It should be:
var labels = wsData.getRange(1, 1, 1, wsData.getLastColumn()).getValues()[0];
getValues() // return [[]], it has to have ().

Error when passing a blob file to send it as e-mail attachment

I'm trying to sending a file as e-mail attachment with Google Apps Script, following this rich answer. But instead of a stand alone app, I'm trying to do so within my spreadsheet, using a function like this:
function sendAttachment(){
var activeSheet = ss.getActiveSheet();
ScriptProperties.setProperty('emailRequest', 1);
if(!person_ID) {
person_ID = getCurrentRow();
//if the current line is the Column Headers line then ask the user to specify the ID, very rare case.
if (person_ID == 1) {
var person_ID = Browser.inputBox("Select one name.", "Click in one row:", Browser.Buttons.OK_CANCEL);
}
}
var app = UiApp.createApplication().setHeight(400).setWidth(600);
var panel = app.createVerticalPanel(); // you can embed that in a form panel
var label = app.createLabel("Choose the receiver").setStyleAttribute("fontSize", 18);
app.add(label);
var currentRow = ss.getActiveSelection().getRowIndex();
var personName = activeSheet.getRange(currentRow, 1).getValue();
var personNumber = activeSheet.getRange(currentRow, 5).getValue();
var item1Panel = app.createHorizontalPanel();
var txt = app.createTextBox().setId("item1").setName("item1").setValue(personName);
item1Panel.add(app.createLabel("Person:")).add(txt);
var item2Panel = app.createHorizontalPanel();
var txt = app.createTextBox().setId("item2").setName("item2").setValue(personNumber);
item2Panel.add(app.createLabel("Num:")).add(txt);
var sheet = SpreadsheetApp.openById(letterSpreadsheetId).getSheetByName("emailsDB");
var recipientEmailArray = sheet.getRange(2, 1, sheet.getLastRow(), sheet.getLastColumn()).getValues();
var item3Panel = app.createHorizontalPanel();
item3Panel.add(app.createLabel("Receiver"));
var listBox = app.createListBox().setName('item3');
for(var i = 0; i < (recipientEmailArray.length); i++){
listBox.addItem(recipientEmailArray[i][0] + ": " + recipientEmailArray[i][2]);
}
item3Panel.add(listBox);
var handlerBut = app.createServerHandler("butSendAttachment").addCallbackElement(panel);
var but = app.createButton("submit").setId("submitButton4").addClickHandler(handlerBut);
panel.add(item1Panel)
.add(item2Panel)
.add(item3Panel)
.add(app.createFileUpload().setName('thefile'))
.add(app.createLabel().setId("answer"))
.add(but);
var scroll = app.createScrollPanel().setPixelSize(600, 400).setTitle("My title 1");
scroll.add(panel);
app.add(scroll);
ss.show(app);
// var handlerBut = app.createServerHandler("butSendAttachment").addCallbackElement(panel);
// .add(app.createFileUpload().setName('thefile'));
// var form = app.createFormPanel();
// form.add(panel);
// app.add(form);
;
}
function butSendAttachment(e){
var recipientEmail = e.parameter.item3;
var fileBlob = e.parameter.thefile;
Logger.log("file blob = " + fileBlob);
recipientEmail = recipientEmail.split(':')[1];
var sheet = ss.getActiveSheet();
var person_ID = getCurrentRow();
var columns = getRowAsArray(sheet, 1);
var personData = getRowAsArray(sheet, person_ID);
var sender = actAuthor + " \n " + position;
var name = personData[0];
var motherName = personData[1];
var title = "my title";
var message = my mesage";
var confirm = Browser.msgBox('Send email','Are you sure?', Browser.Buttons.OK_CANCEL);
if(confirm=='ok'){
// MailApp.sendEmail(recipientEmail, title, message, {attachments: [fileBlob]});
MailApp.sendEmail(recipientEmail, title, message, {attachments: [fileBlob]});
var app = UiApp.createApplication().setHeight(150).setWidth(250);
var msg = "An email was sendo to " + recipientEmail;
app.setTitle("E-mail send!");
app.add(app.createVerticalPanel().add(app.createLabel(msg)));
var doc = SpreadsheetApp.getActive();
doc.show(app);
}
else {
return;
}
}
But I get this error: Execution failed: Invalid argument: inlineImages (line 77. Line 77 is this:
MailApp.sendEmail(recipientEmail, title, message, {attachments: [fileBlob]});
I've read the documentation I tried several argument variations. I conclude that fileBlob is Null. Why? How to fix it?
the fileUpload widget works only in a form parent widget and using a doGet/doPost structure.
That's written somewhere in the doc but right now I don't remember exactly where (I'look for it later)
But actually I guess you can build such a structure from within your spreadsheet, just change the names of your functions and use a formPanel as main widget. Don't forget also that you don't need a handler anymore and must use a submitButton widget.
EDIT : how silly I am ! its written in the first line in the widget's doc !!! ;-)

GAS e.parameter undefined, uploadWidget won't setName

I'm working on an answer for another question found here. I have modified a different tutorial script from here, but I'm having an issue that the e.parameter for the fileUploadWidget will not accept a .setName() change. No matter what I do with setName, it continues to show the "name" as FileUpload, and won't pass the e.parameter.'file'+numRows on to the uploadFiles(e) (formerly doPost(e)). The folderName parameter will come through, but numRows is coming through as NaN, and the file+numRows is coming through undefined. What is going on here/What am I missing? I've been through tons of solutions on and off SO, but can't seem to figure out where this has gone wrong. Maybe a fresh set of eyes with more experience can see what I'm doing wrong.
You can find the example of this code in action here
//modified from script found here http://www.googleappsscript.org/miscellaneous/creating-form-elements-dynamically-using-google-apps-script-gas
function doGet() {
var app = UiApp.createApplication();
var panel = app.createVerticalPanel();
var formPanel = app.createFormPanel();
var folderLabel = app.createLabel('Folder Name (temp placeholder to remember to use .getFolderById(folderId) to place in specific folder)');
var folderNameTextBox = app.createTextBox().setId('folderName').setName('folderName');
var filesLabel = app.createLabel('Add Files to Upload');
var table = app.createFlexTable().setId('table').setTag('0'); //Here tag will count the number of members
//Write the header for the table
var headerArray = ['File(s)'];
for(var i=0; i<headerArray.length; i++){
table.setWidget(0, i, app.createLabel(headerArray[i]));
}
//Add the first row of form elelments to input Member information
addMemebrRow(app);
//Add a button to submit the info
var button = app.createSubmitButton('Upload File(s)');
var handler = app.createServerHandler('uploadFiles');
handler.addCallbackElement(panel);
button.addClickHandler(handler);
panel.add(folderLabel)
.add(folderNameTextBox)
.add(filesLabel)
.add(table)
.add(button);
formPanel.add(panel);
app.add(formPanel);
return app;
}
function addMemebrRow(app){
var table = app.getElementById('table');
var tag = parseInt(table.getTag());
Logger.log(tag);
var numRows = tag+1;
if(numRows >1){
table.removeCell(numRows-1, 5);
table.removeCell(numRows-1, 4);
}
Logger.log(numRows);
var uploadWidget = app.createFileUpload();
var uploadWidgetName = uploadWidget.setName('file'+numRows);
var uploadWidgetId = uploadWidget.setId('file'+numRows);
Logger.log(uploadWidgetId.getId());
Logger.log(uploadWidgetName);
table.setWidget(numRows, 0, uploadWidget);
table.setTag(numRows.toString());
addButtons(app);
}
function addButtons(app){
var table = app.getElementById('table');
var numRows = parseInt(table.getTag());
//Create handler to add/remove row
var addRemoveRowHandler = app.createServerHandler('_addRemoveRow');
addRemoveRowHandler.addCallbackElement(table);
//Add row button and handler
var addRowBtn = app.createButton('+').setId('addOne').setTitle('Add row');
table.setWidget(numRows, 4, addRowBtn);
addRowBtn.addMouseUpHandler(addRemoveRowHandler);
//remove row button and handler
var removeRowBtn = app.createButton('-').setId('removeOne').setTitle('Remove row');
table.setWidget(numRows, 5, removeRowBtn);
removeRowBtn.addMouseUpHandler(addRemoveRowHandler);
}
function _addRemoveRow(e){
Logger.log(e.parameter.source);
var app = UiApp.getActiveApplication();
var table = app.getElementById('table');
var tag = parseInt(e.parameter.table_tag);
var source = e.parameter.source;
//Logger.log(tag);
if(source == 'addOne'){
table.setTag(tag.toString());
addMemebrRow(app);
}
else if(source == 'removeOne'){
if(tag > 1){
//Dcrement the tag by one
var numRows = tag-1;
table.removeRow(tag);
//Set the new tag of the table
table.setTag(numRows.toString());
//Add buttons in previous row
addButtons(app);
}
}
return app;
}
function uploadFiles(e) {
var foldername = e.parameter.folderName;
Logger.log(foldername);
var numFiles = parseInt(e.parameter.table_tag);
Logger.log(numFiles);
for (var i = 1; i<=numFiles; i++){
Logger.log(i);
var fileBlob = e.parameter['file'+i];
var newFile = DocsList.getFolderById("0B2p9JhtmHqC8Q0lIQk1mMERQTW8").createFile(fileBlob);
}
var app = UiApp.getActiveApplication();
var label = app.createLabel(numFiles +' file(s) uploaded successfully');
app.add(label);
return app;
}
File upload in forms needs a doPost function to work, this is not an option ;)
In such a structure (doGet/doPost) you don't have to define a handler nor a callBackElement, the formPanel is supposed to include all its elements automatically.
So I tried your modified code and still get one major issue with the numFiles value that is on the table tag : I can't get it...
If I replace it with a fixed value then everything works nicely, I get the files in the right folder.
So this answer is not a good answer because it doesn't bring a full solution but at least it reduces its initial scope to that point : how to get this ##!#! numFiles value ?
EDIT : Found the issue : table doesn't support setName method so its value can't be retrieved in the submitHandler. We should use another widget to hold that value.
new working Code below : (I used a textBox as a "hidden" widget for test only, please replace by a hiddenWidget when in production)
function doGet() {
var app = UiApp.createApplication();
var panel = app.createVerticalPanel();
var formPanel = app.createFormPanel();
var folderLabel = app.createLabel('Folder Name (temp placeholder to remember to use .getFolderById(folderId) to place in specific folder)');
var folderNameTextBox = app.createTextBox().setId('folderName').setName('folderName');
var filesLabel = app.createLabel('Add Files to Upload');
var table = app.createFlexTable().setId('table').setTag('0'); //Here tag will count the number of members
//Write the header for the table
var headerArray = ['File(s)'];
for(var i=0; i<headerArray.length; i++){
table.setWidget(0, i, app.createLabel(headerArray[i]));
}
//Add the first row of form elelments to input Member information
addMemebrRow(app);
var hidden = app.createTextBox().setName('hidden').setId('hidden').setValue(table.getTag());// used to hold the number of files, replace with createHidden()
//Add a button to submit the info
var button = app.createSubmitButton('Upload File(s)');
panel.add(folderLabel)
.add(folderNameTextBox)
.add(filesLabel)
.add(table)
.add(button);
formPanel.add(panel.add(hidden));
app.add(formPanel);
return app;
}
function addMemebrRow(app){
var table = app.getElementById('table');
var tag = Number(table.getTag());
Logger.log('tag='+tag);
var numRows = tag+1;
if(numRows >1){
table.removeCell(numRows-1, 5);
table.removeCell(numRows-1, 4);
}
Logger.log(numRows);
var uploadWidget = app.createFileUpload();
var uploadWidgetName = uploadWidget.setName('file'+numRows);
var uploadWidgetId = uploadWidget.setId('file'+numRows);
Logger.log(uploadWidgetId.getId());
Logger.log(uploadWidgetName);
table.setWidget(numRows, 0, uploadWidget);
table.setTag(numRows);
addButtons(app);
}
function addButtons(app){
var table = app.getElementById('table');
var numRows = Number(table.getTag());
//Create handler to add/remove row
var addRemoveRowHandler = app.createServerHandler('_addRemoveRow');
addRemoveRowHandler.addCallbackElement(table);
//Add row button and handler
var addRowBtn = app.createButton('+').setId('addOne').setTitle('Add row');
table.setWidget(numRows, 4, addRowBtn);
addRowBtn.addMouseUpHandler(addRemoveRowHandler);
//remove row button and handler
var removeRowBtn = app.createButton('-').setId('removeOne').setTitle('Remove row');
table.setWidget(numRows, 5, removeRowBtn);
removeRowBtn.addMouseUpHandler(addRemoveRowHandler);
}
function _addRemoveRow(e){
Logger.log(e.parameter.source);
var app = UiApp.getActiveApplication();
var hidden = app.getElementById('hidden');
var table = app.getElementById('table');
var tag = Number(e.parameter.table_tag);
var source = e.parameter.source;
//Logger.log(tag);
if(source == 'addOne'){
table.setTag(tag.toString());
hidden.setValue(tag+1);
addMemebrRow(app);
}
else if(source == 'removeOne'){
if(tag > 1){
//Dcrement the tag by one
var numRows = tag-1;
table.removeRow(tag);
//Set the new tag of the table
table.setTag(numRows);
hidden.setValue(numRows);
//Add buttons in previous row
addButtons(app);
}
}
return app;
}
function doPost(e) {
var foldername = e.parameter.folderName;
Logger.log('foldername = '+foldername);
var numFiles = Number(e.parameter.hidden);
Logger.log('numFiles = '+numFiles);
for (var i = 1; i<=numFiles; i++){
Logger.log(i);
var fileBlob = e.parameter['file'+i];
var newFile = DocsList.getFolderById("0B3qSFd3iikE3QXdubnVoMXlGMkk").createFile(fileBlob);
}
var app = UiApp.getActiveApplication();
var label = app.createLabel(numFiles +' file(s) uploaded successfully');
app.add(label);
return app;
}

Image is loaded after the process completion and remains after that

My code is below. It will accept data from the input text; then it will fetch the recent newest feed from stackoverflow(based in the input) in google spreadsheet.
I have(wanted) set an external gif image to be shown as a progress indicator while fetching of rss feed and its processing is going on.
Problem is: When i remove the commented part; that gif image will never be visible to the user and when that is remained as a comment; that gif image is being loaded by the app after the completion of the process and remained then after.
I want it to be visible only when the process(fetching of rss and processing of it) is going on. So is there any way to make it possible?
function stackoverflow(){
var app =UiApp.createApplication().setHeight(380).setWidth(800);
app.setTitle("Enter tag name");
var mydoc=SpreadsheetApp.getActiveSpreadsheet();
var txtBox = app.createTextBox().setFocus(true).setWidth("150").setId("ticker").setName("ticker");
var mainpanel = app.createHorizontalPanel().setId('mainpanel');
mainpanel.add(txtBox);
var subbtn = app.createButton("Get posts!").setId("tick").setWidth("80");
mainpanel.add(subbtn);
var myloadingimg=app.createImage("http://schedule.msu.edu/img/InProgress.gif").setHeight("26").setWidth("26").setId("loadingimg");
mainpanel.add(myloadingimg.setVisible(false));
app.add(mainpanel);
var panel = app.createVerticalPanel().setId('panel');
var submitHandler = app.createServerHandler("clicker").addCallbackElement(mainpanel);
subbtn.addClickHandler(submitHandler);
app.add(panel);
mydoc.show(app);
}
function clicker(e){
var app = UiApp.getActiveApplication();
var txtBox = app.getElementById("ticker");
app.getElementById("loadingimg").setVisible(true);
var panel=app.getElementById("panel");
app.remove(1);
var panel = app.createVerticalPanel().setId('panel');
var stackurl = "http://stackoverflow.com/feeds/tag?tagnames="+e.parameter.ticker+"&sort=newest";
var stackXML = UrlFetchApp.fetch(stackurl).getContentText();
var stackDOC = Xml.parse(stackXML, false);
var stackentry = stackDOC.getElement().getElements('entry');
for (var i = 0; i < stackentry.length; i++) {
var result = stackentry[i];
panel.add(app.createHTML("<br><b>Post Title: </b>"+ result.getElement('title').getText()+"<br>"));
panel.add(app.createAnchor('', '') .setText("Post URL").setHref(result.getElement('link').getAttribute('href').getValue()));
panel.add(app.createHTML("<br>"));
}
var scroll = app.createScrollPanel().setPixelSize(760, 280);
scroll.add(panel);
app.add(scroll);
//app.getElementById("loadingimg").setVisible(false);
return app;
}
function onOpen()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [];
menuEntries.push({name:'stackoverflow',functionName:'stackoverflow'});
ss.addMenu("f2", menuEntries);
}
You can make use of clientHandlers which are meant for precisely such cases.
function stackoverflow(){
...
var clientHandler = app.createClientHandler().forTargets(myloadingimg).setVisible(true);
var submitHandler = app.createServerHandler("clicker").addCallbackElement(mainpanel);
subbtn.addClickHandler(submitHandler).addClickHandler(clientHandler);
...
}
function clicker(e){
var app = UiApp.getActiveApplication();
....
app.getElementById("loadingimg").setVisible(false);
return app;
}

Replace formula of a cell with script

I am trying to replace some part of a formula in cell D3 of a spreadsheet, but I can't seem to do it. The formula in D3 is very long, but I only need to replace what would be searchtext variable and replace it with replacetext variable. Any ideas? Here's my code.
function dashboards(){
var ss1 = SpreadsheetApp.getActiveSpreadsheet();
var origSheet1 = ss1.getSheetByName('Daily');
var searchtext = Browser.inputBox("Enter search text");
var replacetext = Browser.inputBox("Enter replace text");
var form = origSheet1.getRange("D3").getFormulaR1C1();
form.indexof(searchtext);
var updated = form.replace(searchtext, replacetext);
form.setFormula(updated);}
You're not far off. The problem is that form, the below, is a String, not a reference to your Range.
var form = origSheet1.getRange("D3").getFormulaR1C1();
You can see this by inserting
Logger.log(form + "; type: " + typeof form); //String
after that line and checking the log in the Script Editor.
You just need to change
form.setFormula(updated);
to
origSheet1.getRange("D3").setFormulaR1C1(updated);
to update the actual range.
Copy the code below and run it via Script Manager or a menu item.
It operates on whatever the selected range is, whether it's a single cell or extends over multiple rows & columns.
It pops up a toast message to tell you when the procedure has finished but it leaves the UiInstance open in case you want to do more replacing.
You can keep it open and perform multiple search/replace in formulas on multiple selections or the same search on different sheets.
function handySRF() { // script composed by ailish#ahrosters.com
var ss = SpreadsheetApp.getActive();
var app = UiApp.createApplication().setHeight(200).setWidth(270).setTitle('Search and Replace In Formulas');
var panel = app.createAbsolutePanel().setId('panel').setHeight(198).setWidth(268)
.setStyleAttribute('background', 'lightCyan');
var lblSearch = app.createLabel('Search for:').setId('lblSearch');
var txtSearch = app.createTextBox().setId('txtSearch').setName('txtSearch');
var lblReplace = app.createLabel('Replace with:').setId('lblReplace');
var txtReplace = app.createTextBox().setId('txtReplace').setName('txtReplace');
var handler = app.createServerHandler('btnStartSearch');
var btnStartSearch = app.createButton('Start Search').addClickHandler(handler)
.setStyleAttribute('background', 'lightGreen');
handler.addCallbackElement(panel);
var handler2 = app.createServerHandler('btnCloseWindow');
var btnCloseWindow = app.createButton('Close Window').addClickHandler(handler2)
.setStyleAttribute('background', 'lightYellow');
handler2.addCallbackElement(panel);
panel.add(lblSearch, 10, 6)
panel.add(txtSearch, 10, 33)
panel.add(lblReplace, 10, 75)
panel.add(txtReplace, 10, 100)
panel.add(btnStartSearch, 10, 151)
panel.add(btnCloseWindow, 130, 151)
app.add(panel);
ss.show(app);
};
function btnStartSearch(e) {
var ss = SpreadsheetApp.getActive();
var app = UiApp.getActiveApplication();
var search = e.parameter.txtSearch;
var replace = e.parameter.txtReplace;
var rows = ss.getActiveSelection();
var numRows = rows.getNumRows();
var formulas = rows.getFormulas();
var newFormulas = [];
for (var i = 0; i <= numRows - 1; i++) {
var oldData = formulas[i];
var newData = [];
for (var j=0; j<oldData.length; ++j) {
var item = oldData[j].replace(new RegExp(search, "g"), replace);
newData.push(item);
}
newFormulas.push(newData);
}
rows.setFormulas(newFormulas);
var str = 'Finished replacing ' + search + ' with ' + replace;
ss.toast(str, '', 2);
};
function btnCloseWindow(e) {
var ss = SpreadsheetApp.getActive();
var app = UiApp.getActiveApplication();
app.close();
return app;
};