Calling a function inside a function - converting AS2 to AS3 - actionscript-3

I currently have some code from here (https://github.com/jmhnilbog/Nilbog-Lib-AS2/blob/master/mx/mx/remoting/NetServiceProxy.as) which converts a function into a function. This code is shown below:
private var _allowRes:Boolean= false;
function __resolve( methodName:String ):Function {
if( _allowRes ) {
var f = function() :Object {
// did the user give a default client when he created this NetServiceProxy?
if (this.client != null) {
// Yes. Let's create a responder object.
arguments.unshift(new NetServiceProxyResponder(this, methodName));
}
else {
if (typeof(arguments[0].onResult) != "function") {
mx.remoting.NetServices.trace("NetServices", "warning", 3, "There is no defaultResponder, and no responder was given in call to " + methodName);
arguments.unshift(new NetServiceProxyResponder(this, methodName));
}
}
if(typeof(this.serviceName) == "function")
this.serviceName = this.servicename;
arguments.unshift(this.serviceName + "." + methodName);
return( this.nc.call.apply(this.nc, arguments));
};
return f;
}
else {
return null;
}
}
Basically what the code is designed to do is return a new function (returned as f) which performs the correct server operates. However, if I try and use this syntax in AS3, I get the following two errors:
Error: Syntax error: expecting semicolon before colon.
Error: Syntax error: else is unexpected.
How would I go about doing this? I know this is someone else's code, but I am trying to get the old AS1/2 mx.remoting functionality working in AS3. Cheers.

Related

Variable scope & Callback woes

This program is reading through the nested object searching for a specific key & values. Once this data is found it has to initiate callback to send back the data. The object looks like this:
{
"name": "joel",
"title": "CTO",
"edu": {
"school": "RMB",
"college": "GNK",
"pg": "CDAC",
"extract": "This is a large text ..."
}
}
Here as I come from synchronous programming background I am not able to understand when I have to initiate the callback and also ensure variables are in scope
function parseData(str, callback) {
function recursiveFunction(obj) {
var keysArray = Object.keys(obj);
for (var i = 0; i < keysArray.length; i++) {
var key = keysArray[i];
var value = obj[key];
if (value === Object(value)) {
recursiveFunction(value);
}
else {
if (key == 'title') {
var title = value;
}
if (key == 'extract') {
var extract = value.replace(/(\r\n|\n|\r)/gm," ");
callback(null, JSON.stringify({title: title, text: extract}));
}
}
}
}
recursiveFunction(str, callback(null, JSON.stringify({title: title, text: extract})));
};
when this code is executed we get following error
/parseData.js:29
recursiveFunction(str, callback(null, JSON.stringify({title: title, text: extract})));
^
ReferenceError: title is not defined
Okay. So you want a function that retrieves the first property named title and the first property named extract from a nested object, no matter how deeply nested these properties are.
"Extract a property value from an object" is basically is a task in its own right, we could write a function for it.
There are three cases to handle:
The argument is not an object - return undefined
The argument contains the key in question - return the associated value
Otherwise, recurse into the object and repeat steps 1 and 2 - return according result
It could look like this:
function pluck(obj, searchKey) {
var val;
if (!obj || typeof obj !== "object") return;
if (obj.hasOwnProperty(searchKey)) return obj[searchKey];
Object.keys(obj).forEach(function (key) {
if (val) return;
val = pluck(obj[key], searchKey);
});
return val;
}
Now we can call pluck() on any object and with any key and it will return to us the first value it finds anywhere in the object.
Now the rest of your task becomes very easy:
var obj = {
"name": "joel",
"title": "CTO",
"edu": {
"school": "RMB",
"college": "GNK",
"pg": "CDAC",
"extract": "This is a large text ..."
}
}
var data = {
title: pluck(obj, "title"),
text: pluck(obj, "extract")
};
This function that you 've posted above has nothing to do with async programming. I will respond in the context of the chunk of code that you 've posted. The error that you have is because you are calling the recursiveFunction(str, callback(null, JSON.stringify({title: title, text: extract}))); but the title variable is nowhere defined. I can see a definition of the title but it is in the the context of the recursiveFunction function. The variables that you define in there are not visible outside of the scope of that function and that's why you have this error.
You are trying to do something strange in this line:
recursiveFunction(str, callback(null, JSON.stringify({title: title, text: extract})));
This line will invoke the callback and will pass in the recursiveFunction the results of this function. I would expect to see something like that in this line:
recursiveFunction(str, callback);

How to obtain arguments.callee.caller?

I am trying to find out the name of the function that called my Google Apps Script function, by using arguments.callee.caller as in How do you find out the caller function in JavaScript?, but it seems there's no such property exported. (However, arguments.callee exists.)
How can I get that calling function's name in Google Apps Script?
As a secondary question, why isn't arguments.callee.caller there?
I made this function:
function getCaller()
{
var stack;
var ret = "";
try
{
throw new Error("Whoops!");
}
catch (e)
{
stack = e.stack;
}
finally
{
var matchArr = stack.match(/\(.*\)/g);
if (matchArr.length > 2)
{
tmp = matchArr[2];
ret = tmp.slice(1, tmp.length - 1) + "()";
}
return ret;
}
}
It throws as Error() and then gets the function name from the stack trace.
Try vary the '2' in matchArr[2] when using wrappers.
caller is a non-standard extension to JavaScript (that is, many browsers have it but it's not part of the EcmaScript standard) and not implemented in Apps Script.
I made a function to get the call stack based on jgrotius's answer:
function getCallStack()
{
var returnValue = "";
var framePattern = /\sat (.+?):(\d+) \((.+?)\)/;
try
{
throw new Error('');
}
catch (e)
{
returnValue = e.stack
.split('\n')
.filter(function(frame, index) {
return !frame.isBlank() && index > 0;
})
// at app/lib/debug:21 (getCaller)
.map(function(frame) {
var parts = frame.match(framePattern);
return {
file: parts[1],
line: parseInt(parts[2]),
func: parts[3]
};
});
}
return returnValue;
}
This is my updated version of the other two proposed solutions:
const getStacktrace = () => {
try {
throw new Error('')
} catch (exception) {
// example: at getStacktrace (helper:6:11)
const regex = /\sat (.+?) \((.+?):(\d+):(\d+)\)/
return exception
.stack
.split('\n')
.slice(1, -1)
.filter((frame, index) => {
return frame && index > 0
})
.map((frame) => {
const parts = frame.match(regex)
return {
function: parts[1],
file: parts[2],
line: parseInt(parts[3]),
column: parseInt(parts[4])
}
})
}
}
P.S.: please not that the regex has changed and also we are ignoring the first element of the stacktrace, since it is the getStacktrace function itself.

In as3 can an if statement run an event tracker or call a function?

I've come across some syntax I'm not familiar with and am unable to find any reference online.
In the below as3 function is the if statement calling the 'sendTrackingEvent' function? And in the 'sendTrackingEvent' function is the if statement sending the tracking event?
function trackingHandler(page: String)
{
if (! sendTrackingEvent(EID,"Document View",page))
{
fl_SavePreferences_5(EID + ",Document View," + page);
}
}
function sendTrackingEvent(category:String, action:String, label:String):Boolean
{
if (tracker.trackEvent(category,action,label))
{
return true;
}
else
{
return false;
}
}
Thanks in advance for any help. I'm still learning.
sendTrackingEvent is a function that returns true or false
the first if statement is evaluating the RESPONSE (return value) of that function. So if sendTrackingEvent(EID,"Document View",page) returns false, then your if statement will run (since the ! means NOT true).
Same for the second if statement inside of sendTrackingEvent.
It's evaluating the return value of tracker.trackEvent(category,action,label).
That function (sendTrackingEvent) could actually be simplified to this:
function sendTrackingEvent(category:String, action:String, label:String):Boolean
{
return tracker.trackEvent(category,action,label);
}
And for that matter you don't even need the sendTrackingEvent function, as you could just do this:
function trackingHandler(page: String)
{
if (! tracker.trackEvent(EID,"Document View",page))
{
fl_SavePreferences_5(EID + ",Document View," + page);
}
}

How to hide library source code in Google way?

For instance, I have a library and I would like to protect the source code to being viewed. The first method that comes to mind is to create public wrappers for private functions like the following
function executeMyCoolFunction(param1, param2, param3) {
return executeMyCoolFunction_(param1, param2, param3);
}
Only public part of the code will be visible in this way. It is fine, but all Google Service functions look like function abs() {/* */}. I am curious, is there an approach to hide library source code like Google does?
Edit 00: Do not "hide" a library code by using another library, i.e. the LibA with known project key uses the LibB with unknown project key. The public functions code of LibB is possible to get and even execute them. The code is
function exploreLib_(lib, libName) {
if (libName == null) {
for (var name in this) {
if (this[name] == lib) {
libName = name;
}
}
}
var res = [];
for (var entity in lib) {
var obj = lib[entity];
var code;
if (obj["toSource"] != null) {
code = obj.toSource();
}
else if (obj["toString"] != null) {
code = obj.toString();
}
else {
var nextLibCode = exploreLib_(obj, libName + "." + entity);
res = res.concat(nextLibCode);
}
if (code != null) {
res.push({ libraryName: libName, functionCode: code });
}
}
return res;
}
function explorerLibPublicFunctionsCode() {
var lstPublicFunctions = exploreLib_(LibA);
var password = LibA.LibB.getPassword();
}
I don't know what google does, but you could do something like this (not tested! just an idea):
function declarations:
var myApp = {
foo: function { /**/ },
bar: function { /**/ }
};
and then, in another place, an anonymous function writes foo() and bar():
(function(a) {
a['\u0066\u006F\u006F'] = function(){
// here code for foo
};
a['\u0062\u0061\u0072'] = function(){
// here code for bar
};
})(myApp);
You can pack or minify to obfuscate even more.
Edit: changed my answer to reflect the fact that an exception's stacktrace will contain the library project key.
In this example, MyLibraryB is a library included by MyLibraryA. Both are shared publicly to view (access controls) but only MyLibraryA's project key is made known. It appears it would be very difficult for an attacker to see the code in MyLibraryB:
//this function is in your MyLibraryA, and you share its project key
function executeMyCoolFunction(param1, param2, param3) {
for (var i = 0; i < 1000000; i++) {
debugger; //forces a breakpoint that the IDE cannot? step over
}
//... your code goes here
//don't share MyLibraryB project key
MyLibraryB.doSomething(args...);
}
but as per the #megabyte1024's comments, if you were to cause an exception in MyLibraryB.doSomething(), the stacktrace would contain the project key to MyLibraryB.

AS3 arguments

Why do you think the code below does not work?
What would you change/add to make it work?
Any help is appreciated..
function TraceIt(message:String, num:int)
{
trace(message, num);
}
function aa(f:Function, ...args):void
{
bb(f, args);
}
aa(TraceIt, "test", 1);
var func:Function = null;
var argum:Array = null;
function bb(f:Function, ...args):void
{
func = f;
argum = args;
exec();
}
function exec()
{
func.apply(null, argum);
}
I get an ArgumentError (Error #1063):
Argument count mismatch on test_fla::MainTimeline/TraceIt(). Expected 2, got 1.
..so, the passed parameter (argum) fails to provide all passed arguments..
..Please keep the function structure (traffic) intact.. I need a solution using the same functions in the same order.. I have to pass the args to a variable and use them in the exec() method above..
regards
Ok, here is the solution.. after breaking my head : )
function TraceIt(message:String, num:int)
{
trace(message, num);
}
function aa(f:Function=null, ...args):void
{
var newArgs:Array = args as Array;
newArgs.unshift(f);
bb.apply(null, newArgs);
}
aa(TraceIt, "test", 1);
var func:Function = null;
var argum:*;
function bb(f:Function=null, ...args):void
{
func = f;
argum = args as Array;
exec();
}
function exec():void
{
if (func == null) { return; }
func.apply(this, argum);
}
This way, you can pass arguments as variables to a different function and execute them..
Thanks to everyone taking the time to help...
When TraceIt() eventually gets called, it's being called with 1 Array parameter, not a String and int parameters.
You could change TraceIt() to:
function TraceIt(args:Array)
{
trace(args[0], args[1]);
}
Or you could change exec() to:
function exec()
{
func.apply(null, argum[0].toString().split(","));
}
...as it appears when you pass "test", 1, you end up with array whose first value is "test,1". This solution doesn't work beyond the trivial case, though.
Change your bb function to look like this:
function bb(f:Function, args:Array):void
{
func = f;
argum = args;
exec();
}
As you have it now, it accepts a variable number of arguments, but you are passing in an array(of the arguments) from aa.