Recently used the cascade methods of data.Node and discovered some odd behaviour with passing additional arguments. If additional arguments are passed, they 'call'ed rather than 'apply'ed, making for a confusing debug session. The arguments you specify are passed as an array of arguments rather than what might be expected elsewhere within the framework (fn.apply(scope,arguments)):
Ext.override(Ext.data.Node,{
/**
* Bubbles up the tree from this node, calling the specified function with each node. The scope (this) of
* function call will be the scope provided or the current node. The arguments to the function
* will be the args provided or the current node. If the function returns false at any point,
* the bubble is stopped.
* @param {Function} fn The function to call
* @param {Object} scope (optional) The scope of the function (defaults to current node)
* @param {Array} args (optional) The args to call the function with (default to passing the current node)
*/
bubble : function(fn, scope, args){
var p = this;
while(p){
// if(fn.call(scope p, args p) === false){
if(fn.apply(scope p, args [p]) === false){
break;
}
p = p.parentNode;
}
},
/**
* Cascades down the tree from this node, calling the specified function with each node. The scope (this) of
* function call will be the scope provided or the current node. The arguments to the function
* will be the args provided or the current node. If the function returns false at any point,
* the cascade is stopped on that branch.
* @param {Function} fn The function to call
* @param {Object} scope (optional) The scope of the function (defaults to current node)
* @param {Array} args (optional) The args to call the function with (default to passing the current node)
*/
cascade : function(fn, scope, args){
//if(fn.call(scope this, args this) !== false){
if(fn.apply(scope this, args [this]) !== false){
var cs = this.childNodes;
for(var i = 0, len = cs.length; i < len; i++) {
cs[i].cascade(fn, scope, args);
}
}
},
/**
* Interates the child nodes of this node, calling the specified function with each node. The scope (this) of
* function call will be the scope provided or the current node. The arguments to the function
* will be the args provided or the current node. If the function returns false at any point,
* the iteration stops.
* @param {Function} fn The function to call
* @param {Object} scope (optional) The scope of the function (defaults to current node)
* @param {Array} args (optional) The args to call the function with (default to passing the current node)
*/
eachChild : function(fn, scope, args){
var cs = this.childNodes;
for(var i = 0, len = cs.length; i < len; i++) {
//if(fn.call(scope this, args cs[i]) === false){
if(fn.apply(scope this, args [cs[i]]) === false){
break;
}
}
}
});
By design ?
Maybe createDelegate where it wants the arguments to be an Array, so even if you only want one, you have to use [ "myStringArg" ]
If the .concat() method was used, then if you just wanted a single argument, you'd just pass that argument.
This never got fixed in the 2.0 code base.
And, container bubble and cascade has the same problem.
Can someone move it over to 2.0 bugs ?
done.
here's the new thread in 2.x bugs (quoted the 1st post, linked back to this thread):
15415
Given:
* @param {Array} args (optional) The args to call the function with (default to passing the current node)
// if(fn.call(scope p, args p) === false){
// #2 if(fn.apply(scope p, args [p]) === false){
// #3 if(fn.apply(scope p, args .concat(args [p])) === false){
// #4 if(fn.apply(scope p, .concat(args [p])) === false){
RE:...Is not quite right, you correctly use apply, but wrap the passed args Array in an Array. It would work if only one extra argument was passed as an atomic value, but if an Array was passed, it would be embedded in an Array.
I don't see where #2 does any of that. args (assuming it IS an array) are passed 'as is', as required by the apply.
Assuming your gonna follow the Docs and use an array for passing the desired params, I think I'd stick with #2(or equiv #4), as your #3 has a little extra cheese (and need to add p back in there for the default current node tho ;) ).
And none of these approaches really asserts the inbound args as an array anyway ;)
I'd second that. Some Doc readers -- well ....... don't. /:)
Let's vote for #4 then ? =D>
And, regarding my previous blunder:
And none of these approaches really asserts the inbound args as an array anyway ..
#4 would also solve that problem.
@Animal: Anywhere else in framework (you can think of) where there is similar behaviour/benefits?
Well spotted, but:
bubble : function(fn, scope, args){
var p = this;
while(p){
// if(fn.call(scope p, args p) === false){
if(fn.apply(scope p, args [p]) === false){
break;
}
p = p.parentNode;
}
},
Is not quite right, you correctly use apply, but wrap the passed args Array in an Array. It would work if only one extra argument was passed as an atomic value, but if an Array was passed, it would be embedded in an Array.
It should be:
bubble : function(fn, scope, args){
var p = this;
while(p){
if(fn.apply(scope p, args .concat(args )) === false){
break;
}
p = p.parentNode;
}
},
This never got fixed in the 2.0 code base.
And, container bubble and cascade has the same problem.
Can someone move it over to 2.0 bugs ?
Ah, yes, code blindness. p has to be wrapped in an Array, you're right, while args does not!
I'd go for the .concat() technique which I like for allowing either a single scalar value or an Array.
Allergies.. no relief?!?
Religion in the workplace? |