Call Library function from html with google.script.run - javascript

I implement library with Google App Script and I have some difficulties to call a function from library using google.script.run.
Here is the code of my Library :
Code.gs
function ShowSideBar() {
var html = HtmlService.createTemplateFromFile('Index_librairie').evaluate()
.setTitle('Console de gestion')
.setWidth(300);
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.showSidebar(html);
}
function execution_appeler_par_html(){
Logger.log("execution_appeler_par_html");
}
Index_librairie.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script>
google.script.run.withSuccessHandler(work_off).execution_appeler_par_html();
function work_off(e){
alert(e);
}
</script>
</head>
<body>
test de ouf
</body>
</html>
Here is my Spreadsheet that use the Library :
Code.gs
function onopen() {
lbrairietestedouard.ShowSideBar();
}
Google.script.run does not reconize execution_appeler_par_html() function.
I should use libraryname.execution_appeler_par_html() but this syntaxe doesn't work in configuration of google.script.run

It seems that google.script.run can't look inside Objects or self-executing anonymous functions. In my case, putting any code inside an object or IIFE resulted in "is not a function" type of error being thrown in the console.
You can work around this by declaring a single function that will call nested methods inside libraries and objects.
.GS file
function callLibraryFunction(func, args){
var arr = func.split(".");
var libName = arr[0];
var libFunc = arr[1];
args = args || [];
return this[libName][libFunc].apply(this, args);
}
In JavaScript, every object behaves like an associative array of key-value pairs, including the global object that 'this' would be pointing to in this scenario. Although both 'libName' and 'libFunc' are of String type, we can still reference them inside the global object by using the above syntax. apply() simply calls the function on 'this', making the result available in global scope.
Here's how you call the library function from the client:
google.script.run.callLibraryFunction("Library.libraryFunction", [5, 3]);
I don't claim this solution as my own - this is something I saw at Bruce McPherson's website a while back. You could come up with other ad-hoc solutions that may be more appropriate for your case, but I think this one is the most universal.

Noticed the same problem trying to invoque library from google.script.run within HTML code.
Here is the workaround I use :
LIBRARY SIDE : aLibrary
function aFunction(){
//code...;
}
ADDON SIDE (requires library "aLibrary")
function aFunction(){
aLibrary.aFunction();
}
HTML
<input type="button" value="run aFunction" onclick="google.script.run.aFunction()" />
I like the workaround because I keep a clear view and organisation of my functions names, and do not need to alter HTML code if I bring my functions directly inside the addOn project, once development is satsifying.
Only thing I did not try yet : handling arguments and return values....
I hope this contribution is not to foolish, please forgive me I am very amateur...

After quite long time working on this, I found out that any function that you call from the library using google.script.run must exist in both the main script and the library script. The function must at least be declared in the main script while the implementation as well as the parameters are not important. The implementation will be in your library. If the main script does not include the name of the function, the error <your function> is not a function will appear.
Now that there is no meaning of having a library if every single function needs to exist in the main function, the solution provided by #Anton Dementiev would be used as a workaround. Suppose you have a function named testFunc in your library, you can call it using the following method:
google.script.run.callLibraryFunction("testFunc", [5, 3]);
where the library has this function:
function callLibraryFunction(func, args) {
args = args || [];
return this[func].apply(this, args);
}
and the main script has this declaration:
function callLibraryFunction() {
}
Google must fix this stupid behaviour

Related

Razor Syntax in External Javascript

