Calling Method represented by string - javascript

I am being passed a string similar to below.
"Users_Controller.login"
"Users_Controller" represents the object below.
And "login" a method within it.
var Users_Controller = ( function () {
return{
login : function(vo, callback)
{......}
}
})();
Given only the string as a pointer,is it possible call the method?

You can use this function:
function getPropertyFromString(str, start) {
str = str.split(".");
var prop = start || window;
for (var i = 0; i < str.length; i++) {
if(prop == undefined)
return prop;
else
prop = prop[str[i]];
}
return prop;
}
getPropertyFromString("Users_Controller.login");
However, this does not allow bracket syntax.

Something like this?
var Users_Controller = (function () {
return {
login: function (name) {
alert("hello " + name);
},
logout: function (name) { alert("goodbye "+name); }
}
})();
var methods = {};
for (method in Users_Controller) {
methods["Users_Controller." + method] = Users_Controller[method];
}
methods["Users_Controller.login"]('john');
methods["Users_Controller.logout"]('john');

Related

Javascript Callback in for Loop

My problem is that I'm having a Function A which calls at one point another function, let's call it Function B (getChildContent) and needs the return value of Function B in order to proceed. I know that it's because of Javascripts Asynchronous Nature, and i tried to solve it with a callback. But i can't get it work properly.
FunctionA(){
//some Code.....
else {
for(i in clustertitles) {
if(S(text).contains(clustertitles[i])) {
var parent = {};
parent.ClusterName = clustertitles[i];
parent.Functions = [];
var str = '== ' + clustertitles[i] + ' ==\n* ';
str = S(text).between(str,'.').s;
var caps = parseFunctions(str);
for(y in caps) {
//var content = getChildContent(caps[y]);
getChildContent(caps[y], function(content) { //Function call
var child = {};
child.FunctionName = caps[y];
child.Content = [];
child.Content.push(content);
parent.Functions.push(child);
console.log(content);
});
}}}
}
function getChildContent (capname, callback) {
t = capname.replace(' ', '_');
bot.page(t).complete(function (title, text, date) {
var str = S(text).between('== Kurzbeschreibung ==\n* ', '.').s;
if(str === undefined || str === null || str === '') {
throw new Error('Undefined, Null or Empty!');
}
else {
var content = {};
str = parseTitles(str);
content.Owner = str[0];
content.Aim = str[1];
content.What = str[2];
content.Who = str[3];
content.Steps = str[4];
content.Page = 'some URL';
callback(content);
}
});
}
So in Function A I'm trying to call getChildContent from a for-Loop and pass the current string from caps-array. For each String in caps-array getChildContent() makes a http request over a node.js module and retrieves a string. With this string i'm building an object (content) which is needed in Function A to continue. However the 'console.log(content)' in Function A just prints out the object which is created with the last string in caps-array, but for many times. E.G. if caps-array has 5 entries, i get 5 times the object which is created with the last entry of caps-array.
How can i manage the loop/callback to get every time the right object on my console?
Your loop should call another function that preserves the value of y, something like this:
FunctionA(){
//some Code.....
else {
for(i in clustertitles) {
if(S(text).contains(clustertitles[i])) {
var parent = {};
parent.ClusterName = clustertitles[i];
parent.Functions = [];
var str = '== ' + clustertitles[i] + ' ==\n* ';
str = S(text).between(str,'.').s;
var caps = parseFunctions(str);
for(y in caps) {
yourNewFunction (y, caps, parent);
}}}
}
function yourNewFunction (y, caps, parent) {
getChildContent(caps[y], function(content) { //Function call
var child = {};
child.FunctionName = caps[y];
child.Content = [];
child.Content.push(content);
parent.Functions.push(child);
console.log(content);
});
}
function getChildContent (capname, callback) {
t = capname.replace(' ', '_');
bot.page(t).complete(function (title, text, date) {
var str = S(text).between('== Kurzbeschreibung ==\n* ', '.').s;
if(str === undefined || str === null || str === '') {
throw new Error('Undefined, Null or Empty!');
}
else {
var content = {};
str = parseTitles(str);
content.Owner = str[0];
content.Aim = str[1];
content.What = str[2];
content.Who = str[3];
content.Steps = str[4];
content.Page = 'some URL';
callback(content);
}
});
}
There are 2 ways to do so.
Put the loop inside a function, execute your callback after the loop is done. (Problematic if you are doing async call inside the loop.
function doLoopdiloopStuff() {
for() {
}
callback();
}
The other way, the way i prefer looks like this:
for(var i = 0; i < stuff || function(){ /* here's the callback */ }(), false; i++) {
/* do your loop-di-loop */
}
In another example:
for (var index = 0; index < caps.length || function(){ callbackFunction(); /* This is the callback you are calling */ return false;}(); index++) {
var element = caps[index];
// here comes the code of what you want to do with a single element
}

Regex to change a html element class with javascript not working

I have the following javascript function to open and close sub list elements on an onclick event:
function ShowHideDtls(itId) {
var subMen = document.getElementById(itId);
if (subMen != null) {
if (subMen.className == "nav nav-second-level collapse in") {
subMen.className = "nav nav-second-level collapse";
} else {
subMen.className += " in";
}
}
}
The "collapse" is a css class which makes display=none hiding the sub list and "in" is a class which makes display=block showing the sub list, creating a menu with submenus.
I found in this question Change an element's class with JavaScript in the first(accepted) answer use of a regex in order to do this. I tried it like this:
function ShowHideDtls(itId) {
var subMen = document.getElementById(itId);
if (subMen != null) {
if (subMen.className.match(/(?:^|\s)in(?!\S)/)) {
subMen.className.replace(/(?:^|\s)in(?!\S)/g, '');
} else {
subMen.className += " in";
}
}
}
The code without the regex works perfectly but with the regex it doesn't. I checked the regex in regex101.com and it seems to work there. As I understand it's more appropriate to use the regex than a long string of all the class names and also I also have a nav-third-level class that I have to close and open so the regex seems to be the convenient and proper way to do it.
What's wrong?
Thank you.
No need of regex here. You can use classList
Using classList is a convenient alternative to accessing an element's list of classes as a space-delimited string via element.className.
function ShowHideDtls(itId) {
var subMen = document.getElementById(itId);
if (subMen != null) {
subMen.classList.toggle('in');
}
}
toggle() will toggle the class of the element. If the element already has the class, it'll remove it, if not then toggle will add the class to the element.
Check the Browser Compatibility.
You can use following SHIM from MDN for IE9,
/*
* classList.js: Cross-browser full element.classList implementation.
* 2014-07-23
*
* By Eli Grey, http://eligrey.com
* Public Domain.
* NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
*/
/*global self, document, DOMException */
/*! #source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/
if ("document" in self) {
// Full polyfill for browsers with no classList support
if (!("classList" in document.createElement("_"))) {
(function (view) {
"use strict";
if (!('Element' in view)) return;
var
classListProp = "classList",
protoProp = "prototype",
elemCtrProto = view.Element[protoProp],
objCtr = Object,
strTrim = String[protoProp].trim || function () {
return this.replace(/^\s+|\s+$/g, "");
},
arrIndexOf = Array[protoProp].indexOf || function (item) {
var
i = 0,
len = this.length;
for (; i < len; i++) {
if (i in this && this[i] === item) {
return i;
}
}
return -1;
}
// Vendors: please allow content code to instantiate DOMExceptions
,
DOMEx = function (type, message) {
this.name = type;
this.code = DOMException[type];
this.message = message;
},
checkTokenAndGetIndex = function (classList, token) {
if (token === "") {
throw new DOMEx(
"SYNTAX_ERR", "An invalid or illegal string was specified"
);
}
if (/\s/.test(token)) {
throw new DOMEx(
"INVALID_CHARACTER_ERR", "String contains an invalid character"
);
}
return arrIndexOf.call(classList, token);
},
ClassList = function (elem) {
var
trimmedClasses = strTrim.call(elem.getAttribute("class") || ""),
classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [],
i = 0,
len = classes.length;
for (; i < len; i++) {
this.push(classes[i]);
}
this._updateClassName = function () {
elem.setAttribute("class", this.toString());
};
},
classListProto = ClassList[protoProp] = [],
classListGetter = function () {
return new ClassList(this);
};
// Most DOMException implementations don't allow calling DOMException's toString()
// on non-DOMExceptions. Error's toString() is sufficient here.
DOMEx[protoProp] = Error[protoProp];
classListProto.item = function (i) {
return this[i] || null;
};
classListProto.contains = function (token) {
token += "";
return checkTokenAndGetIndex(this, token) !== -1;
};
classListProto.add = function () {
var
tokens = arguments,
i = 0,
l = tokens.length,
token, updated = false;
do {
token = tokens[i] + "";
if (checkTokenAndGetIndex(this, token) === -1) {
this.push(token);
updated = true;
}
}
while (++i < l);
if (updated) {
this._updateClassName();
}
};
classListProto.remove = function () {
var
tokens = arguments,
i = 0,
l = tokens.length,
token, updated = false,
index;
do {
token = tokens[i] + "";
index = checkTokenAndGetIndex(this, token);
while (index !== -1) {
this.splice(index, 1);
updated = true;
index = checkTokenAndGetIndex(this, token);
}
}
while (++i < l);
if (updated) {
this._updateClassName();
}
};
classListProto.toggle = function (token, force) {
token += "";
var
result = this.contains(token),
method = result ?
force !== true && "remove" :
force !== false && "add";
if (method) {
this[method](token);
}
if (force === true || force === false) {
return force;
} else {
return !result;
}
};
classListProto.toString = function () {
return this.join(" ");
};
if (objCtr.defineProperty) {
var classListPropDesc = {
get: classListGetter,
enumerable: true,
configurable: true
};
try {
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
} catch (ex) { // IE 8 doesn't support enumerable:true
if (ex.number === -0x7FF5EC54) {
classListPropDesc.enumerable = false;
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
}
}
} else if (objCtr[protoProp].__defineGetter__) {
elemCtrProto.__defineGetter__(classListProp, classListGetter);
}
}(self));
} else {
// There is full or partial native classList support, so just check if we need
// to normalize the add/remove and toggle APIs.
(function () {
"use strict";
var testElement = document.createElement("_");
testElement.classList.add("c1", "c2");
// Polyfill for IE 10/11 and Firefox <26, where classList.add and
// classList.remove exist but support only one argument at a time.
if (!testElement.classList.contains("c2")) {
var createMethod = function (method) {
var original = DOMTokenList.prototype[method];
DOMTokenList.prototype[method] = function (token) {
var i, len = arguments.length;
for (i = 0; i < len; i++) {
token = arguments[i];
original.call(this, token);
}
};
};
createMethod('add');
createMethod('remove');
}
testElement.classList.toggle("c3", false);
// Polyfill for IE 10 and Firefox <24, where classList.toggle does not
// support the second argument.
if (testElement.classList.contains("c3")) {
var _toggle = DOMTokenList.prototype.toggle;
DOMTokenList.prototype.toggle = function (token, force) {
if (1 in arguments && !this.contains(token) === !force) {
return force;
} else {
return _toggle.call(this, token);
}
};
}
testElement = null;
}());
}
}
If you're using jQuery, you can use toggleClass():
function ShowHideDtls(itId) {
$('#' + itId).toggleClass('in');
}
Edit
If you still want to use regex:
if (/\bin\b/.test(subMen.className))
subMen.className.replace(/\bin\b/, '');
} else {
subMen.className += " in";
}
You can also use split() and indexOf as follow to check if a class is present on element.
var classes = className.split(/\s+/),
classIndex = classes.indexOf('in');
if (classIndex > -1) {
classes.splice(classIndex, 1);
subMen.className = classes.join(' ');
} else {
subMen.className += " in";
}
replace function returns the resultant value, it do not assign value indirectly.
So do following:
function ShowHideDtls(itId) {
var subMen = document.getElementById(itId);
if (subMen != null) {
if (subMen.className.match(/(?:^|\s)in(?!\S)/)) {
subMen.className = subMen.className.replace(/(?:^|\s)in(?!\S)/g, '');
}
else {
subMen.className += " in";
}
}
}

How can I format a resource string?

I have this snippet so far:
resetSettings: function (type, showConfirm, OnImageLoad) {
if (showConfirm) {
var msg = "";
Localization.GetBrowserLocalResource("ConfirmMsg", function (key, value) {
msg = value;
});
But I want to format it with something like this, where I pass in the type to format the string:
Localization.stringFormat(getString(Localization.GetBrowserLocalResource("ConfirmMsg", type)));
But I need to format the string with the function(key,value) in that method. How do I do that?
EDIT:
Definition of GetBrowserLocalResource:
var browserLocalResources = {};
// callback = callback(key,value)
GetBrowserLocalResource: function (key, callback) {
var val = "--no--resource";
if (browserLocalResources != null) {
if (browserLocalResources.hasOwnProperty(key)) {
val = browserLocalResources[key];
callback(key, val);
} else {
Localization.GetResourceFromServer(key, callback);
}
} else {
AjaxLog.WriteLog("error: GetBrowserLocalResource", "browserLocalResources == null");
}
return (val);
},
How would I use my stringFormat?
stringFormat is:
stringFormat: function () {
var s = arguments[0];
for (var i = 0; i < arguments.length - 1; i++) {
var reg = new RegExp("\\{" + i + "\\}", "gm");
s = s.replace(reg, arguments[i + 1]);
}
return s;
}
Something like this?:
if (showConfirm) {
var msg = "";
stringFormat(Localization.GetBrowserLocalResource("ConfirmMsg"), type, function (key, value) {
msg = value;
});
It sounds like you want to be able to call Localization.GetBrowserLocalResource with an optional second parameter. You'll have to modify that function. This should get you started on defining an optional parameter to a method:
// type is optional
// callback = callback(key,value)
GetBrowserLocalResource: function (key, type, callback) {
if (typeof callback === 'undefined' && typeof type === 'function') {
callback = type;
type = void 0;
}
// ...
},
(of course, if type isn't going to be optional, but will always be required, then just add the parameter and don't add the type checking)
Once you've made this change, then you can call the method like this:
resetSettings: function (type, showConfirm, OnImageLoad) {
if (showConfirm) {
var msg = "";
Localization.GetBrowserLocalResource("ConfirmMsg", type, function (key, value) {
msg = Localization.stringFormat(GlobalObjects.getString(value);
});

Writing a function to set some but not necessarily all parameters in another function

I had a coding interview test that asked the following question which I was not able to fully solve. I'm wondering the best way to do this following my approach -- also sorry this is long.
You are given a function to read in like this (not necessarily 2 parameters):
function add(a, b) {
return a + b;
}
The objective is to create a function to initialize some of those variables and again call the function to perform the calculation like, function setParam(func, params). To use this you would do the following:
_add = setParam(add, {b:9})
_add(10) // should return 19
My solution was to parse the function to see how many parameters there are, then set them using the given parameters but since I barely know javascript I was never able to actually return a function with only some variables set and others still undefined.
(attempt at solution)
function setParam(func, params) {
// varray is an array of the the varriables from the function, func
// ie varray = [a,b] in this test
var varray = /function[^\(]*\(([^\)]*)\)/.exec(func.toString())[1].split(',');
//creates an array, paramset, that has the variables in func defined
//where possible
// ex paramset = [a,9] if only b was set
var paramsset = []
for (i = 0; i < varray.length; i++) {
if (typeof(params[varray[i]]) == "undefined"){
paramsset[i] = varray[i];
} else {
paramsset[i] = params[varray[i]];
}
}
//////
// need to modify existing function and return with added parameters
// where I'm stuck as this doesn't work.
newfunc = (function(){
var _func = func;
return function() {
return _func.apply(this, paramsset);
}
})();
newfunc()
}
I'm sure I'm not doing this the correct way, but any help would be appreciated.
I'm certainly not advocating to go towards that solution, but I still implemented something to follow your initial's API design for fun. The signatures weak map is necessary in order to preserve the initial function's signature so that we can call setParams again on partially applied functions.
var setParams = (function () {
var signatures = new WeakMap();
return function (fn, paramsToApply) {
var signature = signatureOf(fn), newFn;
validateParams(paramsToApply, signature.params);
newFn = function () {
var params = appliedParamsFrom(arguments, paramsToApply, signature.indexes);
return fn.apply(this, params);
};
signatures.set(newFn, signature);
return newFn;
};
function signatureOf(fn) {
return signatures.has(fn)?
signatures.get(fn) :
parseSignatureOf(fn);
}
function parseSignatureOf(fn) {
return String(fn)
.match(/function.*?\((.*?)\)/)[1]
.replace(/\s+/g, '')
.split(',')
.reduce(function (r, param, index) {
r.indexes[param] = index;
r.params.push(param);
return r;
}, { indexes: {}, params: [] });
}
function validateParams(paramsToApply, actualParams) {
Object.keys(paramsToApply).forEach(function (param) {
if (actualParams.indexOf(param) == -1) throw new Error("parameter '" + param + "' could not be found in the function's signature which is: 'function (" + actualParams + ")'");
});
}
function appliedParamsFrom(args, paramsToApply, paramsIndex) {
var appliedParams = [],
usedIndexes = [],
argsIndex = 0,
argsLen = args.length,
argSpotIndex = 0;
Object.keys(paramsToApply).forEach(function (param) {
var index = paramsIndex[param];
appliedParams[index] = paramsToApply[param];
usedIndexes.push(index);
});
while (argsIndex < argsLen) {
if (usedIndexes.indexOf(argSpotIndex) == -1) {
appliedParams[argSpotIndex] = args[argsIndex++];
}
++argSpotIndex;
}
return appliedParams;
}
})();
function add(a, b) { return a + b; }
var addTo9 = setParams(add, { b: 9 });
var add10To9 = setParams(addTo9, { a: 10 });
document.write(addTo9(10) + ', ' + add10To9());
Now, note that JavaScript comes with the Function.prototype.bind function which allows to perform in-order partial function application. The first parameter to bind has nothing to do with arguments, it's to bind the this value.
function add(a, b) { return a + b; }
var addTo9 = add.bind(null, 9);
document.write(addTo9(10));
And finally, an implementation with a placholder if you need one:
var partial = (function (undefined) {
var PLACEHOLDER = {};
function partial(fn, partialArgs) {
return function () {
return fn.apply(this, applyPartialArgs(arguments, partialArgs));
};
}
Object.defineProperty(partial, 'PLACEHOLDER', {
get: function () { return PLACEHOLDER; }
});
return partial;
function applyPartialArgs(args, partialArgs) {
var appliedArgs = partialArgs.map(function (arg) {
return arg === PLACEHOLDER? undefined : arg;
}),
partialArgsLen = partialArgs.length,
argsLen = args.length,
argsIndex = 0,
argSpotIndex = 0;
while (argsIndex < argsLen) {
if (
partialArgs[argSpotIndex] === PLACEHOLDER ||
argSpotIndex >= partialArgsLen
) {
appliedArgs[argSpotIndex] = args[argsIndex++];
}
++argSpotIndex;
}
return appliedArgs;
}
})();
function add(a, b, c, d) {
return a + b + c + d;
}
var _ = partial.PLACEHOLDER;
var addTo9 = partial(add, [_, 5, _, 4]);
document.write(addTo9(5, 5));
I'm guessing that they might have been testing for knowledge of partial application. (not currying)
Edit: Edited based upon your comments. This is Crockford's curry function straight from his book.
function add(a, b) {
return a + b;
}
if (!Function.prototype.partial) {
Function.prototype.partial = function() {
var slice = Array.prototype.slice,
args = new Array(arguments.length),
that = this;
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i];
}
return function() {
return that.apply(null, args.concat(slice.apply(arguments)));
}
};
}
var example = add.partial(4);
console.log(example(10)); // output 14
console.log(example(20)); // output 24
var example = adder(4) assigns example to be function with a closure with a (in this case 4). When example is called like in the console.log, it will in effect be returning "the value of a when example was assigned, plus this new number."
Walkthrough of the partial() function:
Converts arguments to an array
returns a function gets passed the arguments given, which can be called later. It has a closure with the previously assigned arguments.

Javascript Metaprogramming

Is there a way to specify something similar to the following in javascript?
var c = {};
c.a = function() { }
c.__call__ = function (function_name, args) {
c[function_name] = function () { }; //it doesn't have to capture c... we can also have the obj passed in
return c[function_name](args);
}
c.a(); //calls c.a() directly
c.b(); //goes into c.__call__ because c.b() doesn't exist
Mozilla implements noSuchMethod but otherwise...no.
No, not really. There are some alternatives - though not as nice or convenient as your example.
For example:
function MethodManager(object) {
var methods = {};
this.defineMethod = function (methodName, func) {
methods[methodName] = func;
};
this.call = function (methodName, args, thisp) {
var method = methods[methodName] = methods[methodName] || function () {};
return methods[methodName].apply(thisp || object, args);
};
}
var obj = new MethodManager({});
obj.defineMethod('hello', function (name) { console.log("hello " + name); });
obj.call('hello', ['world']);
// "hello world"
obj.call('dne');
Almost 6 years later and there's finally a way, using Proxy:
const c = new Proxy({}, {
get (target, key) {
if (key in target) return target[key];
return function () {
console.log(`invoked ${key}() from proxy`);
};
}
});
c.a = function () {
console.log('invoked a()');
};
c.a();
c.b();
No.

Categories

Resources