How do I use appendText() or insertText() for a Google Doc script and maintain formatting?
I want to format the middle portion (group2) of appended strings with italics, while leaving the other parts (group1, group3) as normal text. For example: Hi my name is Nate.
I can bring in "Hi" and append "my name is" and it formats correctly. When I try to append (or insert) "Nate," "Nate" is italicized as well.Between operators +, appendText(), and insertText(), I'm not having much luck.
Below is the relevant portion of the script. Below that, is the entire thing.
How can I append 3 strings together, and only format the middle portion?
NOTE: I commented-out the things I tried (trial1, trial2, etc.). I also started HERE and used it as a guide.
Thanks for any help you can offer!
RELEVANT PART:
if (author1 != "") {
var group1 = author1+author2+author3;
var group2 = title2Italics+containerItalics;
var group3 = contribution1+contribution2+contribution3+version+number+publisher+pubDate+location;
//Only using the calculations below to determine the offset for insertText
var group1Length = group1.length;
var group2Length = group2.length;
var offset = group1Length+group2Length
Logger.log(group1Length);
Logger.log(group2Length);
Logger.log(offset);
//Determines if italicizing is necessary
if (group2.length > 0) {
var addG1 = body.insertParagraph(0,group1)
var addG2 = addG1.appendText(group2);
var formatItalics = addG2.editAsText().setItalic(true);
//var trial1 = addG2.editAsText().setItalic(true) + group3; //does not return the contents of "group3"
//var trial2 = formatItalics + group3; //does not return the contents of "group3"
//var trial3 = formatItalics.insertText(offset,group3); //Error: "Index (18) must be less than or equal to the content length (6)."
//var trial4 = formatItalics.insertText(group2Length, group3); //formats "group3" as well
//var trial5 = formatItalics.appendText(group3); //formats "group3" as well
}
//If italicizing is NOT necessary
else {
var cite = body.insertParagraph(0,group1 + group3);
} //ELSE STATEMENT ENDS HERE
} //FIRST IF STATEMENT ENDS HERE
ENTIRE SCRIPT:
function mlaBibTest() {
// Sheet Information
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.setActiveSheet(ss.getSheetByName('test'));
var startRow = 3;
var startCol = 21;
var numRows = sheet.getLastRow()-1;
var numCols = 14;
var dataRange = sheet.getRange(startRow, startCol, numRows, numCols);
// Document information
var doc = DocumentApp.openById('13MlHq_uoO1rUF0RfdF_kBlLJjbGt4aDoOcSWef0V4zM');
var body = doc.getBody();
// Fetch values for each row in the SS Range.
var cells = dataRange.getValues();
for (var i = 0; i < cells.length; ++i) {
var column = cells[i];
var colU = column[0];
var colV = column[1];
var colW = column[2];
var colX = column[3];
var colY = column[4];
var colZ = column[5];
var colAA = column[6];
var colAB = column[7];
var colAC = column[8];
var colAD = column[9];
var colAE = column[10];
var colAF = column[11];
var colAG = column[12];
var colAH = column[13];
var author1 = colU;
var author2 = colV;
var author3 = colW;
var title1Quotes = colX;
var title2Italics = colY;
var containerItalics = colZ;
var contribution1 = colAA;
var contribution2 = colAB;
var contribution3 = colAC;
var version = colAD;
var number = colAE;
var publisher = colAF;
var pubDate = colAG;
var location = colAH;
if (author1 != "") {
var group1 = author1+author2+author3;
var group2 = title2Italics+containerItalics;
var group3 = contribution1+contribution2+contribution3+version+number+publisher+pubDate+location;
//Only using the calculations below to determine the offset for insertText
var group1Length = group1.length;
var group2Length = group2.length;
var offset = group1Length+group2Length
Logger.log(group1Length);
Logger.log(group2Length);
Logger.log(offset);
//Determines if italicizing is necessary
if (group2.length > 0) {
var addG1 = body.insertParagraph(0,group1)
var addG2 = addG1.appendText(group2);
var formatItalics = addG2.editAsText().setItalic(true);
//var trial1 = addG2.editAsText().setItalic(true) + group3; //does not return the contents of "group3"
//var trial2 = formatItalics + group3; //does not return the contents of "group3"
//var trial3 = formatItalics.insertText(offset,group3); //Error: "Index (18) must be less than or equal to the content length (6)."
//var trial4 = formatItalics.insertText(group2Length, group3); //formats "group3" as well
//var trial5 = formatItalics.appendText(group3); //formats "group3" as well
}
//If italicizing is NOT necessary
else {
var cite = body.insertParagraph(0,group1 + group3);
} //ELSE STATEMENT ENDS HERE
} //FIRST IF STATEMENT ENDS HERE
} //FOR LOOP ENDS HERE
SpreadsheetApp.flush();
} // FUNCTION ENDS HERE
This is a simple example of doing what you asked. It's important to remember that setItalics(true) sets a persistent setting for all new text to be italic, so we have to set it back to false after.
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var paragraph = body.insertParagraph(0, ""); //add paragparh at top of body.
var text1 = paragraph.appendText("Not Italics ");
var text2 = paragraph.appendText("Italics ");
text2.setItalic(true); //Everything after and including this will be italics
var text3 = paragraph.appendText("Not Italics");
text3.setItalic(false); //Everything after and including this will not be italics
>Not Italics Italics Not Italics
So it's easier if you set italics as you build the paragraph.
Related
I'm trying to create a script that will create new documents from a template-document. Replace placeholders in the documents with data from the sheet based on a keyword search in a specific column. And then change the row's value in the specific column so that the row will not process when the script runs again.
I think I've got it right with the first keyword search, and the loop through the rows. But the last part to get the data to 'merge' to the placeholders I can't figure out how to. I just get the value "object Object" and other values in the document.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
var lastColumn = s.getLastColumn();
function createDocFromSheet() {
var headers = getUpsertHeaders(s);//function is defined outside of this function
var statusColNum = headers.indexOf('Status')+1;
var row = getRowsData(s); //The function is defined outside this function.
for (var i=0; i<row.length; i++) {
var jobStatus = '';
if (row[i]['Status'] === '') {
//New: write the status to the correct row and column - this will be moved to the end when I get the rest right
var jobStatus = "Created";
s.getRange(i+2, statusColNum).setValue(jobStatus);
//Find the template and make a copy. Activate the body of the new file.
var templateFile = DriveApp.getFileById('1lkfmqsJMjjPujHqDqKtcDmL-5GMIxpOWTyCOaK29d2A');
var copyFile = templateFile.makeCopy()
var copyId = copyFile.getId()
var copyDoc = DocumentApp.openById(copyId)
var copyBody = copyDoc.getActiveSection()
//Find the rows Values as an object.
var rows = s.getRange(i+2,1,1,lastColumn)
var rowsValues = rows.getValues();
Logger.log(rowsValues)
//Until here I think it's okay but the last part?
//HOW TO replace the text???
for (var columnIndex = 0; columnIndex < lastColumn; columnIndex++) {
var headerValue = headerRow[columnIndex]
var rowValues = s.getActiveRange(i,columnIndex).getValues()
var activeCell = rowsValues[columnIndex]
//var activeCell = formatCell(activeCell);
Logger.log(columnIndex);
copyBody.replaceText('<<' + headerValue + '>>', activeCell)
}
Template doc : Link
Template sheet: Link
You can use the following GAS code to accomplish your goals:
var DESTINATION_FOLDER_ID = 'YOUR_DESTINATION_FOLDER_ID';
var TEMPLATE_FILE_ID = 'YOUR_TEMPLATE_FILE_ID';
function fillTemplates() {
var sheet = SpreadsheetApp.getActiveSheet();
var templateFile = DriveApp.getFileById(TEMPLATE_FILE_ID);
var values = sheet.getDataRange().getDisplayValues();
var destinationFolder = DriveApp.getFolderById(DESTINATION_FOLDER_ID);
for (var i=1; i<values.length; i++) {
var rowElements = values[i].length;
var fileStatus = values[i][rowElements-1];
if (fileStatus == 'Created') continue;
var fileName = values[i][0];
var newFile = templateFile.makeCopy(fileName, destinationFolder);
var fileToEdit = DocumentApp.openById(newFile.getId());
for (var j=1; j<rowElements-1; j++) {
var header = values[0][j];
var docBody = fileToEdit.getBody();
var patternToFind = Utilities.formatString('<<%s>>', header);
docBody.replaceText(patternToFind, values[i][j]);
}
sheet.getRange(i+1, rowElements).setValue('Created');
}
}
You only have to replace the 1st and 2nd lines as appropriate. Please do consider as well that the code will assume that the first column is the file name, and the last one the status. You can insert as many columns as you wish in between.
After some coding I ended up with this code to process everything automatic.
Again thanks to #carlesgg97.
The only thing I simply can't figure out now is how to generate the emailbody from the template with dynamic placeholders like in the document. How to generate the var patternToFind - but for the emailbody?
I've tried a for(var.... like in the document but the output doesn't replace the placeholders.
var DESTINATION_FOLDER_ID = '1inwFQPmUu1ekGGSB5OnWLc_8Ac80igK0';
var TEMPLATE_FILE_ID = '1lkfmqsJMjjPujHqDqKtcDmL-5GMIxpOWTyCOaK29d2A';
function fillTemplates() {
//Sheet variables
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Data');
var values = sheet.getDataRange().getDisplayValues();
//Header variables
var headers = sheet.getDataRange().getValues().shift();
var idIndex = headers.indexOf('ID');
var nameIndex = headers.indexOf('Name');
var emailIndex = headers.indexOf('Email');
var subjectIndex = headers.indexOf('Subject');
var statusIndex = headers.indexOf('Status');
var fileNameIndex = headers.indexOf('File Name');
var filerIndex = headers.indexOf('Filer');
var birthIndex = headers.indexOf('Date of birth');
//Logger.log(statusIndex)
//Document Templates ID
var templateFile = DriveApp.getFileById(TEMPLATE_FILE_ID);
//Destination
var destinationFolder = DriveApp.getFolderById(DESTINATION_FOLDER_ID);
var templateTextHtml = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Email').getRange('D11').getValue();
//Run through the variables
for (var i=1; i<values.length; i++) {
//If first column is empty then stop
var index0 = values[i][0];
if(index0 == "") continue;
var rowElements = values[i].length;
var fileStatus = values[i][statusIndex];
//If the row already processed then stop
if (fileStatus == "Created") continue;
//If the row is not processed continue
//Define the new filename by the relevant Column
var fileName = values[i][fileNameIndex];
var newFile = templateFile.makeCopy(fileName, destinationFolder);
var fileToEdit = DocumentApp.openById(newFile.getId());
//Replace placeholders in the new document
for (var j=1; j<rowElements-1; j++) {
var header = values[0][j];
var docBody = fileToEdit.getBody();
var patternToFind = Utilities.formatString('{{%s}}', header);
docBody.replaceText(patternToFind, values[i][j]);
}
//Create the PDF file
fileToEdit.saveAndClose();
var newPdf = DriveApp.createFile(fileToEdit.getAs('application/pdf'));
DriveApp.getFolderById(DESTINATION_FOLDER_ID).addFile(newPdf);
DriveApp.getRootFolder().removeFile(newPdf);
newFile.setTrashed(true);
var newPdfUrl = newPdf.getUrl();
//Create the emailbody
var textBodyHtml = templateTextHtml.replace("{{Name}}",values[i][nameIndex]).replace("{{Date of birth}}",values[i][birthIndex]);
var textBodyPlain = textBodyHtml.replace(/\<br>/mg,"");
//Will send email to email Column
var email = values[i][emailIndex];
var emailSubject = values[i][idIndex]+" - "+values[i][fileNameIndex]+" - "+values[i][nameIndex];
MailApp.sendEmail(email,emailSubject,textBodyPlain,
{
htmlBody: textBodyHtml+
"<p>Automatic generated email</p>",
attachments: [newPdf],
});
sheet.getRange(i+1, filerIndex+1).setValue(newPdfUrl);
sheet.getRange(i+1, statusIndex+1).setValue('Created');
}//Close for (var i=1...
}
I can't seem to get the "g modifier" in a RegExp to work in a Google script.
When I try to apply it, sometimes I get the error that "ReferenceError: "g" is not defined.". When I remove the /g both regExp.exec and input.match(regExp) work, but only for the first match. Other times, I'll get the /g to work, but it returns null, not even producing the first match. I had attempted a while loop, but I didn't want to slow down this process even more (I'll save optimizing this script for another post once I get it to work as intended).
The short version is, I'm trying to get ALL matches (email addresses) not just the first one. Where do I apply the /g? Should I use another method?
You can see (below) what I've been attempting below.
Any tips? I appreciate any help me understand this and anyone that can show me a better way to approach this. Thanks!
if (colA != "" && colE != processed) {
var html = UrlFetchApp.fetch(colA).getContentText();
//Logger.log(html);
var regExp = new RegExp("[A-z0-9._%+-]+#[A-z0-9.-]+\.[A-z]{2,4}");
var regExp2 = new RegExp("[A-z0-9._%+-]+#[A-z0-9.-]+\.[A-z]{2,4}");
var regExp3 = '[A-z0-9._%+-]+#[A-z0-9.-]+\.[A-z]/g {2,4}';
var regExp4 = '[A-z0-9._%+-]+#[A-z0-9.-]+\.[A-z]{2,4}/g';
var extractTest = html.match(regExp3);
//var extract = regExp.exec(html);
Logger.log(extractTest);
}
You can see the "bigger picture" of it all below.
//TEST
var processed = "YES";
function test() {
var ss = SpreadsheetApp.openById('1E4yUVIpwi00DzjfnXrZSgNFmVjOQbNWewxAiTBHRdD4');
var sheet = ss.getSheetByName('Sheet5');
var currentRow = 2;
var currentColumn = 1;
var numRows = sheet.getLastRow()-1;
var numColumns = 5;
var range = sheet.getRange(currentRow, currentColumn, numRows, numColumns);
var values = range.getValues();
//Logger.log(values);
for (var i = 0; i < numRows; ++i) {
var column = values[i];
var colA = column[0];
var colB = column[1];
var colC = column[2];
var colD = column[3];
var colE = column[4];
if (colA != "" && colE != processed) {
var html = UrlFetchApp.fetch(colA).getContentText();
//Logger.log(html);
var regExp = new RegExp("[A-z0-9._%+-]+#[A-z0-9.-]+\.[A-z]{2,4}");
var regExp2 = new RegExp("[A-z0-9._%+-]+#[A-z0-9.-]+\.[A-z]{2,4}");
var regExp3 = '[A-z0-9._%+-]+#[A-z0-9.-]+\.[A-z]/g {2,4}';
var regExp4 = '[A-z0-9._%+-]+#[A-z0-9.-]+\.[A-z]{2,4}/g';
var extractTest = html.match(regExp3);
//var extract = regExp.exec(html);
Logger.log(extractTest);
}
}
//var destRange = sheet.getRange(currentRow+i,2);
//destRange.setValue(extract);
//var destRange2 = sheet.getRange(currentRow+i,5);
//destRange2.setValue(processed);
SpreadsheetApp.flush();
}
Using the solution provided by #I'-'I (see comments above): var re = /[A-z0-9._%+-]+#[A-z0-9.-]+\.[A-z]{2,4}/g;
I have written script for changing a format like 12.4/12/12.03 into 12:40:00/12:00:00/12:03:00
Here's the code:
function myFunction() {
var sheet=SpreadsheetApp.getActiveSheet();
var rows= sheet.getDataRange();
var numRows=rows.getNumRows();
var values=rows.getValues()
var column = [];
var p = 0;
var k = "H";
for (var i=0;i<numRows;i++) {
// var cell =
//Split the string a the .
var string = values[i][7].split(".");
string[0] = string[0].toString();
p = i+1;
k = "H"+p;
var cell = sheet.getRange(k);
if(string[1]){
string[1] = string[1].toString();
// if the second part is like 4 in 12.4 you set it to 40
if(string[1]!=0) {
if (string[1].length == 1 )
{ string[1] += "0";}
}
// Set the row value to the format you like, here : 12:40:00/12:40
var changed_format = string[0] + ":" + string[1] + ":00";
values[i][7]=changed_format;
p = i+1;
k = "H"+p;
cell.setValue(changed_format);
}
else {
var changed_format = values[i][7]+":00:00";
cell.setValue(changed_format);
}
}
In the above code, I have mentioned columns...i.e., I have to run this script for each column...every time... ex: values[i][7] k="H"+p for 8th column. So, can anyone plz tell me how to do...all at a time...and if possible reduce my code..(optimize)..and also..if is it possible to do like this : if I select the column in the spreadsheet and the changes done by the script applies to that selected region...I mean I want my script to take the selected region as input...is it possible to do..if how.?
One key to optimize your code is to reduce the number of calls to Google services and try getting them done using JavaScript. Here is an optimized version that you could use.
Note that I havent tested it - so if you come across minor syntax errors, feel free to fix them or give a shout if you cannot fix them.
function myFunction() {
var sheet=SpreadsheetApp.getActiveSheet();
var rows= sheet.getDataRange();
var values=rows.getValues();
var COL_H = 8;
var numRows=values.length ; // Length of array = nuymRows. rows.getNumRows();
var column = [];
//var p = 0;
//var k = "H";
var destArray = new Array();
for (var i=0;i<numRows;i++) {
// var cell =
//Split the string a the .
var string = values[i][7].split(".");
string[0] = string[0].toString();
//p = i+1;
//k = "H"+p;
//var cell = sheet.getRange(k);
if(string[1]){
string[1] = string[1].toString();
// if the second part is like 4 in 12.4 you set it to 40
if(string[1]!=0) {
if (string[1].length == 1 )
{ string[1] += "0";}
}
// Set the row value to the format you like, here : 12:40:00/12:40
var changed_format = string[0] + ":" + string[1] + ":00";
//values[i][7]=changed_format;
//p = i+1;
//k = "H"+p;
//cell.setValue(changed_format);
destArray.push([changed_format]);
}
else {
var changed_format = values[i][7]+":00:00";
//values [i][7] = changed_format;
//cell.setValue(changed_format);
destArray.push([changed_format]);
}
}
var destRange = sheet.getRange(1, COL_H, destArray.length, 1);
destRange.setValues(values);
}
TIP: Putting formatted code in the question helps readability
I want to use this script to retrieve multiple pieces of information from the spreadsheet. My goal is to have it look, but skip the cell it already retrieved its information from:
function doGet() {
var app = UiApp.createApplication().setTitle('When Am I Eligible?');
var panel = app.createVerticalPanel().setHeight("400px;").setWidth("800px");
//var submitButton = app.createButton('Check');
var key= '-----'
var ss = SpreadsheetApp.openById(key);
var sh = ss.getSheetByName('SHEET');
var last=ss.getLastRow();
var data=sh.getRange(1,1,last,5).getValues();
var valB= Session.getEffectiveUser();
var nn = 0;
for (cnt=0; cnt<8; ++cnt){
var nn = (nn+1)
for(var nn;nn<data.length;++nn){
if (data[nn][1]==valB){
var final = data[nn][0];
var final1 = data[nn][1];
var final2 = data[nn][2];
var final3 = data[nn][3];
var final4 = data[nn][4];
break} ;// if a match in column B is found, break the loop
}
var app = UiApp.getActiveApplication();
var answer = app.createLabel("BRO").setVisible(true).setText(final)
var answer1 = app.createLabel("BRO").setVisible(true).setText(final1)
var answer2 = app.createLabel("BRO").setVisible(true).setText(final2)
var answer3 = app.createLabel("BRO").setVisible(true).setText(final3)
var answer4 = app.createLabel("BRO").setVisible(true).setText(final4)
panel.add(answer).add(answer1).add(answer2).add(answer3).add(answer4);
app.add(panel);
if (cnt == 7) {
break}
}
return app;
}
Is there any way to do this, my counter is not working to save the place, i've tried ++nn 'making a new variable etc.
Not sure why you have the outer loop with the variable cnt . Also, not sure why you have (re)declared nn thrice.
I think you could do with a simple loop if all you have to match is column B. Something like
for(var nn = 1; nn < data.length ; n++){
if (data[nn][1] == valB){
var final = data[nn][0];
var final1 = data[nn][1];
var final2 = data[nn][2];
var final3 = data[nn][3];
var final4 = data[nn][4];
/* Update the UI */
var panel = app.createVerticalPanel().setHeight("400px;").setWidth("800px");
var answer = app.createLabel("BRO").setVisible(true).setText(final)
var answer1 = app.createLabel("BRO").setVisible(true).setText(final1)
var answer2 = app.createLabel("BRO").setVisible(true).setText(final2)
var answer3 = app.createLabel("BRO").setVisible(true).setText(final3)
var answer4 = app.createLabel("BRO").setVisible(true).setText(final4)
panel.add(answer).add(answer1).add(answer2).add(answer3).add(answer4);
app.add(panel);
}
}
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;
};