I was trying to create a function that takes a number of minutes and outputs it in a nice readable format. For instance, FORMATMINUTES(1570) would output 1d2h10m, but I got hung up trying to find a truncate function. I also had to change my lets to vars. Is there a reference that tells you what javascript features are available when writing custom functions?
To simplify I created two functions. First, the Math.trunc() function doesn't seem to exist as it doesn't show up as a suggestion and throws an error when used:
function MYTRUNC(input) {
return Math.trunc(input);
}
Second I thought that maybe I could use sheets's built-in TRUNC function, but that isn't defined either:
function MYTRUNC2(input) {
return TRUNC(input)
}
I read google's guide but it says "Custom functions are created using standard JavaScript" but not what version of javascript. Apparently it doesn't support let.
Their app scripts reference has a lot of information on interacting with sheets, but not a basic or complete reference. I realize that in this situation I can use Math.floor which is available or subtract input % 1, but I'd like to know what other idiosyncrasies there might be, and if I can use newer javascript features. Template literals give an error as well, so maybe it's based on an earlier javascript version?
The best reference I have found is from within the online Script Editor's debugger.
Make a breakpoint anywhere in any function. Then run that function with the bug button to start debugging. Next, click on the two buttons at the end of the tool bar: "Show inheritance" and "Show all data".
In the debugger, you should see a this with a "+" to expand it. Expand it and then expand [[prototype]] to see all the builtin App Script objects (GmailApp, etc), then find the next [[prototype]] below those to see the standard JavaScript objects such as Array, Math, Date, etc and you can inspect all of the functions that are available from there.
Related
My code is meant to take data from a user input form and insert a new row to a spreadsheet:
function addNewRow(rowData) {
const currentDate = new Date();
const ss = SpreadsheetApp.getActiveSheet;
const ws = ss.getSheetByName('DataTable');
ws.appendRow(rowData.userName,rowData.userProject, rowData.workDate, rowData.timeSpent, currentDate);
}
Yet when I run the code it returns
TypeError: ss.getSheetByName is not a function
Use const ss = SpreadsheetApp.getActive() to get the spreadsheet. Note the parentheses. The function will not get called if you omit them.
The error "getSheetByName is not a function" occurs when running a function in Google Apps Script when this method is called upon a variable that wasn't assigned a Class Spreadsheet object.
In this case, the OP forgot to include the parenthesis at the end of SpreadsheetApp.getActiveSheet causing Spreadsheet.getActiveSheet method to be assigned to the variable instead of executing this method to return a Class Sheet object. This also happens when chaining another method that doesn't return a Class Spreadsheet Object, i.e. Spreadsheet.getActiveSheet().getName() returns a string.
In the OP case the solution is straightforward as already was show in the previous answer. Other cases having the same error might require a different solution, to find it,
Check the correct spelling and syntax. For this it's very helpful to understand the pretty basics of JavaScript, i.e, JavaScript is case sensitive. This means that getActiveSheet is not the same as getactivesheet.
Check the official guides in https://developers.google.com/apps-script/overview, more specifically https://developers.google.com/apps-script/guides/sheets.
Check the official reference in https://developers.google.com/apps-script/reference. In this specific case you might search for getSheetByName directly in this link. In this case this method is used in several examples that might be helpful to understand it.
Going deep in the Google Apps Script editor, use the autocomplete feature to have instant feedback about it the method is available for the object assigned to the variable: Type a dot after the variable name, this will show a list of the available methods, continue typing the method name to filter the list suggestions, if there isn't any suggestion, then there is very likely that there is a problem on the variable declaration. Ref. https://developers.google.com/apps-script/guides/services/#using_autocomplete
Use the Google Apps Script debugger. Ref. https://developers.google.com/apps-script/guides/support/troubleshooting#debugging
Go deep in learning JavaScript. There a lot of resources to start learning JavaScript. By default Google Apps Script use V8 to execute scripts. Please bear in mind that Web APIs like the DOM API aren't available in V8. It's worthy to note that there are some JavaScript functions that aren't supported in Google Apps Script like async as well some Global Objects like Promise
Related
How do I check if an object has a specific property in JavaScript?
Accessing spreadsheet in Google Script (this question is about a similar TypeError message)
Automatically replace dots with commas in a Google Sheets Column with Google Script (this question is about a similar TypeError message)
TL;DR: How can I access variables/functions/names that are defined in ES Modules from the debugger?
More context: I'm a relatively experienced JavaScript programmer, but new to Modules. I've followed the tutorial at MDN here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules. They have a good set of examples here: https://github.com/mdn/js-examples/tree/master/modules
In that collection, say in the "basic-modules" example, (live code here: https://mdn.github.io/js-examples/modules/basic-modules/) there is, for example, a function called random in the file modules/square.js. Suppose I want to execute that function in the debugger, just to try it out, or because it's my code and I want to test/debug it, or I want to demonstrate to another coder what the function does. All the stuff you expect to do in a REPL or debugger. Is there a way to do that? I've tried both the Firefox debugger and the Chrome debugger, with no luck.
Back in the pre-Modules era, that code would be put into the global namespace (making access easy) or it would be locked up in an IIFE (making access impossible) or maybe in some home-made module system (access depends). I am hoping that the new Modules system still allows the debugger access to the names inside modules.
Thanks.
It says in the docs:
Last but not least, let's make this clear — module features are imported into the scope of a single script — they aren't available in the global scope. Therefore, you will only be able to access imported features in the script they are imported into, and you won't be able to access them from the JavaScript console, for example. You'll still get syntax errors shown in the DevTools, but you'll not be able to use some of the debugging techniques you might have expected to use.
To take your example from before, you'll need to invoke that function from a scope where it is visible, i.e where it's been imported:
import { random } from 'path/to/square.js'
debugger; // you should be able to invoke random() from here
Here's what I'm looking for:
I want to use the wonderful features of SIMPLE mode minification while disabling just one specific feature (disable local function inline).
UPDATE: The answer is NO, it's not possible given my setup. But for me there is a workaround given I am using Grails.
As #Chad has explained below, "This violates core assumptions of the compiler". See my UPDATE3 below for more info.
IN QUESTION FORM:
I'm using CompilationLevel.SIMPLE_OPTIMIZATIONS which does everything I want, except that it's inlining my local functions.
Is there any way around this? For example, is there a setting I can place in my JS files to tell Google Closure not to inline my local functions?
It would be cool to have some directives at the top of my javascript file such as:
// This is a JS comment...
// google.closure.compiler = [inlineLocalFunctions: false]
I'm developing a Grails app and using the Grails asset-pipeline plugin, which uses Google Closure Compiler (hereafter, Compiler). The plugin supports the different minification levels that Compiler supports via the Grails config grails.assets.minifyOptions. This allows for 'SIMPLE', 'ADVANCED', 'WHITESPACE_ONLY'.
AssetCompiler.groovy (asset-pipeline plugin) calls ClosureCompilerProcessor.process()
That eventually assigns SIMPLE_OPTIMIZATIONS on the CompilerOptions object. And by doing so, CompilerOptions.inlineLocalFunctions = true as a byproduct (this is hard coded behavior in Compiler). If I were to use WHITESPACE_ONLY the result would be inlineLocalFunctions=false.
So by using Asset Pipeline's 'SIMPLE' setting, local functions are being inlined and that is causing me trouble. Example: ExtJS ext-all-debug.js which uses lots of local functions.
SO post Is it possible to make Google Closure compiler *not* inline certain functions? provides some help. I can use its window['dontBlowMeAway'] = dontBlowMeAway trick to keep my functions from inlining. However I have LOTS of functions and I'm not about to manually do this for each one; nor would I want to write a script to do it for me. Creating a JS model and trying to identity local functions doesn't sound safe, fun nor fast.
The previous SO post directs the reader to https://developers.google.com/closure/compiler/docs/api-tutorial3#removal, where the window['bla'] trick is explained, and it works.
Wow thanks for reading this long.
Help? :-)
UPDATE1:
Okay. While spending all the effort in writing this question, I may have a trick that could work. Grails uses Groovy. Groovy makes method call interception easy using its MetaClass API.
I'm going to try intercepting the call to:
com.google.javascript.jscomp.Compiler.compile(
List<T1> externs, List<T2> inputs, CompilerOptions options)
My intercepting method will look like:
options.inlineLocalFunctions=false
// Then delegate call to the real compile() method
It's bed time so I'll have to try this later. Even so, it would be nice to solve this without a hack.
UPDATE2:
The response in a similar post (Is it possible to make Google Closure compiler *not* inline certain functions?) doesn't resolve my problem because of the large quantity of functions I need inlined. I've already explained this point.
Take the ExtJS file I cited above as an example of why the above similar SO post doesn't resolve my problem. Look at the raw code for ext-all-debug.js. Find the byAttribute() function. Then keep looking for the string "byAttribute" and you'll see that it is part of strings that are being defined. I am not familiar with this code, but I'm supposing that these string-based values of byAttribute are later being passed to JS's eval() function for execution. Compiler does not alter these values of byAttribute when it's part of a string. Once function byAttribute is inlined, attempts to call the function is no longer possible.
UPDATE3: I attempted two strategies to resolve this problem and both proved unsuccessful. However, I successfully implemented a workaround. My failed attempts:
Use Groovy method interception (Meta Object Protocol, aka MOP) to intercept com.google.javascript.jscomp.Compiler.compile().
Fork the closure-compiler.jar (make my own custom copy) and modify com.google.javascript.jscomp.applySafeCompilationOptions() by setting options.setInlineFunctions(Reach.NONE); instead of LOCAL.
Method interception doesn't work because Compiler.compile() is a Java class which is invoked by a Groovy class marked as #CompileStatic. That means Groovy's MOP is not used when process() calls Google's Compiler.compile(). Even ClosureCompilerProcessor.translateMinifyOptions() (Groovy code) can't be intercepted because the class is #CompileStatic. The only method that can be intercepted is ClosureCompilerProcessor.process().
Forking Google's closure-compiler.jar was my last ugly resort. But just like #Chad said below, simply inserting options.setInlineFunctions(Reach.NONE) in the right place didn't resurrect my inline JS functions names. I tried toggling other options such as setRemoveDeadCode=false to no avail. I realized what Chad said was right. I would end up flipping settings around and probably destroying how the minification works.
My solution: I pre-compressed ext-all-debug.js with UglifyJS and added them to my project. I could have named the files ext-all-debug.min.js to do it more cleanly but I didn't. Below are the settings I placed in my Grails Config.groovy:
grails.assets.minifyOptions = [
optimizationLevel: 'SIMPLE' // WHITESPACE_ONLY, SIMPLE or ADVANCED
]
grails.assets.minifyOptions.excludes = [
'**ext-all-debug.js',
'**ext-theme-neptune.js'
]
Done. Problem solved.
Keywords: minify, minification, uglify, UglifyJS, UglifyJS2
In this case, you would either need to make a custom build of the compiler or use the Java API.
However - disabling inlining is not enough to make this safe. Renaming and dead code elimination will also cause problems. This violates core assumptions of the compiler. This local function is ONLY referenced from within strings.
This code is only safe for the WHITESPACE_ONLY mode of the compiler.
Use the function constructor
var fnc = new Function("param1", "param2", "alert(param1+param2);");
Closure will leave the String literals alone.
See https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Function
I often see in my code I am loading from my company's media central that jQuery is not available in the console normally. (No $ and jQuery)
But sometimes, to those elements to which jQuery is attached, it has a long number with it.
jQuery18306575689211022109_1378907534666
What is the purpose of doing this? Security?
Also, jQuery is sometimes available directly in the console, but only with the above numbers.
I am therefore unable to debug my apps in console, where I need to query using jQuery.
However, in the JavaScript code, jQuery is perfectly being used as $ and jQuery. So I apply a break-point and export as window.jQuery = jQuery.
Is this a proper way to debug the app, when jQuery is obfuscated?
UPDATE:
For eg., check this URL the app is calling. It seems that the URL knows what is the number appended to jQuery. How do I come to know of the same number while debugging? Any lights on what is going on?
It's a callback id automatically generated by jQuery. See the documentation for jsonpCallback.
It is preferable to let jQuery generate a unique name as it'll make it easier to manage the requests and provide callbacks and error handling.
It seems that you are confusing two different variables. You can make a quick test on this website : http://www.jquery4u.com/function-demos/jsonp/#demo. Click "run demo", open Chrome's console, type "jQuery" in order to retrieve the callback's name, then perform this simple comparison :
jQuery16409391013463027775_1379048051365 === jQuery // false
That said, the fact that the variables named "$" and "jQuery" are not available in your case may be due to a specific implementation. One possibility could be the use of jQuery.noConflict() which allows either to customize the name used as a reference to jQuery, or to completely remove all references to jQuery from the global scope (window), so there is no way to access it in the console.
I write a plugin for android browser and follow the npruntime rule to let it support JavaScript method. However, after I call the function of my plugin in JavaScript, I get different identifier number in NPAPI's pluginHasMethod() function. I am sure there is no typo error in my JavaScript code. Is there any idea to debug this situation?
Thanks in advance.
NPN_UTF8fromIdentifier is a function you have to provide yourself to call the correct function on the NPNFuncs that you were given when the plugin initialized.
The NPIdentifier is only guaranteed to be the same during the same browser instance; if you quit the browser and start a new one, it may change.
All NPN_* functions are not real functions; those are the "typical" names that you might use for them, but in actuality you are given a structure of function pointers of type NPNFuncs that will have a function pointer for each of the NPN_* functions; if you want those to actually work you need to create the NPN_UTF8FromIdentifier function, etc, and have it call the correct function pointer.
For more info see http://npapi.com/tutorial and http://npapi.com/tutorial2