So as you might know, Razor Syntax in ASP.NET MVC does not work in external JavaScript files.
My current solution is to put the Razor Syntax in a a global variable and set the value of that variable from the mvc view that is making use of that .js file.
JavaScript file:
function myFunc() {
alert(myValue);
}
MVC View file:
<script language="text/javascript">
myValue = #myValueFromModel;
</script>
I want to know how I can pass myValue directly as a parameter to the function ? I prefer to have explicit calling with param than relying on globals, however I'm not so keen on javascript.
How would I implement this with javascript parameters? Thanks!
Just have your function accept an argument and use that in the alert (or wherever).
external.js
function myFunc(value) {
alert(value);
}
someview.cshtml
<script>
myFunc(#myValueFromModel);
</script>
One thing to keep in mind though, is that if myValueFromModel is a string then it is going to come through as myFunc(hello) so you need to wrap that in quotes so it becomes myFunc('hello') like this
myFunc('#(myValueFromModel)');
Note the extra () used with razor. This helps the engine distinguish where the break between the razor code is so nothing odd happens. It can be useful when there are nested ( or " around.
edit
If this is going to be done multiple times, then some changes may need to take place in the JavaScript end of things. Mainly that the shown example doesn't properly depict the scenario. It will need to be modified. You may want to use a simple structure like this.
jsFiddle Demo
external.js
var myFunc= new function(){
var func = this,
myFunc = function(){
alert(func.value);
};
myFunc.set = function(value){
func.value = value;
}
return myFunc;
};
someview.cshtml
<script>
myFunc.set('#(myValueFromModel)');
myFunc();//can be called repeatedly now
</script>
I often find that JavaScript in the browser is typically conceptually tied to a specific element. If that's the case for you, you may want to associate the value with that element in your Razor code, and then use JavaScript to extract that value and use it in some way.
For example:
<div class="my-class" data-func-arg="#myValueFromModel"></div>
Static JavaScript:
$(function() {
$('.my-class').click(function() {
var arg = $(this).data('func-arg');
myFunc(arg);
});
});
Do you want to execute your function immediately? Or want to call the funcion with the parameter?
You could add a wrapper function with no parameter and inside call your function with the global var as a parameter. And when you need to call myFunc() you call it trough myFuncWrapper();
function myFuncWrapper(){
myFunc(myValue);
}
function myFunc(myParam){
//function code here;
}

jasmine: using function with a dot in the name

I'm testing a js function that uses functions from other js files.
One of my external js files has a function defined as such:
functionname.functionextension = function () {.....}
when testing using jasmine, and calling functionname.functionextension, it complains that functionname is not defined. I think it believes that functionname is an object..
I know that one way to get around this is to modify the function name but I can't do that. Is there any other way?
Thanks
In javascript, all functions are objects. In the external js file, the function is probably defined like this:
var functionname = functionname || {};
functionname.functionextension = function () {
...
};
If you're getting a script error that functionname is not defined, there is either an error in the external javascript or you are not calling some initialization function that the external script requires to set up its objects.
It worked for me...u need to call function by its full name like functionname.functionextension() while calling.

How to generate global, named javascript functions in coffeescript, for Google Apps Script

I'd like to write Javascript scripts for Google Apps Script using CoffeeScript, and I'm having trouble generating functions in the expected form.
Google Apps Script expects a script to contain top-level, named functions. (I may be using the wrong terminology, so I'll illustrate what I mean with examples...)
For example, this function is happily recognised by Google Apps Script:
function triggerableFunction() {
// ...
}
... while this function is not (it will parse, but won't you won't be able to trigger it):
var nonTriggerableFunction;
nonTriggerableFunction = function() {
// ...
};
I've found that with CoffeeScript, the closest I'm able to get is the nonTriggerableFunction form above. What's the best approach to generating a named function like triggerableFunction above?
I'm already using the 'bare' option (the -b switch), to compile
without the top-level function safety wrapper.
The one project I've found on the web which combines CoffeeScript and Google App Script is Gmail GTD Bot, which appears to do this using a combination of back-ticks, and by asking the user to manually remove some lines from the resulting code. (See the end of the script, and the 'Installation' section of the README). I'm hoping for a simpler and cleaner solution.
CoffeeScript does not allow you to create anything in the global namespace implicitly; but, you can do this by directly specifying the global namespace.
window.someFunc = (someParam) ->
alert(someParam)
Turns out this can be done using a single line of embedded Javascript for each function.
E.g. this CoffeeScript:
myNonTriggerableFunction = ->
Logger.log("Hello World!")
`function myTriggerableFunction() { myNonTriggerableFunction(); }`
... will produce this JavaScript, when invoking the coffee compiler with the 'bare' option (the -b switch):
var myNonTriggerableFunction;
myNonTriggerableFunction = function() {
return Logger.log("Hello World!");
};
function myTriggerableFunction() { myNonTriggerableFunction(); };
With the example above, Google Apps Script is able to trigger myTriggerableFunction directly.
This should give you a global named function (yes, it's a little hacky, but far less that using backticks):
# wrap in a self invoking function to capture global context
do ->
# use a class to create named function
class #triggerableFunction
# the constructor is invoked at instantiation, this should be the function body
constructor: (arg1, arg2) ->
# whatever
juste use # in script, exemple of my code :
#isArray = (o)->
Array.isArray(o)
it will be compiled in :
(function() {
this.isArray = function(o) {
return Array.isArray(o);
};
}).call(this);
this = window in this case, so it's global function

Avoiding eval when executing js returned from ajax call

I want to make an ajax call that will return a json object. One of this JSON object's properties will be the string of a function to be executed in the client. I realise this can easily be solved by using eval, but seeing the many disadvantages of eval, I'd rather avoid it. My question is:
Can I in some way return from the server some js code and execute it without resorting to eval?
As requested, here's some example code:
Server (Node.js):
var testFunc = function() {
alert('h1');
};
app.get('/testPack', function(req, res) {
var template = jade.render('h1 hi');
res.send({
template : template,
entity : testFunc.toString(),
data : {
id: "OMG I love this"
}
});
});
Client:
$(document).ready(function() {
$.ajax({
url: '/testPack',
success: function(data) {
$('body').append($(data.template))
alert(data.data.id);
var entity = eval(data.entity);
entity();
}
})
})
Of course, the returned function called entity wouldn't do such a silly thing, it would expose an API of the returned widget.
Just to clarify, I'd like to avoid having to make a separate call for the javascript itself. I'd rather bundle it with the template and data to render.
Easiest way to do that, is not to call a server through an ajax, but instead to create a new script tag on the page with the url pointing to a RESTful web-service that would output pure JavaScript (not JSON). That way your output will be evaluated by the browser directly without the use of eval.
To expand a little on my answer:
To get around the problems of running script in the global context you could do some tricks. For example, when you are adding script tag to the head, you can bind onload event (or rather fake onload event, since IE doesn't support onload on the script tag) to it, and if your response from the server will be always wrapped in the the function with a known name, you could apply that function from within your object. Example code below (this is just an example though):
function test ()
{
this.init = function ()
{
var script = document.createElement("script");
script.type = "text/javascript";
script.language = "javascript";
script.src = "test.js";
var me = this;
window.callMe = function () { me.scriptReady(me); };
var head = document.getElementsByTagName("head")[0];
head.appendChild(script);
};
this.scriptReady = function (object)
{
serverResponse.call(object);
};
this.name = "From inside the object";
this.init();
}
var t=new test();
The server response should look something like this:
function serverResponse()
{
alert(this.name);
}
window.callMe();
In this case, everything inside serverResponse() will use your object as "this". Now if you modify your server response in this way:
function serverResponse()
{
this.serverJSONString = { "testVar1": "1", "testVar2": 2 };
function Test()
{
alert("From the server");
}
Test();
}
window.callMe();
You can have multiple things being returned from the server and with just one response. If you don't like just setting variables, then create a function in your main object to handle JSON string that you can supply by calling this function from your response.
As you can see, it's all doable, it really doesn't look pretty, but then again, what you are trying to do is not pretty to begin with.
P.S. Just inserting a string inside tag will not work for IE, it will not allow you to do that. If you don't have to support IE, then you could get away with just inserting server response inside a newly created script tag and be done with it.
P.P.S. Please don't use this code as is, cause I didn't spend too much time writting it. It's ugly as hell, but was just ment as an example:-)
No, you can't do this by definition, because JavaScript functions are not valid JSON. See the spec here:
http://www.json.org/
If you're returning a string, then that's what it is: just a string. You can't evaluate it without eval. You can call whatever else you're returning whatever you want, but please don't call it JSON.
Here's an example of how I think this could work.
The json object represents what is returned from the server. The c and d properties contain function names as strings. If those functions are properties of some other object which exists in your page, then you should be able to call them using the object["property"] accessor.
See it working on jsFiddle: http://jsfiddle.net/WUY4n/1/
// This function is a child of the window object
window.winScopedFunction = function() {
alert("ROCK THE WIN");
}
// This function is a child of another object
var myObject = {
myFunction : function() {
alert("ROCK ON");
}
};
// pretend that this json object was the result of an ajax call.
var jsonResultFromServer= {
a : 1,
b : 2,
c : "myFunction",
d : "winScopedFunction"
};
// you can call the local functions like so
myObject[jsonResultFromServer.c]();
window[jsonResultFromServer.d]();
Yes, there's a way, but it has the exact same disadvantages as eval.
You can use the Function constructor to create a new function, and then call it. For example:
new Function(code)();
http://code.google.com/p/json-sans-eval/ is a fast JSON parser that does not use eval, and JSON.parse is becoming increasing widely available in new browsers. Both are excellent alternatives to eval for parsing JSON.
You can use the trick that Google does with Google Charts.
<html>
<head>
<script>
function onWorkDone(data) {
console.log(data);
}
</script>
<script src="callback.js"></script>
</head>
</html>
Then your callback.js is:
function doWork(callback) {
callback({result: 'foo'});
}
doWork(onWorkDone);
Basically, your script will call onWorkDone when the doWork completed. You can see a working example here:
http://jsfiddle.net/ea9Gc/
Do you have some example cases? Some things I can think of is you that you can just have a regular function inside your js file, and your server will return some parameters for your function to execute. You can even specify what function to use! (Isn't that amazing?)
// your js file
var some_namespace = {
some_function : function(a, b){
// stuff
}
}
// your server output
{
some_other_data: "123",
execute: {
func: "some_namespace.some_function",
params: [1, 2]
}
}
// your ajax callback
function(r){
window[r.execute.func].apply(this, r.execute.params);
}
The reasons of not using eval
Well, you already said it yourself. Don't use eval. But you have a wrong picture regarding why.
It is not that eval is evil. You are getting the reason wrong. Performance considerations aside, using eval this way allows a sloppy programmer to execute code passed from server on the client. Notice the "passed from server" part.
Why never execute code passed from server
Why don't you want to execute code passed from the server (incidentally that's what you're planning to do)?
When a browser executes a script on a web page, as long as the web site is valid -- i.e. really yours, and not a malware site pretending to be yours trying to trick your users -- you can be reasonably sure that every bit of code the browser is running is written by yourself.
Hacker's heaven -- script injection attacks
Now, if you are passing data from the server to your web application, and that data contains executable functions, you're asking for trouble. In the long, twisted journey of that data going from your server to your client's browser, it goes through the wild west called the Internet, perhaps through multiple layers of proxies and filters and converters, most of which you do not control.
Now, if a hacker is hiding somewhere in the middle, takes your data from the server, modify the code to those functions to something really bad, and sends it away to your client, then your client browser takes the data and executes the code. Voila! Bad things happen. The worse is: you (at the server side) will never know that your clients are hacked.
This is called a "script injection attack" and is a serious sercurity risk.
Therefore, the rule is: Never execute functions returned from a server.
Only pass data from server
If you only accept data from a server, the most that can happen whan a hacker tempers with it is that your client will see strange data coming back, and hopefully your scripts will filter them out or handle them as incorrect data. Your client's browser will not be running any arbitrary code written by the hacker with glee.
In your client-side script, of course you're sticking to the Golden Rule: Do not trust ANY data coming through the Internet. Therefore you'd already be type-check and validating the JSON data before using it, and disallowing anything that looks suspicious.
Don't do it -- pass functions from server and execute on client
So, to make a long story short: DON'T DO IT.
Think of another way to specify pluggable functionalities on the browser -- there are multiple methods.
I've had this same question, and I fixed it this way:
File: functions.js.php?f=1,3
$functions=array(
'showMessage' => 'function(msg){ alert(msg); }',
'confirmAction' => 'function(action){
return confirm("Are you sure you want to "+action+"?");
}',
'getName' => 'function getName(){
return prompt("What is your name?");
}'
);
$queried = explode($_REQUEST['f']);
echo 'var FuncUtils = {'; // begin javascript object
$counter=1;
foreach($functions as $name=>$function){
if(in_array($counter, $queried))
echo '"'.$name.'":,'.$function.',';
$counter++;
}
echo '"dummy":null };'; // end javascript object
File: data5.json
{
"action" : ['confirmAction','exit']
}
File: test.js
$(document).ready(function(){
$.getScript('functions.js.php?f=1,3');
});
function onBeforeExit(){
$.getJSON('data5.json', function(data) {
var func = data.action.shift();
FuncUtils[func].apply(null, data.action);
});
}

How do I pass argument to anonymous Javascript function?

I am writing a simple counter, and I would like to make installation of this counter very simple for users. One of the simplest counter code (for users who install it) I ever see was Google Analytics Code
So I would like to store main code in a file and user who will install my counter will need just to set websiteID like this:
<html><head><title></title></head><body>
<script type="text/javascript" src="http://counterhost.lan/tm.js">
var websiteId = 'XXXXX';
</script>
</body></html>
Here is my code:
<script type="text/javascript" src="http://counterhost.lan/tm.js">
var page = _gat.init('new');
</script>
and this is my JS file:
(function() {
var z = '_gat';
var aa = function init(data) { alert(data); alert(z);};
function na() {
return new z.aa();
}
na();
})();
I tried to understand Google Analytics javascript code but I failed to do this. Can anyone suggest how can I specify variable between tags and then read it in anonymous function which is located in a javascript file ?
Thanks.
In your example, websiteId is a global variable. So it is accessible everywhere including anonymous functions unless there is a local variable with the same name
<script> var websiteId = "something"; </script>
Later in the page or included js file...
(function() {
alert(websiteId); //this should work
})();
Can anyone suggest how can I specify variable between tags and then read it [...]
Not if your tag has both a SRC attribute and JS content.
<script type="text/javascript" src="http:/x.com/x.js"></script>
.. is different from,
<script type="text/javascript">
var x = 1;
</script>
One framework that optionally adds JS variables to SCRIPT tags is Dojo. So if you're using Dojo you can add variables to the global djConfig hash by writing,
<script type="text/javascript" src="mxclientsystem/dojo/dojo.js"
djConfig="
usePlainJson: true,
parseOnLoad: true
">
</script>
Dojo does this by running through the SCRIPT tags and evaluating the custom djConfig attribute.
This does not, however solve your problem.
You do really want two SCRIPT tags. One saying,
<script type="text/javascript">
var websiteId = '123456';
</script>
which will set a global variable websiteId and a second one,
<script type="text/javascript" src="http:/x.com/myreporter.js"></script>
which can load from anywhere and read out the websiteId variable and, I assume, report it back.
You can pass variables to an anonymous function like so:
(function(arg1, arg2, arg3) {
alert(arg1);
alert(arg2);
alert(arg3);
})("let's", "go", "redsox");
// will alert "let's", then "go", then "redsox" :)
I'm not entirely clear about what you're asking, but...
You can tag any HTML element with an id attribute, then use
document.getEntityById() to retrieve that specific element.
You can also give any HTML element user-defined attributes having names of your own choosing, then get and set them for that element within Javascript.
I think you've got a bit confused with how JS objects are called.
z is a String, '_gat'. You can't call aa() on it because a String has no member called aa. aa is a standalone function stored in a local variable. Even if you did call aa(), it doesn't return anything, so using the new operator on its results is meaningless. new can only be called on constructor-functions.
I guess you mean something like:
var _gat= function() {
// Private variable
//
var data= null;
// Object to put in window._gat
//
return {
// Set the private variable
//
init: function(d) {
data= d;
}
};
}();
Then calling _gat.init('foo') as in your second example would set the variable to website ID 'foo'. This works because the _gat object is the return {init: function() {...}} object defined inside the anonymous function, keeping a reference (a ‘closure’) on the hidden data variable.
If you specify a src attribute as part of a script element, any code within the script element tags themselves will not be executed. However, you can add this functionality with the following code. I got this technique from Crockford (I believe it was him), where he uses it in of his talks on the unrelated topic of rendering performance and asynchronously loading scripts into a page to that end.
JavaScript:
(function() {
// Using inner class example from bobince's answer
var _gat = (function() {
var data= null;
return {
init: function(d) {
console.info("Configuration data: ", d);
data = d;
}
}
})();
// Method 1: Extract configuration by ID (SEE FOOT NOTE)
var config = document.getElementById("my-counter-apps-unique-and-long-to-avoid-collision-id").innerHTML;
// Method 2: search all script tags for the script with the expected name
var scripts = document.getElementsByTagName("script");
for ( var i=0, l=scripts.length; i<l; ++i ) {
if ( scripts[i].src = "some-script.js" ) {
config = scripts[i].innerHTML;
break;
}
}
_gat.init( eval("(" +config+ ")") );
})();
HTML:
<script type="text/javascript" src="some-script.js" id="my-counter-apps-unique-and-long-to-avoid-collision-id">
{some: "foo", config: "bar", settings: 123}
</script>
Both methods have their draw backs:
Using a unique and non-colliding ID will make determining the proper script element more precise and faster; however, this is not valid HTML4/XHTML markup. In HTML5, you can define arbitrary attributes, so it wont be an issue at that time
This method is valid HTML markup; however, the simple comparison that I have shown can be easily broken if your url is subject to change (e.g.: http vs https) and a more robust comparison method may be in order
A note on eval
Both methods make use of eval. The typical mantra concerning this feature is that "eval is evil." However, that goes with say that using eval without knowing the dangers of eval is evil.
In this case, AFAIK, the data contained within the script tags is not subject to inject attack since the eval'ing script (the code shown) is executed as soon as that element is reached when parsing the HTML into the DOM. Scripts that may have been defined previously are unable to access the data contained within the counter's script tags as that node does not exist in the DOM tree at the point when they are executed.
It may be the case that a well timed setTimeout executed from a previously included script may be able to run at the time between the counter's script's inclusion and the time of the eval; however, this may or may not be the case, and if possible, may not be so consistently depending on CPU load, etc.
Moral of the story, if you're worried about it, include a non-eval'ing JSON parser and use that instead.

Categories