Beruflich Dokumente
Kultur Dokumente
62;
var NAME = 'FormEmailer'; //used as menu and settings sheet name
var T = {
sender: "Sender Name", replyTo: "Reply To", //please try to keep these first
texts short
to: "To", cc: "Cc", bcc: "Bcc",
subject: "Subject", body: "Body", html: "html",
fSheet: "Form sheet", fSheetDESC: "Name of the sheet that holds the form
answers",
qtt: "Qtt Emails", qttDESC: "Max quantity of emails sent per form submitted",
qWarn: "Quota warning", qWarnDESC: "Daily quota threshold that will trigger a
warning email",
qLimit: "Quota limit", qLimitDESC: "Quota minimum value where FormEmailer will
stop sending emails",
fLoc: "Formulas location", fLocDESC: "Enter the location using the format:
SheetName!A:B",
closure: "Closure mode", closureDESC: "What the script should do after your
formulas are evaluated",
closureValues: "values", closureFormulas: "formulas", closureClear: "clear",
quota: "Remaining quota", quotaDESC: "Amount of your daily quota you got left
now",
statusEmail: "Email{1} sent", //email index (will be blank if there"s only one)
statusNot: "Email{1} not sent", //email index
statusError: "Error sending email{1}: {2}", //email index; error message
mailError: "Error sending email{1} on line{3}: {2}", //email index; error
message; line number
statusQuota: "Quota limit reached",
statusMissingField: "Field does not exist: {1}", //placeholder name
statusFormattingErr: "Error formatting field: {1}", //full placeholder definition
statusDateFormatErr: "Error formatting date field: {1}", //full placeholder
definition
sheetError: "{1} informed does not exist!", //'Form sheet' parameter name
numberError: "{1} is not a valid number!", //parameter name
formulasError: "Invalid {1}", //'formulas location' parameter name
formulasSheetError: "Invalid sheet for {1}", //'formulas location' parameter name
formulasColsError: "Invalid column reference for {1}", //'formulas location'
parameter name
errorTitle: "Error",
badSettings: "Your data was not saved.",
versionConflict: "Config version is {1}, but script's is {2}. They are not
compatible.\n", //config version; script version
resolveConflict: "You settings version \"{1}\", is not compatible with the
script's version \"{2}\". Do you want to overwrite it new default settings?",
//config version; script version
badConfig: "Your config is damaged or missing!\nDo you want to overwrite it with
the default values?",
badConfigCancel: "Nothing done.\nHere is the error message:\n{1}", //error
message
noProblem: 'There seem to be no problem with your settings.',
missingSheet: "Unable to find sheet: {1}", //sheet name
fSheetMissing: "Your \"{1}: {2}\" cannot be found, please choose a new one and
click \"{3}\" to fix", //fSheet parameter name; fSheet parameter value; yes
cancelMessage: "Canceled",
errorReportSubject: "{1} errors report", //script name
errorReportBody: "The following errors occurred on \"{1}\"
spreadsheet:\n{2}", //spreadsheet name; errors
alsoQuota: "Also, your quota is below the warning limit",
quotaMailSubject: "{1} quota warning", //script name
quotaMailBody: "Your actual quota is: {1}\nIt is bellow the warning threshold:
{2}", //remaining quota; quota warning
//css
var _emailField = {
width: '480px',
fontSize: '12px'
};
var _body = {
height: '234px',
minHeight: '234px', //to avoid resizable textArea of some browsers
maxHeight: '234px',
width: '100%',
minWidth: '100%',
maxWidth: '100%' //this one does not work
};
var _placeHolders = {
width: '230px',
fontSize: '12px'
};
var _link = {
color: 'blue',
cursor: 'hand'
};
var _right = {
width: '100%',
textAlign: 'right'
};
var numParam = {
width: '70px',
textAlign: 'right'
};
var textParam = {
width: '150px'
};
//This is the monitoring function. You should set up a time-driven trigger to run
it.
function timeDriven(e) {
if( e !== undefined )
errorHandling_(monitor_,true);
else
monitor_(false);
}
function about() {
if( !loadOrDefaults_() )
return;
var app = UiApp.createApplication().setTitle(repl_(T.version, NAME,
VERSION)).setWidth(340).setHeight(100);
app.add(app.createScrollPanel().setSize('340px', '100px').add(
app.createVerticalPanel().setSpacing(3).setStyleAttribute('margin', '10px')
.add(
app.createLabel(repl_(T.about1, 'Henrique G. Abreu'))).add(
app.createLabel(T.about2)).add(
app.createAnchor('http://sites.google.com/site/formemailer',
'http://sites.google.com/site/formemailer'))));
SpreadsheetApp.getActiveSpreadsheet().show(app);
}
function onOpen() {
c.ss = c.ss || SpreadsheetApp.getActiveSpreadsheet();
c.cs = c.cs || c.ss.getSheetByName(NAME);
if( c.cs != null ) {
try {
loadConfig_();
c.ss.removeMenu(NAME+' ERROR');
c.ss.addMenu(NAME, [{name:T.menuSettings, functionName: 'settings'},
{name:T.menuManually, functionName: 'processManually'},
{name:T.menuAbout, functionName: 'about'}]);
} catch(e) {
c.ss.removeMenu(NAME);
c.ss.addMenu(NAME+' ERROR', [{name:T.menuFix, functionName: 'fix'}])
}
checkTrigger_(false);
} else //language pack will be available only after the install
c.ss.addMenu(NAME, [{name:'Install', functionName: 'install'}]);
}
function onInstall() {
onOpen();
}
function onEdit(e) {
c.ss = e.source;
c.cs = c.ss.getSheetByName(NAME);
if( c.cs != null )
checkTrigger_(false);
}
function monitor_(trigger) {
loadConfig_();
checkTrigger_(trigger);
var last = c.fs.getLastRow();
if( last == 1 )
return null;
var status = c.fs.getRange(2,1,last-1,1).getValues();
for( var i = 0; i < status.length; i++ )
if( status[i][0] === '' )
doIt_(i+2);
}
function doIt_(line) {
var raw = c.fs.getRange(line,2,1,c.size).getValues()[0];
var answers = {};
for( var i = 0; i < raw.length; i++ )
answers[c.questions[i]] = raw[i];
if( c.fl ) {
var all = c.fl.getRange(line,c.fc[0],1,c.fc[1]);
var values = all.setFormulasR1C1([c.formulas]).getValues()[0];
for( var i = 0, j = c.fl === c.fs ? c.fc[0]-2 : raw.length; i < values.length;
++i, ++j )
answers[c.questions[j]] = values[i];
}
var status = [];
for( var i = 0; i < s.qtt; ++i ) {
if( s.quota > s.qLimit ) {
try {
var email = {};
var html = s['html'+i] == 'true';
for( var j = 0; j < ef.length-1; ++j ) {
var parts = s[ef[j]+i].split("#");
email[ef[j]] = parts[0];
for( var k = 1; k < parts.length; k+=2 )
email[ef[j]] += (parts[k] != '' ? format_(answers,parts[k],status,html)
: '#') + parts[k+1];
}
if( email.to == '' ) {
status.push(repl_(T.statusNot, s.qtt == 1 ? '' : i+1));
continue;
}
var args = {name:email.sender, cc:email.cc, bcc:email.bcc};
if( html ) args.htmlBody = email.body;
if( email.replyTo == 'noReply' ) args.noReply = true;
else if( email.replyTo != '' ) args.replyTo = email.replyTo;
MailApp.sendEmail(email.to, email.subject, html ? 'html' : email.body,
args);
status.push(repl_(T.statusEmail, s.qtt == 1 ? '' : i+1));
var m = [email.cc != '' || email.bcc != '' ? ',' : '',
email.cc != '' && email.bcc != '' ? ',' : ''];
s.quota -= (email.to + m[0] + email.cc + m[1] + email.bcc).
replace(/"[^"]*"/g,'').split(/,/).length;
} catch(e) {
status.push(repl_(T.statusError, s.qtt == 1 ? '' : i+1, e));
c.err.push(repl_(T.mailError, s.qtt == 1 ? '' : i+1, e, line));
}
} else {
status.push(T.statusQuota);
if( c.err.length == 0 || !startsWith_(c.err[c.err.length-1], T.statusQuota) )
c.err.push(t.statusQuota+'. '+new Date());
break;
}
}
c.fs.getRange(line,1).setValue(status.join('; '));
if( c.fl && line != 2 ) {
if( s.closure === 'values' )
all.setValues([values]);
else if( s.closure === 'clear' )
all.clearContent();
//else formulas > just leave them there
}
}
function format_(answers,part,status,html) {
var t;
if( answers[part] !== undefined ) {
t = [part,''];
} else {
t = part.match(/^(.+)\|([^\|]*)$/);
if( t == null || answers[t[1]] == null ) {
status.push(repl_(T.statusMissingField, t == null ? part : t[1]));
return '';
}
t = t.slice(1);
}
var v = answers[t[0]];
try {
if( t.length == 1 )
t.push('');
if( typeof(v) == 'object' ) {
try {
v = Utilities.formatDate(v, c.tz || (c.tz = c.ss.getSpreadsheetTimeZone()),
t[1] == '' ? ddf : t[1]);
} catch(e) {
status.push(repl_(T.statusDateFormatErr, part));
v = Utilities.formatDate(v, c.tz, ddf);
}
} else if( typeof(v) == 'number' && t[1] != '' && t[1] != 's' ) {
var p = t[1].length-1;
if( t[1].charAt(p) == '%' ) {
v *= 100;
p--;
}
if( t[1].charAt(p).match(/\d/) ) { //number of decimals
v = v.toFixed(+t[1].charAt(p));
p--;
} else
v = v.toString();
var s = v.split('\.');
var sep = t[1].charAt(p);
p--;
if( p >= 0 && t[1].charAt(p) != '(' ) { //thousands sep
s[0] =
s[0].split('').reverse().join('').match(/\d{1,3}/g).join(t[1].charAt(p)).split('').
reverse().join('');
p--;
}
if( v.charAt(0) == '-' ) {
if( p >= 0 && t[1].charAt(p) == '(' ) {
s[0] = '(' + s[0];
s[1] = s[1] + ')';
} else
s[0] = '-' + s[0];
}
v = s.join(sep);
} else {
v = v.toString();
if( t[1] != '' && t[1].charAt(0) == 's' ) {
if( t[1].charAt(1) == 'U' )
v = v.toUpperCase();
else if( t[1].charAt(1) == 'D' )
v = v.toLowerCase();
else if( t[1].charAt(1) == 'P' && v.length > 0 )
v = v.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase()
+txt.substr(1).toLowerCase();});
}
if( html && (t[1] == '' || t[1].charAt(1) != 'H') )
v =
v.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'&qu
ot;');
}
} catch(e) {
status.push(repl_(T.statusFormattingErr, part));
}
return v;
}
function processManually() {
if( !loadOrDefaults_() )
return;
var v = showInput_(T.manualQuestion);
if( v == 'cancel' )
return null;
var multi = false;
if( v.charAt(v.length-1) == '*' ) {
multi = true;
v = v.substring(0,v.length-1);
}
var start = parseInt(v);
var last = c.fs.getLastRow();
if( isNaN(start) ) {
return showMsg_(repl_(T.manualInvalid, v));
} else if( start < 2 ) {
if( multi ) {
start = 2;
showMsg_(T.manual1stMult);
} else {
return showMsg_(T.manual1stSingle);
}
} else if( start > last )
return showMsg_(T.manualAfterLast);
if( !multi )
last = start;
var cont = false;
for( var line = start; line <= last; line++ ) {
if( s.quota <= s.qLimit )
return showMsg_(repl_(T.manualQuotaLimit, line));
if( s.quota <= s.qWarn && !cont ) {
cont = 'yes' == showMsg_(T.manualQuotaWarn, 'YES_NO');
if( !cont )
return null;
}
doIt_(line);
}
showMsg_(c.err.length == 0 ? repl_(last > start ? T.manualSuccessMult :
T.manualSuccessSingle, start, last) : T.manualErrors);
}
function checkTrigger_(runningNow) {
var state = runningNow || (new Date().getTime())-(+sget_('lastRun',0)) <
10*60*100; //10 min
var r = c.cs.getRange('B2');
if( r.getValue() != (state ? T.yes : T.no) ) {
if( state )
r.setValue(T.yes).setFontColor('#008000');
else
r.setValue(T.no).setFontColor('#993300');
}
}
function settings() {
if( !loadOrDefaults_() )
return;
var app = UiApp.createApplication().setTitle(repl_(T.title,
NAME)).setWidth(_appSize[0]).setHeight(_appSize[1]);
var tabPanel = app.createDecoratedTabPanel().setId('tabPanel');
function createEmailTab_(app,idx,h) {
var questions = app.createListBox().setName('question'+idx);
for( var i in c.questions )
questions.addItem(c.questions[i]);
var tp =
app.createVerticalPanel().setSpacing(0).setWidth('100%').setHorizontalAlignment(UiA
pp.HorizontalAlignment.RIGHT).add(
app.createGrid(1,2).setCellPadding(0).setCellSpacing(0).setWidget(
0, 0, createLabel_(app,T.placeholders)).setWidget(
0, 1, css_(questions, _placeHolders)));
var html =
app.createCheckBox(T.html+nbs).setValue(s['html'+idx]=='true').setName('html'+idx);
var vp = app.createHorizontalPanel().setSize('100%',
_body.height).setVerticalAlignment(UiApp.VerticalAlignment.TOP).add(
app.createVerticalPanel().setWidth('100%').setHorizontalAlignment(UiApp.HorizontalA
lignment.RIGHT).add(
css_(createLabel_(app,T.body+':'), {marginTop:'5px'})).add(
html.setWidth('100%')));
grid.setWidget(ef.length-2, 0, vp).setWidget(
ef.length-2, 1,
css_(app.createTextArea().setText(s['body'+idx]).setId('body'+idx).setName('body'+i
dx), _body));
function linkClick_(e) {
var app = UiApp.getActiveApplication();
var id = e.parameter.source.substr(4);
app.getElementById('box'+id).setVisible(true);
app.getElementById(id+':').setVisible(true);
app.getElementById('link'+id).setVisible(false);
return app;
}
function insertQuestion_(e) {
var app = UiApp.getActiveApplication();
var id = e.parameter.source.substr(2);
var idx = id.match(/\d*$/);
var value = e.parameter[id]+'#'+e.parameter['question'+idx]+'#';
app.getElementById(id).setText(value).setFocus(true);
return app;
}
function save_(e) {
var app = UiApp.getActiveApplication();
c.ss = SpreadsheetApp.getActiveSpreadsheet();
//load language
c.cs = c.ss.getSheetByName(NAME);
var r = c.cs.getRange('B3');
T = JSON.parse(r.getValue()).T;
//read settings
for( var i in ap )
s[ap[i]] = e.parameter[ap[i]];
var idx = 0;
for( ; e.parameter['to'+idx] !== undefined; idx++ )
for( var i in ef )
s[ef[i]+idx] = e.parameter[ef[i]+idx];
//do validations
c.ss = SpreadsheetApp.getActiveSpreadsheet();
var id = 'fSheet';
c.fs = c.ss.getSheetByName(s[id]);
if( c.fs == null )
return showErrorDialog_(app,idx,id,T.sheetError);
s[id] = c.fs.getName();
id = 'fLoc';
if( s[id] != '' ) {
var pos = s[id].lastIndexOf('!');
if( pos == -1 )
return showErrorDialog_(app,idx,id,T.formulasError);
var name = s[id].substr(0,pos);
c.fl = name == s.fSheet ? c.fs : c.ss.getSheetByName(name);
if( c.fl == null )
return showErrorDialog_(app,idx,id,T.formulasSheetError);
var maxCol = c.fl.getMaxColumns();
var fc = s[id].substr(pos+1).split(':');
for( var i = 0; i < fc.length; i++ )
if( fc[i].match(/^[a-zA-Z]{1,2}2?$/) ) {
var last = fc[i].length-1;
fc[i] = changeColumnNotation_(fc[i].charAt(last)=='2' ?
fc[i].substr(0,last) : fc[i],true);
if( fc[i] > maxCol )
return showErrorDialog_(app,idx,id,T.formulasColsError);
}
if( fc.length == 1 )
fc.push(fc[0]);
s[id] = c.fl.getName()+'!'+changeColumnNotation_(fc[0],false)
+':'+changeColumnNotation_(fc[1],false);
}
c.size = c.fs.getLastColumn()-1;
c.questions = parseQuestions_(c.fs.getRange(1,2,1,c.size).getValues()[0]);
for( var i = s.qtt-1; i > 0; i-- ) {
if( s[ef[0]+i] == null )
createDefaultEmail_(i);
else
break;
}
//TODO validade emails fields
//look for the questions
//check the adv formattings
//verify the 'To' field; can even take a peek at the real answers
//parse the html
//save settings
delete s.quota;
s.Version = VERSION;
r.setValue(JSON.stringify({s:s, T:T}));
app.close();
return app;
}
function showErrorDialog_(app,tab,id,text) {
app.getElementById('tabPanel').setVisible(false).selectTab(tab);
app.add(css_(app.createDialogBox(false,true).setId('errorDialog').setText(T.errorTi
tle),
{position:'fixed', top:'30%', left:'35%'}).add(
app.createVerticalPanel().add(
app.createLabel(repl_(text,T[id]))).add(app.createLabel(T.badSettings)).add(
app.createButton(T.ok,
app.createServerClickHandler('closeDialog_')).setId('focus'+id))));
return app;
}
function closeDialog_(e) {
var app = UiApp.getActiveApplication();
app.getElementById('errorDialog').setVisible(false);
app.getElementById('tabPanel').setVisible(true);
app.getElementById(e.parameter.source.substr(5)).setFocus(true);
return app;
}
function loadConfig_() {
c.ss = c.ss || SpreadsheetApp.getActiveSpreadsheet();
c.cs = c.cs || getSheet_(NAME);
var saved = JSON.parse(c.cs.getRange('B3').getValue());
s = saved.s;
T = saved.T;
sVersion = +s.Version;
var compat = 3.6;
if( isNaN(sVersion) || sVersion < compat || sVersion > VERSION )
throw repl_(T.versionConflict, s.Version, VERSION, NAME).replace(/ /g,nbs);
c.origQuota = s.quota = getQuotaSecured_();
c.fs = getSheet_(s.fSheet);
c.err = [];
c.size = c.fs.getLastColumn()-1;
var headers = c.fs.getRange(1,2,1,c.size).getValues()[0];
if( s.fLoc != '' ) {
var pos = s.fLoc.lastIndexOf('!');
var name = s.fLoc.substr(0,pos);
c.fc = s.fLoc.substr(pos+1).split(':');
for( var i = 0; i < 2; i++ )
c.fc[i] = changeColumnNotation_(c.fc[i],true);
c.fc[1] = c.fc[1]-c.fc[0]+1;
for( var i = 2; i < 4; i++ ) //custom closure mode
c.fc[i] = c.fc.length > i ? +c.fc[i] : 0;
if( name == s.fSheet ) {
c.fl = c.fs;
} else {
c.fl = c.ss.getSheetByName(name);
headers = headers.concat(c.fl.getRange(1,c.fc[0],1,c.fc[1]).getValues()[0]);
}
c.formulas = c.fl.getRange(2,c.fc[0],1,c.fc[1]).getFormulasR1C1()[0];
}
c.questions = parseQuestions_(headers);
}
function fix() {
if( loadOrDefaults_() ) {
onOpen();
showMsg_(T.noProblem);
}
}
function loadOrDefaults_() {
try {
loadConfig_();
return true;
} catch(e) {
onOpen();
var conflict = e == repl_(T.versionConflict, s.Version, VERSION,
NAME).replace(/ /g,nbs);
var app = UiApp.createApplication().setTitle(NAME).setHeight(140);
var panel =
app.createVerticalPanel().setHorizontalAlignment(UiApp.HorizontalAlignment.CENTER).
setSpacing(10);
var err =
app.createVerticalPanel().setHorizontalAlignment(UiApp.HorizontalAlignment.CENTER);
var closeHandler = app.createServerHandler('closeApp_');
var noHandler;
var yesHandler = 'install';
var msg;
if( !conflict ) {
if( s.Version && !c.fs ) {
var sheetList = app.createListBox().setName("fSheet");
var sheets = c.ss.getSheets();
for( var i in sheets ) {
var name = sheets[i].getName();
if( name != NAME )
sheetList.addItem(name);
}
yesHandler = 'fixSheetName_';
msg = app.createGrid(1,2).setWidth('100%').setWidget(
0, 0, app.createLabel(repl_(T.fSheetMissing, T.fSheet, s.fSheet,
T.yes))).setWidget(
0, 1, css_(sheetList, _placeHolders));
} else
msg = T.badConfig;
var msgs = repl_(T.badConfigCancel, e).replace(/ /g,nbs).split('\n');
for( var i in msgs )
err.add(app.createLabel(msgs[i]).setWidth('100%'));
err.add(app.createButton(T.ok, closeHandler));
noHandler =
app.createClientHandler().forTargets(panel).setVisible(false).forTargets(err).setVi
sible(true);
} else {
msg = T.resolveConflict;
noHandler = closeHandler;
}
panel.setWidth('100%').add(
typeof(msg) == "string" ? app.createLabel(repl_(msg, s.Version, VERSION,
NAME)) : msg).add(
app.createFlowPanel().add(
app.createButton(T.yes,
app.createServerHandler(yesHandler).addCallbackElement(panel))).add(
app.createButton(T.no, noHandler)));
c.ss.show(app.add(panel).add(err.setVisible(false)));
return false;
}
}
function fixSheetName_(e) {
c.ss = SpreadsheetApp.getActiveSpreadsheet();
c.cs = getSheet_(NAME);
var r = c.cs.getRange('B3');
var saved = JSON.parse(r.getValue());
saved.s.fSheet = e.parameter.fSheet;
r.setValue(JSON.stringify(saved));
onOpen();
return closeApp_();
}
function closeApp_() {
return UiApp.getActiveApplication().close();
}
function parseQuestions_(q) {
var map = {};
for( var i = 0; i < q.length; i++ ) {
q[i] = q[i].toString();
q[i] = q[i].length > 70 ? q[i].substr(0,70)+'...' : q[i];
if( map[q[i]] !== undefined ) {
if( map[q[i]].length == 1 )
q[map[q[i]][0]] += '('+changeColumnNotation_(map[q[i]][0]+2,false)+')';
map[q[i]].push(i);
q[i] += '('+changeColumnNotation_(i+2,false)+')';
} else
map[q[i]] = [i];
}
return q; //useful for chaining
}
//change a column notation between number and letter, e.g. B <> 2; AB <> 28
function changeColumnNotation_(col,toNumber) {
if( toNumber ) { //since the greatest value is 256(IV). I prefer this 'if' based
solution over a loop one
var c = col.toUpperCase();
var v = c.charCodeAt(0)-64;
return c.length > 1 ? v*26 + c.charCodeAt(1)-64 : v;
} else {
var toS = function(c) { return String.fromCharCode(c+64); }
return col < 27 ? toS(col) : toS(Math.floor((col-1)/26)) + toS((col-1)%26+1);
}
}
function createDefaults_() {
s.Version = VERSION;
//Form sheet,Qtt Emails,Quota warning,Quota limit,Formulas location,Closure mode
var apv = [c.fs.getName(),1,150,30,'',T.closureValues];
for( var i in apv )
s[ap[i]] = apv[i];
c.questions = parseQuestions_(c.fs.getRange(1,2,1,c.fs.getLastColumn()-
1).getValues()[0]);
createDefaultEmail_(0);
}
function createDefaultEmail_(idx) {
var email = 'user@example.com';
try { email = Session.getEffectiveUser().getEmail(); } catch(suppress) {}
var body = T.defaultBody+'<br>\n<b>'+c.questions[0]+': </b>#'+c.questions[0]+'|
M/d/yyyy H:mm:ss#<br>\n';
for( var i = 1; i < c.questions.length; i++ )
body += '<b>'+c.questions[i]+': </b>#'+c.questions[i]+'#<br>\n';
//sender,replyTo, to, cc,bcc, subject, body, html
var v = [NAME, '', email, '', '', T.defaultSubject, body, 'true'];
for( var i in ef )
s[ef[i]+idx] = v[i];
}
function install(e) {
var overwrite = e !== undefined ? true : false;
var app = UiApp.createApplication().setTitle(NAME).setWidth(520).setHeight(180);
c.ss = c.ss || SpreadsheetApp.getActiveSpreadsheet();
var langDefinitionUrl =
'https://sites.google.com/site/formemailer/translate/languages.txt?
attredirects=0&d=1';
var langs = UrlFetchApp.fetch(langDefinitionUrl).getContentText().split('\n');
var locale = SpreadsheetApp.getActive().getSpreadsheetLocale();
var myLang = locale.split('_')[0];
var found = false;
var langList = css_(app.createListBox().setName("language"), _placeHolders);
var langMap = {};
var langCompat = 0;
var firstLang = null;
var count = 0;
for( var i in langs ) {
var csv = langs[i].split(',');
if( csv[0] == 'versions' ) {
langCompat = 0;
for( var j = 1; j < csv.length; ++j )
if( +csv[j] == VERSION ) {
langCompat = 1;
break;
} else if( +csv[j] < VERSION )
langCompat = 2;
} else if( langCompat != 0 && langMap[csv[0]] === undefined ) {
langMap[csv[0]] = 1;
if( firstLang === null ) firstLang = csv[2];
eval("pack="+UrlFetchApp.fetch(found || firstLang).getContentText());
for( var i in pack )
T[i] = pack[i];
var panel =
app.createVerticalPanel().setHorizontalAlignment(UiApp.HorizontalAlignment.CENTER).
setSpacing(5);
panel.add(
app.createGrid(2, 2).setWidget(
0, 0, app.createLabel(T.pickLanguage).setId('pickLanguage')).setWidget(
0, 1,
langList.addChangeHandler(app.createServerHandler('langChange_').addCallbackElement
(langList))).setWidget(
1, 0, app.createLabel(T.pickSheet).setId('pickSheet')).setWidget(
1, 1, sheetList)).add(
css_(app.createLabel(T.langIncomplete).setId('langIncomplete').setVisible(false),
{fontStyle:'italic'}));
var button = app.createButton(T.install,
app.createServerHandler('proceedInstall_').addCallbackElement(panel)).setId('instal
l');
if( found )
panel.add(
app.createCheckBox(repl_(T.overwrite,
NAME)).setName('overwrite').setId('overwrite').setValue(overwrite).
addValueChangeHandler(app.createClientHandler().forTargets(button.setEnabled(overwr
ite)).setEnabled(false)).
addValueChangeHandler(app.createServerHandler('overwrite_').addCallbackElement(pane
l)));
panel.add(button);
c.ss.show(app.add(panel));
}
function langChange_(e) {
var app = UiApp.getActiveApplication();
var lang = e.parameter.language.split(',');
eval("pack="+UrlFetchApp.fetch(lang[1]).getContentText());
var update = ['pickLanguage','pickSheet','install','overwrite','langIncomplete'];
for( var i in update )
app.getElementById(update[i]).setText(repl_(pack[update[i]] === undefined ?
T[update[i]] : pack[update[i]], NAME));
app.getElementById('langIncomplete').setVisible(lang[0] == '2');
return app;
}
function overwrite_(e) {
var app = UiApp.getActiveApplication();
if( e.parameter.overwrite == 'true' )
app.getElementById('install').setEnabled(true);
return app;
}
function proceedInstall_(e) {
eval("pack="+UrlFetchApp.fetch(e.parameter.language.split(',')
[1]).getContentText());
for( var i in pack )
T[i] = pack[i];
c.ss = SpreadsheetApp.getActiveSpreadsheet();
c.fs = getSheet_(e.parameter.fSheet);
if( c.fs.getLastColumn() == 0 ) {
var app = UiApp.createApplication().setTitle(NAME).setHeight(100);
app.add( app.createVerticalPanel().setHorizontalAlignment(UiApp.HorizontalAlignment
.CENTER).add(
app.createLabel(repl_(T.blankSheet, c.fs.getName()))).add(
app.createButton(T.ok, app.createServerHandler('closeApp_'))));
return c.ss.show(app);
}
c.cs = c.ss.getSheetByName(NAME);
if( c.cs != null ) {
c.cs.clear();
} else
c.cs = c.ss.insertSheet(NAME);
var header = repl_(T.header, NAME).split('\n');
var prepare = [];
for( var i in header )
prepare.push([header[i]]);
c.cs.getRange("A1:A3").setValues(prepare);
var instructions = repl_(T.instructions, T.fSheet).split('\n');
prepare = [];
var boldLine = 6;
for( var i in instructions ) {
prepare.push([instructions[i],'']);
if( boldLine == 6 && instructions[i] == '' )
boldLine += +i;
}
c.cs.getRange(5, 1, instructions.length, 2).mergeAcross().setValues(prepare);
c.cs.getRange('A1:B1').mergeAcross().setHorizontalAlignment('center').setFontSize(1
4);
c.cs.getRange('B2').setValue('No').setFontColor('#993300').setHorizontalAlignment('
left').setFontWeight('bold');
c.cs.getRange('B3').setWrap(false).setValue(JSON.stringify({s:s,
T:T})).setComment(T.settingsComment);
c.cs.getRange('A5').setFontWeight('bold');
c.cs.getRange(boldLine,1).setFontWeight('bold');
c.cs.setColumnWidth(1,217).setColumnWidth(2,342);
onOpen();
if( relocateStatus ) {
var app = UiApp.createApplication().setTitle(NAME).setHeight(100);
app.add(app.createVerticalPanel().setHorizontalAlignment(UiApp.HorizontalAlignment.
CENTER).add(
app.createLabel(repl_(T.relocateStatus, c.fs.getName()))).add(
app.createButton(T.ok, app.createServerHandler('closeApp_'))));
return c.ss.show(app);
} else
return UiApp.getActiveApplication().close();
}
function createLabel_(app,text,id,handler) {
var label = css_(app.createLabel(text,false), _right);
if( id != null )
label.setId(id == '' ? text : id);
if( handler !== undefined )
css_(label.addClickHandler(handler), _link);
return label;
}
function createTextBox_(app,id,style) {
return css_(app.createTextBox().setId(id).setName(id).setText(s[id]+''),
style || (typeof(s[id]) == 'string' ? textParam : numParam));
}
function getSheet_(name) {
var s = c.ss.getSheetByName(name);
if( s == null )
throw name == 'cancel' ? T.cancelMessage : repl_(T.missingSheet, name+'');
return s;
}
function getQuotaSecured_() {
try { //prevent the script from stop working if the quota server fails
return MailApp.getRemainingDailyQuota();
} catch(e) { return 500; }
}
function errorHandling_(f,p) {
var now = new Date().getTime();
sset_('lastRun',now);
//the idea here is to suppress random errors that occurs when using time-driven
triggers.
var errs = +sget_('filterRnd', '0');
if( errs < 4 ) { //number of consecutive errors that are filtered
try { f(p); }
catch(suppress) { return sset_('filterRnd', errs+1); }
} else
f(p);
if( errs != 0 )
sset_('filterRnd', 0);
var warn = s.quota < s.qWarn;
if( c.err.length > 0 ) {
MailApp.sendEmail(Session.getEffectiveUser().getEmail(),
repl_(T.errorReportSubject, NAME),
repl_(T.errorReportBody, c.ss.getName(), c.err.join('\n'))+
(warn ? T.alsoQuota : '') +'\n\n'+c.ss.getUrl());
if( warn )
uset_('quota.lastWarning', now);
} else if( c.origQuota != s.quota && warn && now-
(+uget_('quota.lastWarning',''+now)) > 24*60*60*1000 ) { //1 day
MailApp.sendEmail(Session.getEffectiveUser().getEmail(),
repl_(T.quotaMailSubject, NAME),
repl_(T.quotaMailBody, s.quota, s.qWarn)+ '\n\n' +
c.ss.getUrl());
uset_('quota.lastWarning', now);
}
}