Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: rgrove/yui3
...
head fork: rgrove/yui3
Checking mergeability… Don't worry, you can still create the pull request.
  • 18 commits
  • 18 files changed
  • 0 commit comments
  • 6 contributors
Commits on Apr 25, 2012
@ericf ericf Add WidgetButtons test case that uses Y.Nodes from another YUI instance. c32f18a
@msweeney msweeney update HISTORY for 3.5.1 2979719
@msweeney msweeney update HISTORY for 3.5.1 ca7c9bb
@msweeney msweeney make removeAttribute case-insensitive for IE 5dc6f6c
@msweeney msweeney add readonly removeAttribute test 039e53f
Commits on Apr 26, 2012
@ericf ericf WidgetButtons now deals with Y.Node instances from other YUI sandboxes.
Removes `Y.instanceOf()` check in favor of a simply sniff for the
`getDOMNode()` method on an object. The `createButton()` method has been
updated to make sure to create a node instance in the current sandbox to
avoid any weird issues.

Fixes #2532207
Closes #2532203
05ecd63
@ericf ericf Build WidgetButtons. d35e25d
@ericf ericf Update WidgetButtons change history. 305b570
@ericf ericf Merge branch 'master' of git://github.com/yui/yui3 63a3174
Satyen Desai Attempt to fix intermittent test failure with widget-buttons-test
May be the nested use(*) or that the test needed to account for
async callback. Was able to intermittently get it to fail on local
FF.
af25edf
@ghinch ghinch making modellist add() allow for an index to be passed as insertion p…
…oint
dec656a
@davglass davglass Ref #2529939 - Added tests to show this functionality dc334ee
@rgrove Merge branch 'modellist-index' of https://github.com/ConatyConsulting… 929472a
@rgrove Add arrays of models at the correct indices; support 0 as index; tests. b3da3c3
@rgrove Clarity. b94521a
@rgrove remove() can now remove models by index. bdf2377
@rgrove Update model-list docs to reflect the latest changes. 8133cc0
@rgrove Build model-list. ab9eadf
View
61 build/model-list/model-list-debug.js
@@ -230,6 +230,10 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
@param {Object} [options] Data to be mixed into the event facade of the
`add` event(s) for the added models.
+ @param {Number} [options.index] Index at which to insert the added
+ models. If not specified, the models will automatically be inserted
+ in the appropriate place according to the current sort order as
+ dictated by the `comparator()` method, if any.
@param {Boolean} [options.silent=false] If `true`, no `add` event(s)
will be fired.
@@ -239,8 +243,19 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
var isList = models._isYUIModelList;
if (isList || Lang.isArray(models)) {
- return YArray.map(isList ? models.toArray() : models, function (model) {
- return this._add(model, options);
+ return YArray.map(isList ? models.toArray() : models, function (model, index) {
+ var modelOptions = options || {};
+
+ // When an explicit insertion index is specified, ensure that
+ // the index is increased by one for each subsequent item in the
+ // array.
+ if ('index' in modelOptions) {
+ modelOptions = Y.merge(modelOptions, {
+ index: modelOptions.index + index
+ });
+ }
+
+ return this._add(model, modelOptions);
}, this);
} else {
return this._add(models, options);
@@ -408,7 +423,11 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
if (options.asList) {
list = new Y.ModelList({model: this.model});
- filtered.length && list.add(filtered, {silent: true});
+
+ if (filtered.length) {
+ list.add(filtered, {silent: true});
+ }
+
return list;
} else {
return filtered;
@@ -649,10 +668,12 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
/**
Removes the specified model or array of models from this list. You may also
pass another ModelList instance to remove all the models that are in both
- that instance and this instance.
+ that instance and this instance, or pass numerical indices to remove the
+ models at those indices.
@method remove
- @param {Model|Model[]|ModelList} models Models to remove.
+ @param {Model|Model[]|ModelList|Number|Number[]} models Models or indices of
+ models to remove.
@param {Object} [options] Data to be mixed into the event facade of the
`remove` event(s) for the removed models.
@@ -665,7 +686,18 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
var isList = models._isYUIModelList;
if (isList || Lang.isArray(models)) {
- return YArray.map(isList ? models.toArray() : models, function (model) {
+ // We can't remove multiple models by index because the indices will
+ // change as we remove them, so we need to get the actual models
+ // first.
+ models = YArray.map(isList ? models.toArray() : models, function (model) {
+ if (Lang.isNumber(model)) {
+ return this.item(model);
+ }
+
+ return model;
+ }, this);
+
+ return YArray.map(models, function (model) {
return this._remove(model, options);
}, this);
} else {
@@ -904,7 +936,7 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
}
facade = Y.merge(options, {
- index: this._findIndex(model),
+ index: 'index' in options ? options.index : this._findIndex(model),
model: model
});
@@ -1016,7 +1048,7 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
Removes the specified _model_ if it's in this list.
@method _remove
- @param {Model} model Model to remove.
+ @param {Model|Number} model Model or index of the model to remove.
@param {Object} [options] Data to be mixed into the event facade of the
`remove` event for the removed model.
@param {Boolean} [options.silent=false] If `true`, no `remove` event will
@@ -1025,14 +1057,21 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
@protected
**/
_remove: function (model, options) {
- var index = this.indexOf(model),
- facade;
+ var index, facade;
options || (options = {});
- if (index === -1) {
+ if (Lang.isNumber(model)) {
+ index = model;
+ model = this.item(index);
+ } else {
+ index = this.indexOf(model);
+ }
+
+ if (index === -1 || !model) {
this.fire(EVT_ERROR, {
error: 'Model is not in the list.',
+ index: index,
model: model,
src : 'remove'
});
View
2  build/model-list/model-list-min.js
@@ -1 +1 @@
-YUI.add("model-list",function(b){var g=b.Attribute.prototype,j=b.Lang,k=b.Array,c="add",e="create",h="error",i="load",d="remove",f="reset";function a(){a.superclass.constructor.apply(this,arguments);}b.ModelList=b.extend(a,b.Base,{model:b.Model,_isYUIModelList:true,initializer:function(m){m||(m={});var l=this.model=m.model||this.model;if(typeof l==="string"){this.model=b.Object.getValue(b,l.split("."));if(!this.model){b.error("ModelList: Model class not found: "+l);}}this.publish(c,{defaultFn:this._defAddFn});this.publish(f,{defaultFn:this._defResetFn});this.publish(d,{defaultFn:this._defRemoveFn});this.after("*:idChange",this._afterIdChange);this._clear();},destructor:function(){k.each(this._items,this._detachList,this);},add:function(n,l){var m=n._isYUIModelList;if(m||j.isArray(n)){return k.map(m?n.toArray():n,function(o){return this._add(o,l);},this);}else{return this._add(n,l);}},create:function(n,m,o){var l=this;if(typeof m==="function"){o=m;m={};}m||(m={});if(!n._isYUIModel){n=new this.model(n);}l.fire(e,b.merge(m,{model:n}));return n.save(m,function(p){if(!p){l.add(n,m);}o&&o.apply(null,arguments);});},each:function(q,p){var m=this._items.concat(),n,o,l;for(n=0,l=m.length;n<l;n++){o=m[n];q.call(p||o,o,n,this);}return this;},filter:function(o,s){var n=[],m=this._items,p,q,l,r;if(typeof o==="function"){s=o;o={};}for(p=0,l=m.length;p<l;++p){q=m[p];if(s.call(this,q,p,this)){n.push(q);}}if(o.asList){r=new b.ModelList({model:this.model});n.length&&r.add(n,{silent:true});return r;}else{return n;}},get:function(l){if(this.attrAdded(l)){return g.get.apply(this,arguments);}return this.invoke("get",l);},getAsHTML:function(l){if(this.attrAdded(l)){return b.Escape.html(g.get.apply(this,arguments));}return this.invoke("getAsHTML",l);},getAsURL:function(l){if(this.attrAdded(l)){return encodeURIComponent(g.get.apply(this,arguments));}return this.invoke("getAsURL",l);},getByClientId:function(l){return this._clientIdMap[l]||null;},getById:function(l){return this._idMap[l]||null;},invoke:function(m){var l=[this._items,m].concat(k(arguments,1,true));return k.invoke.apply(k,l);},load:function(m,n){var l=this;if(typeof m==="function"){n=m;m={};}m||(m={});this.sync("read",m,function(r,p){var q={options:m,response:p},o;if(r){q.error=r;q.src="load";l.fire(h,q);}else{if(!l._loadEvent){l._loadEvent=l.publish(i,{preventable:false});}o=q.parsed=l.parse(p);l.reset(o,m);l.fire(i,q);}n&&n.apply(null,arguments);});return this;},map:function(l,m){return k.map(this._items,l,m);},parse:function(l){if(typeof l==="string"){try{return b.JSON.parse(l)||[];}catch(m){this.fire(h,{error:m,response:l,src:"parse"});return null;}}return l||[];},remove:function(n,l){var m=n._isYUIModelList;if(m||j.isArray(n)){return k.map(m?n.toArray():n,function(o){return this._remove(o,l);},this);}else{return this._remove(n,l);}},reset:function(n,l){n||(n=[]);l||(l={});var m=b.merge({src:"reset"},l);if(n._isYUIModelList){n=n.toArray();}else{n=k.map(n,function(o){return o._isYUIModel?o:new this.model(o);},this);}m.models=n;if(l.silent){this._defResetFn(m);}else{if(this.comparator){n.sort(b.bind(this._sort,this));}this.fire(f,m);}return this;},some:function(q,p){var m=this._items.concat(),n,o,l;for(n=0,l=m.length;n<l;n++){o=m[n];if(q.call(p||o,o,n,this)){return true;}}return false;},sort:function(l){if(!this.comparator){return this;}var n=this._items.concat(),m;l||(l={});n.sort(b.bind(this._sort,this));m=b.merge(l,{models:n,src:"sort"});l.silent?this._defResetFn(m):this.fire(f,m);return this;},sync:function(){var l=k(arguments,0,true).pop();if(typeof l==="function"){l();}},toArray:function(){return this._items.concat();},toJSON:function(){return this.map(function(l){return l.toJSON();});},_add:function(m,l){var n,o;l||(l={});if(!m._isYUIModel){m=new this.model(m);}o=m.get("id");if(this._clientIdMap[m.get("clientId")]||(j.isValue(o)&&this._idMap[o])){this.fire(h,{error:"Model is already in the list.",model:m,src:"add"});return;}n=b.merge(l,{index:this._findIndex(m),model:m});l.silent?this._defAddFn(n):this.fire(c,n);return m;},_attachList:function(l){l.lists.push(this);l.addTarget(this);},_clear:function(){k.each(this._items,this._detachList,this);this._clientIdMap={};this._idMap={};this._items=[];},_compare:function(m,l){return m<l?-1:(m>l?1:0);},_detachList:function(m){var l=k.indexOf(m.lists,this);if(l>-1){m.lists.splice(l,1);m.removeTarget(this);}},_findIndex:function(o){var m=this._items,l=m.length,p=0,q,n,r;if(!this.comparator||!l){return l;}r=this.comparator(o);while(p<l){n=(p+l)>>1;q=m[n];if(this._compare(this.comparator(q),r)<0){p=n+1;}else{l=n;}}return p;},_remove:function(n,m){var l=this.indexOf(n),o;m||(m={});if(l===-1){this.fire(h,{error:"Model is not in the list.",model:n,src:"remove"});return;}o=b.merge(m,{index:l,model:n});m.silent?this._defRemoveFn(o):this.fire(d,o);return n;},_sort:function(m,l){return this._compare(this.comparator(m),this.comparator(l));},_afterIdChange:function(l){if(j.isValue(l.prevVal)){delete this._idMap[l.prevVal];}if(j.isValue(l.newVal)){this._idMap[l.newVal]=l.target;}},_defAddFn:function(m){var l=m.model,n=l.get("id");this._clientIdMap[l.get("clientId")]=l;if(j.isValue(n)){this._idMap[n]=l;}this._attachList(l);this._items.splice(m.index,0,l);},_defRemoveFn:function(m){var l=m.model,n=l.get("id");this._detachList(l);delete this._clientIdMap[l.get("clientId")];if(j.isValue(n)){delete this._idMap[n];}this._items.splice(m.index,1);},_defResetFn:function(l){if(l.src==="sort"){this._items=l.models.concat();return;}this._clear();if(l.models.length){this.add(l.models,{silent:true});}}},{NAME:"modelList"});b.augment(a,b.ArrayList);},"@VERSION@",{requires:["array-extras","array-invoke","arraylist","base-build","escape","json-parse","model"]});
+YUI.add("model-list",function(b){var g=b.Attribute.prototype,j=b.Lang,k=b.Array,c="add",e="create",h="error",i="load",d="remove",f="reset";function a(){a.superclass.constructor.apply(this,arguments);}b.ModelList=b.extend(a,b.Base,{model:b.Model,_isYUIModelList:true,initializer:function(m){m||(m={});var l=this.model=m.model||this.model;if(typeof l==="string"){this.model=b.Object.getValue(b,l.split("."));if(!this.model){b.error("ModelList: Model class not found: "+l);}}this.publish(c,{defaultFn:this._defAddFn});this.publish(f,{defaultFn:this._defResetFn});this.publish(d,{defaultFn:this._defRemoveFn});this.after("*:idChange",this._afterIdChange);this._clear();},destructor:function(){k.each(this._items,this._detachList,this);},add:function(n,l){var m=n._isYUIModelList;if(m||j.isArray(n)){return k.map(m?n.toArray():n,function(p,o){var q=l||{};if("index" in q){q=b.merge(q,{index:q.index+o});}return this._add(p,q);},this);}else{return this._add(n,l);}},create:function(n,m,o){var l=this;if(typeof m==="function"){o=m;m={};}m||(m={});if(!n._isYUIModel){n=new this.model(n);}l.fire(e,b.merge(m,{model:n}));return n.save(m,function(p){if(!p){l.add(n,m);}o&&o.apply(null,arguments);});},each:function(q,p){var m=this._items.concat(),n,o,l;for(n=0,l=m.length;n<l;n++){o=m[n];q.call(p||o,o,n,this);}return this;},filter:function(o,s){var n=[],m=this._items,p,q,l,r;if(typeof o==="function"){s=o;o={};}for(p=0,l=m.length;p<l;++p){q=m[p];if(s.call(this,q,p,this)){n.push(q);}}if(o.asList){r=new b.ModelList({model:this.model});if(n.length){r.add(n,{silent:true});}return r;}else{return n;}},get:function(l){if(this.attrAdded(l)){return g.get.apply(this,arguments);}return this.invoke("get",l);},getAsHTML:function(l){if(this.attrAdded(l)){return b.Escape.html(g.get.apply(this,arguments));}return this.invoke("getAsHTML",l);},getAsURL:function(l){if(this.attrAdded(l)){return encodeURIComponent(g.get.apply(this,arguments));}return this.invoke("getAsURL",l);},getByClientId:function(l){return this._clientIdMap[l]||null;},getById:function(l){return this._idMap[l]||null;},invoke:function(m){var l=[this._items,m].concat(k(arguments,1,true));return k.invoke.apply(k,l);},load:function(m,n){var l=this;if(typeof m==="function"){n=m;m={};}m||(m={});this.sync("read",m,function(r,p){var q={options:m,response:p},o;if(r){q.error=r;q.src="load";l.fire(h,q);}else{if(!l._loadEvent){l._loadEvent=l.publish(i,{preventable:false});}o=q.parsed=l.parse(p);l.reset(o,m);l.fire(i,q);}n&&n.apply(null,arguments);});return this;},map:function(l,m){return k.map(this._items,l,m);},parse:function(l){if(typeof l==="string"){try{return b.JSON.parse(l)||[];}catch(m){this.fire(h,{error:m,response:l,src:"parse"});return null;}}return l||[];},remove:function(n,l){var m=n._isYUIModelList;if(m||j.isArray(n)){n=k.map(m?n.toArray():n,function(o){if(j.isNumber(o)){return this.item(o);}return o;},this);return k.map(n,function(o){return this._remove(o,l);},this);}else{return this._remove(n,l);}},reset:function(n,l){n||(n=[]);l||(l={});var m=b.merge({src:"reset"},l);if(n._isYUIModelList){n=n.toArray();}else{n=k.map(n,function(o){return o._isYUIModel?o:new this.model(o);},this);}m.models=n;if(l.silent){this._defResetFn(m);}else{if(this.comparator){n.sort(b.bind(this._sort,this));}this.fire(f,m);}return this;},some:function(q,p){var m=this._items.concat(),n,o,l;for(n=0,l=m.length;n<l;n++){o=m[n];if(q.call(p||o,o,n,this)){return true;}}return false;},sort:function(l){if(!this.comparator){return this;}var n=this._items.concat(),m;l||(l={});n.sort(b.bind(this._sort,this));m=b.merge(l,{models:n,src:"sort"});l.silent?this._defResetFn(m):this.fire(f,m);return this;},sync:function(){var l=k(arguments,0,true).pop();if(typeof l==="function"){l();}},toArray:function(){return this._items.concat();},toJSON:function(){return this.map(function(l){return l.toJSON();});},_add:function(m,l){var n,o;l||(l={});if(!m._isYUIModel){m=new this.model(m);}o=m.get("id");if(this._clientIdMap[m.get("clientId")]||(j.isValue(o)&&this._idMap[o])){this.fire(h,{error:"Model is already in the list.",model:m,src:"add"});return;}n=b.merge(l,{index:"index" in l?l.index:this._findIndex(m),model:m});l.silent?this._defAddFn(n):this.fire(c,n);return m;},_attachList:function(l){l.lists.push(this);l.addTarget(this);},_clear:function(){k.each(this._items,this._detachList,this);this._clientIdMap={};this._idMap={};this._items=[];},_compare:function(m,l){return m<l?-1:(m>l?1:0);},_detachList:function(m){var l=k.indexOf(m.lists,this);if(l>-1){m.lists.splice(l,1);m.removeTarget(this);}},_findIndex:function(o){var m=this._items,l=m.length,p=0,q,n,r;if(!this.comparator||!l){return l;}r=this.comparator(o);while(p<l){n=(p+l)>>1;q=m[n];if(this._compare(this.comparator(q),r)<0){p=n+1;}else{l=n;}}return p;},_remove:function(n,m){var l,o;m||(m={});if(j.isNumber(n)){l=n;n=this.item(l);}else{l=this.indexOf(n);}if(l===-1||!n){this.fire(h,{error:"Model is not in the list.",index:l,model:n,src:"remove"});return;}o=b.merge(m,{index:l,model:n});m.silent?this._defRemoveFn(o):this.fire(d,o);return n;},_sort:function(m,l){return this._compare(this.comparator(m),this.comparator(l));},_afterIdChange:function(l){if(j.isValue(l.prevVal)){delete this._idMap[l.prevVal];}if(j.isValue(l.newVal)){this._idMap[l.newVal]=l.target;}},_defAddFn:function(m){var l=m.model,n=l.get("id");this._clientIdMap[l.get("clientId")]=l;if(j.isValue(n)){this._idMap[n]=l;}this._attachList(l);this._items.splice(m.index,0,l);},_defRemoveFn:function(m){var l=m.model,n=l.get("id");this._detachList(l);delete this._clientIdMap[l.get("clientId")];if(j.isValue(n)){delete this._idMap[n];}this._items.splice(m.index,1);},_defResetFn:function(l){if(l.src==="sort"){this._items=l.models.concat();return;}this._clear();if(l.models.length){this.add(l.models,{silent:true});}}},{NAME:"modelList"});b.augment(a,b.ArrayList);},"@VERSION@",{requires:["array-extras","array-invoke","arraylist","base-build","escape","json-parse","model"]});
View
61 build/model-list/model-list.js
@@ -230,6 +230,10 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
@param {Object} [options] Data to be mixed into the event facade of the
`add` event(s) for the added models.
+ @param {Number} [options.index] Index at which to insert the added
+ models. If not specified, the models will automatically be inserted
+ in the appropriate place according to the current sort order as
+ dictated by the `comparator()` method, if any.
@param {Boolean} [options.silent=false] If `true`, no `add` event(s)
will be fired.
@@ -239,8 +243,19 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
var isList = models._isYUIModelList;
if (isList || Lang.isArray(models)) {
- return YArray.map(isList ? models.toArray() : models, function (model) {
- return this._add(model, options);
+ return YArray.map(isList ? models.toArray() : models, function (model, index) {
+ var modelOptions = options || {};
+
+ // When an explicit insertion index is specified, ensure that
+ // the index is increased by one for each subsequent item in the
+ // array.
+ if ('index' in modelOptions) {
+ modelOptions = Y.merge(modelOptions, {
+ index: modelOptions.index + index
+ });
+ }
+
+ return this._add(model, modelOptions);
}, this);
} else {
return this._add(models, options);
@@ -408,7 +423,11 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
if (options.asList) {
list = new Y.ModelList({model: this.model});
- filtered.length && list.add(filtered, {silent: true});
+
+ if (filtered.length) {
+ list.add(filtered, {silent: true});
+ }
+
return list;
} else {
return filtered;
@@ -649,10 +668,12 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
/**
Removes the specified model or array of models from this list. You may also
pass another ModelList instance to remove all the models that are in both
- that instance and this instance.
+ that instance and this instance, or pass numerical indices to remove the
+ models at those indices.
@method remove
- @param {Model|Model[]|ModelList} models Models to remove.
+ @param {Model|Model[]|ModelList|Number|Number[]} models Models or indices of
+ models to remove.
@param {Object} [options] Data to be mixed into the event facade of the
`remove` event(s) for the removed models.
@@ -665,7 +686,18 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
var isList = models._isYUIModelList;
if (isList || Lang.isArray(models)) {
- return YArray.map(isList ? models.toArray() : models, function (model) {
+ // We can't remove multiple models by index because the indices will
+ // change as we remove them, so we need to get the actual models
+ // first.
+ models = YArray.map(isList ? models.toArray() : models, function (model) {
+ if (Lang.isNumber(model)) {
+ return this.item(model);
+ }
+
+ return model;
+ }, this);
+
+ return YArray.map(models, function (model) {
return this._remove(model, options);
}, this);
} else {
@@ -904,7 +936,7 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
}
facade = Y.merge(options, {
- index: this._findIndex(model),
+ index: 'index' in options ? options.index : this._findIndex(model),
model: model
});
@@ -1016,7 +1048,7 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
Removes the specified _model_ if it's in this list.
@method _remove
- @param {Model} model Model to remove.
+ @param {Model|Number} model Model or index of the model to remove.
@param {Object} [options] Data to be mixed into the event facade of the
`remove` event for the removed model.
@param {Boolean} [options.silent=false] If `true`, no `remove` event will
@@ -1025,14 +1057,21 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
@protected
**/
_remove: function (model, options) {
- var index = this.indexOf(model),
- facade;
+ var index, facade;
options || (options = {});
- if (index === -1) {
+ if (Lang.isNumber(model)) {
+ index = model;
+ model = this.item(index);
+ } else {
+ index = this.indexOf(model);
+ }
+
+ if (index === -1 || !model) {
this.fire(EVT_ERROR, {
error: 'Model is not in the list.',
+ index: index,
model: model,
src : 'remove'
});
View
29 build/widget-buttons/widget-buttons-debug.js
@@ -22,6 +22,12 @@ var YArray = Y.Array,
isString = YLang.isString,
isValue = YLang.isValue;
+// Utility to determine if an object is a Y.Node instance, even if it was
+// created in a different YUI sandbox.
+function isNode(node) {
+ return !!node.getDOMNode;
+}
+
/**
Provides header/body/footer button support for Widgets that use the
`WidgetStdMod` extension.
@@ -353,7 +359,7 @@ WidgetButtons.prototype = {
sectionButtons, atIndex;
// Makes sure we have the full config object.
- if (!Y.instanceOf(button, Y.Node)) {
+ if (!isNode(button)) {
button = this._mergeButtonConfig(button);
section || (section = button.section);
}
@@ -525,9 +531,10 @@ WidgetButtons.prototype = {
var config, buttonConfig, nonButtonNodeCfg,
i, len, action, context, handle;
- // Plug and return an existing Y.Node instance.
- if (Y.instanceOf(button, Y.Node)) {
- return button.plug(ButtonPlugin);
+ // Makes sure the exiting `Y.Node` instance is from this YUI sandbox and
+ // is plugged with `Y.Plugin.Button`.
+ if (isNode(button)) {
+ return Y.one(button.getDOMNode()).plug(ButtonPlugin);
}
// Merge `button` config with defaults and back-compat.
@@ -626,7 +633,7 @@ WidgetButtons.prototype = {
@since 3.5.0
**/
_getButtonDefault: function (button) {
- var isDefault = Y.instanceOf(button, Y.Node) ?
+ var isDefault = isNode(button) ?
button.getData('default') : button.isDefault;
if (isString(isDefault)) {
@@ -656,7 +663,7 @@ WidgetButtons.prototype = {
_getButtonName: function (button) {
var name;
- if (Y.instanceOf(button, Y.Node)) {
+ if (isNode(button)) {
name = button.getData('name') || button.get('name');
} else {
name = button && (button.name || button.type);
@@ -701,7 +708,7 @@ WidgetButtons.prototype = {
@method _mapButton
@param {Node} button The button node to map.
- @param {String} section The `WidgetStdMod` section.
+ @param {String} section The `WidgetStdMod` section (header/body/footer).
@protected
@since 3.5.0
**/
@@ -867,7 +874,7 @@ WidgetButtons.prototype = {
button = buttonConfigs[i];
section = currentSection;
- if (!Y.instanceOf(button, Y.Node)) {
+ if (!isNode(button)) {
button = this._mergeButtonConfig(button);
section || (section = button.section);
}
@@ -1101,11 +1108,13 @@ WidgetButtons.prototype = {
},
/**
- Removes the specified `button` to the buttons map, and nulls-out the
- `defaultButton` if it is currently the default button.
+ Removes the specified `button` from the buttons map (both name -> button and
+ section:name -> button), and nulls-out the `defaultButton` if it is
+ currently the default button.
@method _unMapButton
@param {Node} button The button node to remove from the buttons map.
+ @param {String} section The `WidgetStdMod` section (header/body/footer).
@protected
@since 3.5.0
**/
View
4 build/widget-buttons/widget-buttons-min.js
@@ -1,2 +1,2 @@
-YUI.add("widget-buttons",function(c){var i=c.Array,k=c.Lang,f=c.Object,a=c.Plugin.Button,j=c.Widget,m=c.WidgetStdMod,d=c.ClassNameManager.getClassName,e=k.isArray,l=k.isNumber,b=k.isString,h=k.isValue;function g(){if(!this._stdModNode){c.error("WidgetStdMod must be added to a Widget before WidgetButtons.");}this._buttonsHandles={};}g.ATTRS={buttons:{getter:"_getButtons",setter:"_setButtons",value:{}},defaultButton:{readOnly:true,value:null}};g.CLASS_NAMES={button:d("button"),buttons:j.getClassName("buttons"),primary:d("button","primary")};g.HTML_PARSER={buttons:function(n){return this._parseButtons(n);}};g.NON_BUTTON_NODE_CFG=["action","classNames","context","events","isDefault","section"];g.prototype={BUTTONS:{},BUTTONS_TEMPLATE:"<span />",DEFAULT_BUTTONS_SECTION:m.FOOTER,initializer:function(){this._mapButtons(this.get("buttons"));this._updateDefaultButton();this.after("buttonsChange",c.bind("_afterButtonsChange",this));c.after(this._bindUIButtons,this,"bindUI");c.after(this._syncUIButtons,this,"syncUI");},destructor:function(){f.each(this._buttonsHandles,function(n){n.detach();});delete this._buttonsHandles;delete this._buttonsMap;delete this._defaultButton;},addButton:function(p,s,n){var r=this.get("buttons"),q,o;if(!c.instanceOf(p,c.Node)){p=this._mergeButtonConfig(p);s||(s=p.section);}s||(s=this.DEFAULT_BUTTONS_SECTION);q=r[s]||(r[s]=[]);l(n)||(n=q.length);q.splice(n,0,p);o=i.indexOf(q,p);this.set("buttons",r,{button:p,section:s,index:o,src:"add"});return this;},getButton:function(n,q){if(!h(n)){return;}var p=this._buttonsMap,o;q||(q=this.DEFAULT_BUTTONS_SECTION);if(l(n)){o=this.get("buttons");return o[q]&&o[q][n];}return arguments.length>1?p[q+":"+n]:p[n];},removeButton:function(o,q){if(!h(o)){return this;}var p=this.get("buttons"),n;if(l(o)){q||(q=this.DEFAULT_BUTTONS_SECTION);n=o;o=p[q][n];}else{if(b(o)){o=this.getButton.apply(this,arguments);}f.some(p,function(r,s){n=i.indexOf(r,o);if(n>-1){q=s;return true;}});}if(o&&n>-1){p[q].splice(n,1);this.set("buttons",p,{button:o,section:q,index:n,src:"remove"});}return this;},_bindUIButtons:function(){var n=c.bind("_afterContentChangeButtons",this);this.after({defaultButtonChange:c.bind("_afterDefaultButtonChange",this),visibleChange:c.bind("_afterVisibleChangeButtons",this),headerContentChange:n,bodyContentChange:n,footerContentChange:n});},_createButton:function(s){var p,o,v,r,u,q,n,t;if(c.instanceOf(s,c.Node)){return s.plug(a);}p=c.merge({context:this,events:"click",label:s.value},s);o=c.merge(p);v=g.NON_BUTTON_NODE_CFG;for(r=0,u=v.length;r<u;r+=1){delete o[v[r]];}s=a.createNode(o);n=p.context;q=p.action;if(b(q)){q=c.bind(q,n);}t=s.on(p.events,q,n);this._buttonsHandles[c.stamp(s,true)]=t;s.setData("name",this._getButtonName(p));s.setData("default",this._getButtonDefault(p));i.each(i(p.classNames),s.addClass,s);return s;},_getButtonContainer:function(s,q){var t=m.SECTION_CLASS_NAMES[s],r=g.CLASS_NAMES.buttons,p=this.get("contentBox"),n,o;n="."+t+" ."+r;o=p.one(n);if(!o&&q){o=c.Node.create(this.BUTTONS_TEMPLATE);o.addClass(r);}return o;},_getButtonDefault:function(n){var o=c.instanceOf(n,c.Node)?n.getData("default"):n.isDefault;if(b(o)){return o.toLowerCase()==="true";}return !!o;},_getButtonName:function(o){var n;if(c.instanceOf(o,c.Node)){n=o.getData("name")||o.get("name");}else{n=o&&(o.name||o.type);}return n;},_getButtons:function(o){var n={};f.each(o,function(p,q){n[q]=p.concat();});return n;},_mapButton:function(o,r){var q=this._buttonsMap,n=this._getButtonName(o),p=this._getButtonDefault(o);if(n){q[n]=o;q[r+":"+n]=o;}p&&(this._defaultButton=o);},_mapButtons:function(n){this._buttonsMap={};this._defaultButton=null;f.each(n,function(q,r){var p,o;for(p=0,o=q.length;p<o;p+=1){this._mapButton(q[p],r);}},this);},_mergeButtonConfig:function(q){var n,t,p,s,r,o;q=b(q)?{name:q}:c.merge(q);if(q.srcNode){s=q.srcNode;r=s.get("tagName").toLowerCase();o=s.get(r==="input"?"value":"text");n={disabled:!!s.get("disabled"),isDefault:this._getButtonDefault(s),name:this._getButtonName(s)};o&&(n.label=o);c.mix(q,n,false,null,0,true);}p=this._getButtonName(q);t=this.BUTTONS&&this.BUTTONS[p];if(t){c.mix(q,t,false,null,0,true);}return q;},_parseButtons:function(p){var n="."+g.CLASS_NAMES.button,q=["header","body","footer"],o=null;i.each(q,function(u){var r=this._getButtonContainer(u),t=r&&r.all(n),s;if(!t||t.isEmpty()){return;}s=[];t.each(function(v){s.push({srcNode:v});});o||(o={});o[u]=s;},this);return o;},_setButtons:function(p){var o=this.DEFAULT_BUTTONS_SECTION,q={};function n(u,w){if(!e(u)){return;}var t,r,s,v;for(t=0,r=u.length;t<r;t+=1){s=u[t];v=w;if(!c.instanceOf(s,c.Node)){s=this._mergeButtonConfig(s);v||(v=s.section);}s=this._createButton(s);v||(v=o);(q[v]||(q[v]=[])).push(s);}}if(e(p)){n.call(this,p);}else{f.each(p,n,this);}return q;},_syncUIButtons:function(){this._uiSetButtons(this.get("buttons"));this._uiSetDefaultButton(this.get("defaultButton"));this._uiSetVisibleButtons(this.get("visible"));},_uiInsertButton:function(p,s,o){var r=g.CLASS_NAMES.button,n=this._getButtonContainer(s,true),q=n.all("."+r);n.insertBefore(p,q.item(o));this.setStdModContent(s,n,"after");},_uiRemoveButton:function(r,u,o){var q=c.stamp(r,this),p=this._buttonsHandles,t=p[q],n,s;t&&t.detach();delete p[q];r.remove();o||(o={});if(!o.preserveContent){n=this._getButtonContainer(u);s=g.CLASS_NAMES.button;if(n&&n.all("."+s).isEmpty()){n.remove();this._updateContentButtons(u);}}},_uiSetButtons:function(n){var o=g.CLASS_NAMES.button,p=["header","body","footer"];i.each(p,function(x){var v=n[x]||[],s=v.length,y=this._getButtonContainer(x,s),w=false,r,t,u,q;if(!y){return;}r=y.all("."+o);for(t=0;t<s;t+=1){u=v[t];q=r?r.indexOf(u):-1;if(q>-1){r.splice(q,1);if(q!==t){y.insertBefore(u,t+1);w=true;}}else{y.appendChild(u);w=true;}}r.each(function(z){this._uiRemoveButton(z,x,{preserveContent:true});w=true;},this);if(s===0){y.remove();this._updateContentButtons(x);return;}if(w){this.setStdModContent(x,y,"after");}},this);},_uiSetDefaultButton:function(p,o){var n=g.CLASS_NAMES.primary;p&&p.addClass(n);
-o&&o.removeClass(n);},_uiSetVisibleButtons:function(o){if(!o){return;}var n=this.get("defaultButton");if(n){n.focus();}},_unMapButton:function(p,r){var q=this._buttonsMap,o=this._getButtonName(p),n;if(o){if(q[o]===p){delete q[o];}n=r+":"+o;if(q[n]===p){delete q[n];}}if(this._defaultButton===p){this._defaultButton=null;}},_updateDefaultButton:function(){var n=this._defaultButton;if(this.get("defaultButton")!==n){this._set("defaultButton",n);}},_updateContentButtons:function(o){var n=this.getStdModNode(o).get("childNodes");this.set(o+"Content",n.isEmpty()?null:n,{src:"buttons"});},_afterButtonsChange:function(r){var p=r.newVal,q=r.section,n=r.index,s=r.src,o;if(s==="add"){o=p[q][n];this._mapButton(o,q);this._updateDefaultButton();this._uiInsertButton(o,q,n);return;}if(s==="remove"){o=r.button;this._unMapButton(o,q);this._updateDefaultButton();this._uiRemoveButton(o,q);return;}this._mapButtons(p);this._updateDefaultButton();this._uiSetButtons(p);},_afterContentChangeButtons:function(o){var p=o.src,q=o.stdModPosition,n=!q||q===m.REPLACE;if(n&&p!=="buttons"&&p!==j.UI_SRC){this._uiSetButtons(this.get("buttons"));}},_afterDefaultButtonChange:function(n){this._uiSetDefaultButton(n.newVal,n.prevVal);},_afterVisibleChangeButtons:function(n){this._uiSetVisibleButtons(n.newVal);}};c.WidgetButtons=g;},"@VERSION@",{requires:["button-plugin","cssbutton","widget-stdmod"]});
+YUI.add("widget-buttons",function(c){var i=c.Array,k=c.Lang,f=c.Object,a=c.Plugin.Button,j=c.Widget,n=c.WidgetStdMod,d=c.ClassNameManager.getClassName,e=k.isArray,m=k.isNumber,b=k.isString,h=k.isValue;function l(o){return !!o.getDOMNode;}function g(){if(!this._stdModNode){c.error("WidgetStdMod must be added to a Widget before WidgetButtons.");}this._buttonsHandles={};}g.ATTRS={buttons:{getter:"_getButtons",setter:"_setButtons",value:{}},defaultButton:{readOnly:true,value:null}};g.CLASS_NAMES={button:d("button"),buttons:j.getClassName("buttons"),primary:d("button","primary")};g.HTML_PARSER={buttons:function(o){return this._parseButtons(o);}};g.NON_BUTTON_NODE_CFG=["action","classNames","context","events","isDefault","section"];g.prototype={BUTTONS:{},BUTTONS_TEMPLATE:"<span />",DEFAULT_BUTTONS_SECTION:n.FOOTER,initializer:function(){this._mapButtons(this.get("buttons"));this._updateDefaultButton();this.after("buttonsChange",c.bind("_afterButtonsChange",this));c.after(this._bindUIButtons,this,"bindUI");c.after(this._syncUIButtons,this,"syncUI");},destructor:function(){f.each(this._buttonsHandles,function(o){o.detach();});delete this._buttonsHandles;delete this._buttonsMap;delete this._defaultButton;},addButton:function(q,t,o){var s=this.get("buttons"),r,p;if(!l(q)){q=this._mergeButtonConfig(q);t||(t=q.section);}t||(t=this.DEFAULT_BUTTONS_SECTION);r=s[t]||(s[t]=[]);m(o)||(o=r.length);r.splice(o,0,q);p=i.indexOf(r,q);this.set("buttons",s,{button:q,section:t,index:p,src:"add"});return this;},getButton:function(o,r){if(!h(o)){return;}var q=this._buttonsMap,p;r||(r=this.DEFAULT_BUTTONS_SECTION);if(m(o)){p=this.get("buttons");return p[r]&&p[r][o];}return arguments.length>1?q[r+":"+o]:q[o];},removeButton:function(p,r){if(!h(p)){return this;}var q=this.get("buttons"),o;if(m(p)){r||(r=this.DEFAULT_BUTTONS_SECTION);o=p;p=q[r][o];}else{if(b(p)){p=this.getButton.apply(this,arguments);}f.some(q,function(s,t){o=i.indexOf(s,p);if(o>-1){r=t;return true;}});}if(p&&o>-1){q[r].splice(o,1);this.set("buttons",q,{button:p,section:r,index:o,src:"remove"});}return this;},_bindUIButtons:function(){var o=c.bind("_afterContentChangeButtons",this);this.after({defaultButtonChange:c.bind("_afterDefaultButtonChange",this),visibleChange:c.bind("_afterVisibleChangeButtons",this),headerContentChange:o,bodyContentChange:o,footerContentChange:o});},_createButton:function(t){var q,p,w,s,v,r,o,u;if(l(t)){return c.one(t.getDOMNode()).plug(a);}q=c.merge({context:this,events:"click",label:t.value},t);p=c.merge(q);w=g.NON_BUTTON_NODE_CFG;for(s=0,v=w.length;s<v;s+=1){delete p[w[s]];}t=a.createNode(p);o=q.context;r=q.action;if(b(r)){r=c.bind(r,o);}u=t.on(q.events,r,o);this._buttonsHandles[c.stamp(t,true)]=u;t.setData("name",this._getButtonName(q));t.setData("default",this._getButtonDefault(q));i.each(i(q.classNames),t.addClass,t);return t;},_getButtonContainer:function(t,r){var u=n.SECTION_CLASS_NAMES[t],s=g.CLASS_NAMES.buttons,q=this.get("contentBox"),o,p;o="."+u+" ."+s;p=q.one(o);if(!p&&r){p=c.Node.create(this.BUTTONS_TEMPLATE);p.addClass(s);}return p;},_getButtonDefault:function(o){var p=l(o)?o.getData("default"):o.isDefault;if(b(p)){return p.toLowerCase()==="true";}return !!p;},_getButtonName:function(p){var o;if(l(p)){o=p.getData("name")||p.get("name");}else{o=p&&(p.name||p.type);}return o;},_getButtons:function(p){var o={};f.each(p,function(q,r){o[r]=q.concat();});return o;},_mapButton:function(p,s){var r=this._buttonsMap,o=this._getButtonName(p),q=this._getButtonDefault(p);if(o){r[o]=p;r[s+":"+o]=p;}q&&(this._defaultButton=p);},_mapButtons:function(o){this._buttonsMap={};this._defaultButton=null;f.each(o,function(r,s){var q,p;for(q=0,p=r.length;q<p;q+=1){this._mapButton(r[q],s);}},this);},_mergeButtonConfig:function(r){var o,u,q,t,s,p;r=b(r)?{name:r}:c.merge(r);if(r.srcNode){t=r.srcNode;s=t.get("tagName").toLowerCase();p=t.get(s==="input"?"value":"text");o={disabled:!!t.get("disabled"),isDefault:this._getButtonDefault(t),name:this._getButtonName(t)};p&&(o.label=p);c.mix(r,o,false,null,0,true);}q=this._getButtonName(r);u=this.BUTTONS&&this.BUTTONS[q];if(u){c.mix(r,u,false,null,0,true);}return r;},_parseButtons:function(q){var o="."+g.CLASS_NAMES.button,r=["header","body","footer"],p=null;i.each(r,function(v){var s=this._getButtonContainer(v),u=s&&s.all(o),t;if(!u||u.isEmpty()){return;}t=[];u.each(function(w){t.push({srcNode:w});});p||(p={});p[v]=t;},this);return p;},_setButtons:function(q){var p=this.DEFAULT_BUTTONS_SECTION,r={};function o(v,x){if(!e(v)){return;}var u,s,t,w;for(u=0,s=v.length;u<s;u+=1){t=v[u];w=x;if(!l(t)){t=this._mergeButtonConfig(t);w||(w=t.section);}t=this._createButton(t);w||(w=p);(r[w]||(r[w]=[])).push(t);}}if(e(q)){o.call(this,q);}else{f.each(q,o,this);}return r;},_syncUIButtons:function(){this._uiSetButtons(this.get("buttons"));this._uiSetDefaultButton(this.get("defaultButton"));this._uiSetVisibleButtons(this.get("visible"));},_uiInsertButton:function(q,t,p){var s=g.CLASS_NAMES.button,o=this._getButtonContainer(t,true),r=o.all("."+s);o.insertBefore(q,r.item(p));this.setStdModContent(t,o,"after");},_uiRemoveButton:function(s,v,p){var r=c.stamp(s,this),q=this._buttonsHandles,u=q[r],o,t;u&&u.detach();delete q[r];s.remove();p||(p={});if(!p.preserveContent){o=this._getButtonContainer(v);t=g.CLASS_NAMES.button;if(o&&o.all("."+t).isEmpty()){o.remove();this._updateContentButtons(v);}}},_uiSetButtons:function(o){var p=g.CLASS_NAMES.button,q=["header","body","footer"];i.each(q,function(y){var w=o[y]||[],t=w.length,z=this._getButtonContainer(y,t),x=false,s,u,v,r;if(!z){return;}s=z.all("."+p);for(u=0;u<t;u+=1){v=w[u];r=s?s.indexOf(v):-1;if(r>-1){s.splice(r,1);if(r!==u){z.insertBefore(v,u+1);x=true;}}else{z.appendChild(v);x=true;}}s.each(function(A){this._uiRemoveButton(A,y,{preserveContent:true});x=true;},this);if(t===0){z.remove();this._updateContentButtons(y);return;}if(x){this.setStdModContent(y,z,"after");}},this);},_uiSetDefaultButton:function(q,p){var o=g.CLASS_NAMES.primary;q&&q.addClass(o);p&&p.removeClass(o);
+},_uiSetVisibleButtons:function(p){if(!p){return;}var o=this.get("defaultButton");if(o){o.focus();}},_unMapButton:function(q,s){var r=this._buttonsMap,p=this._getButtonName(q),o;if(p){if(r[p]===q){delete r[p];}o=s+":"+p;if(r[o]===q){delete r[o];}}if(this._defaultButton===q){this._defaultButton=null;}},_updateDefaultButton:function(){var o=this._defaultButton;if(this.get("defaultButton")!==o){this._set("defaultButton",o);}},_updateContentButtons:function(p){var o=this.getStdModNode(p).get("childNodes");this.set(p+"Content",o.isEmpty()?null:o,{src:"buttons"});},_afterButtonsChange:function(s){var q=s.newVal,r=s.section,o=s.index,t=s.src,p;if(t==="add"){p=q[r][o];this._mapButton(p,r);this._updateDefaultButton();this._uiInsertButton(p,r,o);return;}if(t==="remove"){p=s.button;this._unMapButton(p,r);this._updateDefaultButton();this._uiRemoveButton(p,r);return;}this._mapButtons(q);this._updateDefaultButton();this._uiSetButtons(q);},_afterContentChangeButtons:function(p){var q=p.src,r=p.stdModPosition,o=!r||r===n.REPLACE;if(o&&q!=="buttons"&&q!==j.UI_SRC){this._uiSetButtons(this.get("buttons"));}},_afterDefaultButtonChange:function(o){this._uiSetDefaultButton(o.newVal,o.prevVal);},_afterVisibleChangeButtons:function(o){this._uiSetVisibleButtons(o.newVal);}};c.WidgetButtons=g;},"@VERSION@",{requires:["button-plugin","cssbutton","widget-stdmod"]});
View
29 build/widget-buttons/widget-buttons.js
@@ -22,6 +22,12 @@ var YArray = Y.Array,
isString = YLang.isString,
isValue = YLang.isValue;
+// Utility to determine if an object is a Y.Node instance, even if it was
+// created in a different YUI sandbox.
+function isNode(node) {
+ return !!node.getDOMNode;
+}
+
/**
Provides header/body/footer button support for Widgets that use the
`WidgetStdMod` extension.
@@ -353,7 +359,7 @@ WidgetButtons.prototype = {
sectionButtons, atIndex;
// Makes sure we have the full config object.
- if (!Y.instanceOf(button, Y.Node)) {
+ if (!isNode(button)) {
button = this._mergeButtonConfig(button);
section || (section = button.section);
}
@@ -525,9 +531,10 @@ WidgetButtons.prototype = {
var config, buttonConfig, nonButtonNodeCfg,
i, len, action, context, handle;
- // Plug and return an existing Y.Node instance.
- if (Y.instanceOf(button, Y.Node)) {
- return button.plug(ButtonPlugin);
+ // Makes sure the exiting `Y.Node` instance is from this YUI sandbox and
+ // is plugged with `Y.Plugin.Button`.
+ if (isNode(button)) {
+ return Y.one(button.getDOMNode()).plug(ButtonPlugin);
}
// Merge `button` config with defaults and back-compat.
@@ -626,7 +633,7 @@ WidgetButtons.prototype = {
@since 3.5.0
**/
_getButtonDefault: function (button) {
- var isDefault = Y.instanceOf(button, Y.Node) ?
+ var isDefault = isNode(button) ?
button.getData('default') : button.isDefault;
if (isString(isDefault)) {
@@ -656,7 +663,7 @@ WidgetButtons.prototype = {
_getButtonName: function (button) {
var name;
- if (Y.instanceOf(button, Y.Node)) {
+ if (isNode(button)) {
name = button.getData('name') || button.get('name');
} else {
name = button && (button.name || button.type);
@@ -701,7 +708,7 @@ WidgetButtons.prototype = {
@method _mapButton
@param {Node} button The button node to map.
- @param {String} section The `WidgetStdMod` section.
+ @param {String} section The `WidgetStdMod` section (header/body/footer).
@protected
@since 3.5.0
**/
@@ -867,7 +874,7 @@ WidgetButtons.prototype = {
button = buttonConfigs[i];
section = currentSection;
- if (!Y.instanceOf(button, Y.Node)) {
+ if (!isNode(button)) {
button = this._mergeButtonConfig(button);
section || (section = button.section);
}
@@ -1101,11 +1108,13 @@ WidgetButtons.prototype = {
},
/**
- Removes the specified `button` to the buttons map, and nulls-out the
- `defaultButton` if it is currently the default button.
+ Removes the specified `button` from the buttons map (both name -> button and
+ section:name -> button), and nulls-out the `defaultButton` if it is
+ currently the default button.
@method _unMapButton
@param {Node} button The button node to remove from the buttons map.
+ @param {String} section The `WidgetStdMod` section (header/body/footer).
@protected
@since 3.5.0
**/
View
7 src/app/HISTORY.md
@@ -6,9 +6,16 @@ App Framework Change History
### ModelList
+* The `add()` method now accepts an `index` option, which can be used to insert
+ the specified model(s) at a specific index in the list. [Greg Hinch]
+
* The `each()` and `some()` methods now iterate over a copy of the list, so it's
safe to remove a model during iteration. [Ticket #2531910]
+* The `remove()` method now optionally accepts the index of a model to remove
+ (or an array of indices). You no longer need to specify the actual model
+ instance(s), although that's still supported as well.
+
### Router
* The `req` object passed to routes now has a `pendingRoutes` property that
View
33 src/app/docs/model-list/index.mustache
@@ -159,6 +159,19 @@ pies.add(cheesecakes);
```
<p>
+Models are automatically inserted into the list at the correct index based on the current sort comparator, so the list is always guaranteed to be sorted. By default, no sort comparator is defined, so models are sorted in insertion order. See [[#Creating a Custom Sort Comparator]] for details on customizing how a list is sorted.
+</p>
+
+<p>
+To add one or more models at a specific index in the list regardless of the current sort order, specify a value for the `index` option:
+</p>
+
+```
+// Add a pie at index 2, regardless of the current sort order.
+pies.add({type: 'lemon meringue'}, {index: 2});
+```
+
+<p>
The `create()` method accepts an optional callback function, which will be executed when the save operation finishes. Provide a callback if you'd like to be notified of the success or failure of the save operation.
</p>
@@ -195,10 +208,6 @@ You can also call `reset()` with no arguments to quickly empty the list.
pies.reset();
```
-<p>
-Models are automatically inserted into the list at the correct index based on the current sort comparator, so the list is always guaranteed to be sorted. By default, no sort comparator is defined, so models are sorted in insertion order. See [[#Creating a Custom Sort Comparator]] for details on customizing how a list is sorted.
-</p>
-
<h4>Retrieving Models</h4>
<p>
@@ -248,14 +257,20 @@ var applePiesList = pies.filter({asList: true}, function (pie) {
<h4>Removing Models</h4>
<p>
-Pass a model or array of models to the <a href="{{apiDocs}}/classes/ModelList.html#method_remove">`remove()`</a> method to remove them from the list.
+Pass a model index, array of model indices, model instance, or array of model instances to the <a href="{{apiDocs}}/classes/ModelList.html#method_remove">`remove()`</a> method to remove them from the list.
</p>
```
-// Remove a single model from the list.
+// Remove the model at index 1 from the list.
+pies.remove(1);
+
+// Remove multiple models from the list by index.
+pies.remove([0, 3, 4]);
+
+// Remove a specific model instance from the list, regardless of its index.
pies.remove(pies.getById('1234'));
-// Remove multiple models from the list.
+// Remove multiple model instances from the list.
pies.remove([
pies.getById('1235'),
pies.getById('1236')
@@ -263,11 +278,11 @@ pies.remove([
```
<p>
-This will only remove the specified model instances from the list; it won't actually call the models' <a href="{{apiDocs}}/classes/Model.html#method_destroy">`destroy()`</a> methods or delete them via the models' sync layer. Calling a model's `destroy()` method will automatically remove it from any lists it's in, so that would be a better option if you want to both remove and destroy or delete a model.
+This will only remove the specified models from the list; it won't actually call the models' <a href="{{apiDocs}}/classes/Model.html#method_destroy">`destroy()`</a> methods or delete them via the models' sync layer. Calling a model's `destroy()` method will automatically remove it from any lists it's in, so that would be a better option if you want to both remove and destroy or delete a model.
</p>
<p>
-You can pass another ModelList instance to `remove()` to remove all the models that exist in that list from this list (note that you may get `error` events if some of the models in the other list don't exist in the list you're trying to remove them from).
+You can also pass another ModelList instance to `remove()` to remove all the models that exist in that list from this list (note that you may get `error` events if some of the models in the other list don't exist in the list you're trying to remove them from).
</p>
<h3>List Attributes</h3>
View
61 src/app/js/model-list.js
@@ -228,6 +228,10 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
@param {Object} [options] Data to be mixed into the event facade of the
`add` event(s) for the added models.
+ @param {Number} [options.index] Index at which to insert the added
+ models. If not specified, the models will automatically be inserted
+ in the appropriate place according to the current sort order as
+ dictated by the `comparator()` method, if any.
@param {Boolean} [options.silent=false] If `true`, no `add` event(s)
will be fired.
@@ -237,8 +241,19 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
var isList = models._isYUIModelList;
if (isList || Lang.isArray(models)) {
- return YArray.map(isList ? models.toArray() : models, function (model) {
- return this._add(model, options);
+ return YArray.map(isList ? models.toArray() : models, function (model, index) {
+ var modelOptions = options || {};
+
+ // When an explicit insertion index is specified, ensure that
+ // the index is increased by one for each subsequent item in the
+ // array.
+ if ('index' in modelOptions) {
+ modelOptions = Y.merge(modelOptions, {
+ index: modelOptions.index + index
+ });
+ }
+
+ return this._add(model, modelOptions);
}, this);
} else {
return this._add(models, options);
@@ -406,7 +421,11 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
if (options.asList) {
list = new Y.ModelList({model: this.model});
- filtered.length && list.add(filtered, {silent: true});
+
+ if (filtered.length) {
+ list.add(filtered, {silent: true});
+ }
+
return list;
} else {
return filtered;
@@ -647,10 +666,12 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
/**
Removes the specified model or array of models from this list. You may also
pass another ModelList instance to remove all the models that are in both
- that instance and this instance.
+ that instance and this instance, or pass numerical indices to remove the
+ models at those indices.
@method remove
- @param {Model|Model[]|ModelList} models Models to remove.
+ @param {Model|Model[]|ModelList|Number|Number[]} models Models or indices of
+ models to remove.
@param {Object} [options] Data to be mixed into the event facade of the
`remove` event(s) for the removed models.
@@ -663,7 +684,18 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
var isList = models._isYUIModelList;
if (isList || Lang.isArray(models)) {
- return YArray.map(isList ? models.toArray() : models, function (model) {
+ // We can't remove multiple models by index because the indices will
+ // change as we remove them, so we need to get the actual models
+ // first.
+ models = YArray.map(isList ? models.toArray() : models, function (model) {
+ if (Lang.isNumber(model)) {
+ return this.item(model);
+ }
+
+ return model;
+ }, this);
+
+ return YArray.map(models, function (model) {
return this._remove(model, options);
}, this);
} else {
@@ -902,7 +934,7 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
}
facade = Y.merge(options, {
- index: this._findIndex(model),
+ index: 'index' in options ? options.index : this._findIndex(model),
model: model
});
@@ -1014,7 +1046,7 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
Removes the specified _model_ if it's in this list.
@method _remove
- @param {Model} model Model to remove.
+ @param {Model|Number} model Model or index of the model to remove.
@param {Object} [options] Data to be mixed into the event facade of the
`remove` event for the removed model.
@param {Boolean} [options.silent=false] If `true`, no `remove` event will
@@ -1023,14 +1055,21 @@ Y.ModelList = Y.extend(ModelList, Y.Base, {
@protected
**/
_remove: function (model, options) {
- var index = this.indexOf(model),
- facade;
+ var index, facade;
options || (options = {});
- if (index === -1) {
+ if (Lang.isNumber(model)) {
+ index = model;
+ model = this.item(index);
+ } else {
+ index = this.indexOf(model);
+ }
+
+ if (index === -1 || !model) {
this.fire(EVT_ERROR, {
error: 'Model is not in the list.',
+ index: index,
model: model,
src : 'remove'
});
View
67 src/app/tests/model-list-test.js
@@ -135,6 +135,17 @@ modelListSuite.add(new Y.Test.Case({
Assert.areSame('foo', added.get('foo'));
},
+ 'add() should add a model to the list at the specified index': function () {
+ var list = this.createList(),
+ model = this.createModel();
+
+ list.add([{name: 'first'}, {name: 'second'}, {name: 'third'}]);
+ list.add(model, {index: 0});
+
+ Assert.areSame(4, list.size(), 'list should contain 4 items');
+ Assert.areSame(model, list.item(0), 'model should be inserted at index 0');
+ },
+
'add() should add an array of models to the list': function () {
var list = this.createList(),
models = [this.createModel(), this.createModel()],
@@ -151,6 +162,21 @@ modelListSuite.add(new Y.Test.Case({
Assert.areSame('bar', added[1].get('bar'));
},
+ 'add() should add an array of models to the list at the specified index': function () {
+ var list = this.createList(),
+ modelOne = this.createModel(),
+ modelTwo = this.createModel(),
+ modelThree = this.createModel();
+
+ list.add([{name: 'first'}, {name: 'second'}, {name: 'third'}]);
+ list.add([modelOne, modelTwo, modelThree], {index: 0});
+
+ Assert.areSame(6, list.size(), 'list should contain 6 items');
+ Assert.areSame(modelOne, list.item(0), 'modelOne should be inserted at index 0');
+ Assert.areSame(modelTwo, list.item(1), 'modelTwo should be inserted at index 1');
+ Assert.areSame(modelThree, list.item(2), 'modelThree should be inserted at index 2');
+ },
+
'add() should add models in another ModelList to the list': function () {
var list = this.createList(),
otherList = this.createList(),
@@ -164,6 +190,24 @@ modelListSuite.add(new Y.Test.Case({
Assert.areSame(otherList.item(1), list.item(1));
},
+ 'add() should add models in another ModelList to the list at the specified index': function () {
+ var list = this.createList(),
+ otherList = this.createList(),
+ modelOne = this.createModel(),
+ modelTwo = this.createModel(),
+ modelThree = this.createModel();
+
+ otherList.add([modelOne, modelTwo, modelThree]);
+
+ list.add([{name: 'first'}, {name: 'second'}, {name: 'third'}]);
+ list.add(otherList, {index: 0});
+
+ Assert.areSame(6, list.size(), 'list should contain 6 items');
+ Assert.areSame(modelOne, list.item(0), 'modelOne should be inserted at index 0');
+ Assert.areSame(modelTwo, list.item(1), 'modelTwo should be inserted at index 1');
+ Assert.areSame(modelThree, list.item(2), 'modelThree should be inserted at index 2');
+ },
+
'add() should support models created in other windows': function () {
var list = this.createList(),
iframe = document.getElementById('test-iframe'),
@@ -579,6 +623,17 @@ modelListSuite.add(new Y.Test.Case({
Assert.areSame('zero', list.remove(list.item(0)).get('foo'));
Assert.areSame(1, list.size());
+ Assert.areSame('one', list.item(0).get('foo'));
+ },
+
+ 'remove() should remove a single model from the list by index': function () {
+ var list = this.createList();
+
+ list.add([{foo: 'zero'}, {foo: 'one'}]);
+
+ Assert.areSame('zero', list.remove(0).get('foo'));
+ Assert.areSame(1, list.size());
+ Assert.areSame('one', list.item(0).get('foo'));
},
'remove() should remove an array of models from the list': function () {
@@ -593,6 +648,18 @@ modelListSuite.add(new Y.Test.Case({
Assert.areSame(0, list.size());
},
+ 'remove() should remove an array of models from the list by index': function () {
+ var list = this.createList(),
+ removed;
+
+ list.add([{foo: 'zero'}, {foo: 'one'}, {foo: 'two'}]);
+ removed = list.remove([1, 2]);
+
+ Assert.areSame('one', removed[0].get('foo'));
+ Assert.areSame('two', removed[1].get('foo'));
+ Assert.areSame(1, list.size());
+ },
+
'remove() should remove models in another ModelList from the list': function () {
var list = this.createList(),
otherList = this.createList(),
View
5 src/dom/HISTORY.md
@@ -1,6 +1,11 @@
DOM Change History
==================
+3.5.1
+-----
+ * Bug fix: Fix multiple grouped queries for IE. [Ticket 2532155]
+
+
3.5.0
-----
* Bug fix: Comments are now filtered from IE child queries. [Ticket 2530101]
View
81 src/loader/tests/loader-tests.js
@@ -583,6 +583,87 @@ YUI.add('loader-tests', function(Y) {
test.wait();
},
+ 'test: conditional trigger is an array': function() {
+
+ var loader = new Y.Loader({
+ modules: {
+ test_one: {
+ fullpath: 'one.js'
+ },
+ test_two: {
+ fullpath: 'two.js'
+ },
+ cond_array: {
+ fullpath: 'cond_array.js',
+ condition: {
+ trigger: ['test_one', 'test_two']
+ }
+ }
+ },
+ require: ['test_one']
+ });
+
+ var out = loader.resolve(true);
+ Assert.areEqual(2, out.js.length, 'Wrong number of files returned');
+ Assert.areSame('one.js', out.js[0], 'Failed to load required module');
+ Assert.areSame('cond_array.js', out.js[1], 'Failed to load conditional from trigger array');
+ },
+ 'test: conditional module in array second module': function() {
+
+ var loader = new Y.Loader({
+ modules: {
+ test2_one: {
+ fullpath: '2one.js'
+ },
+ test2_two: {
+ fullpath: '2two.js'
+ },
+ test2_three: {
+ fullpath: '2three.js'
+ },
+ cond2_array: {
+ fullpath: '2cond_array.js',
+ condition: {
+ trigger: ['test2_one', 'test2_two']
+ }
+ }
+ },
+ require: ['test2_two']
+ });
+
+ var out = loader.resolve(true);
+ Assert.areEqual(2, out.js.length, 'Wrong number of files returned (2)');
+ Assert.areSame('2two.js', out.js[0], 'Failed to load required module (2)');
+ Assert.areSame('2cond_array.js', out.js[1], 'Failed to load conditional from trigger array (2)');
+
+ },
+ 'test: conditional array in modules not required': function() {
+ var loader = new Y.Loader({
+ modules: {
+ test3_one: {
+ fullpath: '3one.js'
+ },
+ test3_two: {
+ fullpath: '3two.js'
+ },
+ test3_three: {
+ fullpath: '3three.js'
+ },
+ cond3_array: {
+ fullpath: '3cond_array.js',
+ condition: {
+ trigger: ['test3_one', 'test3_two']
+ }
+ }
+ },
+ require: ['test3_three']
+ });
+
+ var out = loader.resolve(true);
+ Assert.areEqual(1, out.js.length, 'Wrong number of files returned (3)');
+ Assert.areSame('3three.js', out.js[0], 'Failed to load required module (3)');
+
+ },
test_css_stamp: function() {
var test = this,
links = document.getElementsByTagName('link').length + document.getElementsByTagName('style').length;
View
4 src/node/HISTORY.md
@@ -1,6 +1,10 @@
Node Change History
===================
+3.5.1
+-----
+ * Bug fix: Force case-insensitive removeAttribute in IE. [Ticket 2532192]
+
3.5.0
-----
View
2  src/node/js/node-imports.js
@@ -115,7 +115,7 @@ Y.Array.each([
Y.Node.prototype.removeAttribute = function(attr) {
var node = this._node;
if (node) {
- node.removeAttribute(attr);
+ node.removeAttribute(attr, 0); // comma zero for IE < 8 to force case-insensitive
}
return this;
View
10 src/node/tests/node.html
@@ -107,7 +107,7 @@
<a href="foo.html" id="link-2" tabIndex="-1">foo</a>
<form id="test-form" class="test-class" action="#">
<label for="test-text-value"><em>label</em></label>
- <input name="test-text-value" id="test-text-value" value="text value">
+ <input name="test-text-value" id="test-text-value" readonly="read" value="text value">
<input name="test-text-novalue">
<textarea name="test-textarea-value" value="textarea value"></textarea>
@@ -1775,6 +1775,14 @@
node = Y.one(node.getElementsByTagName('div')[0]);
Assert.areEqual(node, node.removeAttribute('id'));
+ },
+
+ 'should remove readonly': function() {
+ var node = Y.one('#test-text-value');
+ node.removeAttribute('readonly');
+
+ Assert.isTrue(!node._node.readonly, 'readonly');
+ Assert.isTrue(!node._node.readOnly, 'readOnly'); // IE
}
}));
View
3  src/widget-buttons/HISTORY.md
@@ -10,6 +10,9 @@ Widget Buttons Change History
event facade is now always the actual index at which the new button exists.
[Ticket #253219]
+ * Fixed issue with properly handling `Y.Node` instances from other YUI
+ sandboxes. [Ticket #2532207]
+
3.5.0
-----
View
29 src/widget-buttons/js/widget-buttons.js
@@ -20,6 +20,12 @@ var YArray = Y.Array,
isString = YLang.isString,
isValue = YLang.isValue;
+// Utility to determine if an object is a Y.Node instance, even if it was
+// created in a different YUI sandbox.
+function isNode(node) {
+ return !!node.getDOMNode;
+}
+
/**
Provides header/body/footer button support for Widgets that use the
`WidgetStdMod` extension.
@@ -351,7 +357,7 @@ WidgetButtons.prototype = {
sectionButtons, atIndex;
// Makes sure we have the full config object.
- if (!Y.instanceOf(button, Y.Node)) {
+ if (!isNode(button)) {
button = this._mergeButtonConfig(button);
section || (section = button.section);
}
@@ -523,9 +529,10 @@ WidgetButtons.prototype = {
var config, buttonConfig, nonButtonNodeCfg,
i, len, action, context, handle;
- // Plug and return an existing Y.Node instance.
- if (Y.instanceOf(button, Y.Node)) {
- return button.plug(ButtonPlugin);
+ // Makes sure the exiting `Y.Node` instance is from this YUI sandbox and
+ // is plugged with `Y.Plugin.Button`.
+ if (isNode(button)) {
+ return Y.one(button.getDOMNode()).plug(ButtonPlugin);
}
// Merge `button` config with defaults and back-compat.
@@ -624,7 +631,7 @@ WidgetButtons.prototype = {
@since 3.5.0
**/
_getButtonDefault: function (button) {
- var isDefault = Y.instanceOf(button, Y.Node) ?
+ var isDefault = isNode(button) ?
button.getData('default') : button.isDefault;
if (isString(isDefault)) {
@@ -654,7 +661,7 @@ WidgetButtons.prototype = {
_getButtonName: function (button) {
var name;
- if (Y.instanceOf(button, Y.Node)) {
+ if (isNode(button)) {
name = button.getData('name') || button.get('name');
} else {
name = button && (button.name || button.type);
@@ -699,7 +706,7 @@ WidgetButtons.prototype = {
@method _mapButton
@param {Node} button The button node to map.
- @param {String} section The `WidgetStdMod` section.
+ @param {String} section The `WidgetStdMod` section (header/body/footer).
@protected
@since 3.5.0
**/
@@ -865,7 +872,7 @@ WidgetButtons.prototype = {
button = buttonConfigs[i];
section = currentSection;
- if (!Y.instanceOf(button, Y.Node)) {
+ if (!isNode(button)) {
button = this._mergeButtonConfig(button);
section || (section = button.section);
}
@@ -1099,11 +1106,13 @@ WidgetButtons.prototype = {
},
/**
- Removes the specified `button` to the buttons map, and nulls-out the
- `defaultButton` if it is currently the default button.
+ Removes the specified `button` from the buttons map (both name -> button and
+ section:name -> button), and nulls-out the `defaultButton` if it is
+ currently the default button.
@method _unMapButton
@param {Node} button The button node to remove from the buttons map.
+ @param {String} section The `WidgetStdMod` section (header/body/footer).
@protected
@since 3.5.0
**/
View
41 src/widget-buttons/tests/widget-buttons-test.js
@@ -268,6 +268,41 @@ suite.add(new Y.Test.Case({
ArrayAssert.isNotEmpty(this.widget.get('buttons.footer'), '`buttons.footer` was empty.');
},
+ '`buttons` should be able to be specified as an Array of Y.Nodes from another YUI instance': function () {
+ var buttons,
+ footerButtons,
+ test = this,
+
+ resumeTest = function() {
+ test.widget = new TestWidget({
+ buttons: buttons
+ });
+
+ footerButtons = test.widget.get('buttons.footer');
+
+ Assert.areSame(2, footerButtons.length, '`buttons.footer` did not have 2 buttons.');
+ Assert.areSame('foo', footerButtons[0].get('text'), 'First button did not have the test "foo".');
+ Assert.areSame('bar', footerButtons[1].get('text'), 'Second button did not have the test "bar".');
+
+ Assert.areNotSame(buttons[0], footerButtons[0]);
+ Assert.isTrue(!!footerButtons[0].hasPlugin('button'), 'Node does not have button plugin.');
+ Assert.isFalse(!!buttons[0].hasPlugin('button'), 'Node from other sandbox has the button plugin.');
+ };
+
+ // use(*) was causing intermittent problems in FF 11 here.
+
+ YUI().use('node', function (Y1) {
+ buttons = [
+ Y1.Node.create('<button>foo</button>'),
+ Y1.Node.create('<button>bar</button>')
+ ];
+
+ test.resume(resumeTest);
+ });
+
+ test.wait();
+ },
+
'`buttons` should be able to be specified as a mixture of all possibile configurations': function () {
var PanelWidget = Y.Base.create('panelWidget', Y.Widget, [Y.WidgetStdMod, Y.WidgetButtons], {
BUTTONS: {
@@ -574,7 +609,7 @@ suite.add(new Y.Test.Case({
Assert.areSame(null, this.widget.get('defaultButton'), '`defaultButton` was not null.');
Assert.areSame(1, called, '`defaultButtonChange` was not called.');
- },
+ }
}));
// -- Methods ------------------------------------------------------------------
@@ -914,10 +949,10 @@ suite.add(new Y.Test.Case({
});
headerButton = this.widget.getStdModNode('header').one('.yui3-button');
- footerBotton = this.widget.getStdModNode('footer').one('.yui3-button');
+ footerButton = this.widget.getStdModNode('footer').one('.yui3-button');
Assert.areSame('Foo', headerButton.get('text'), '`headerButton` text is not "Foo".');
- Assert.areSame('Bar', footerBotton.get('text'), '`footerButton` text is not "Bar".');
+ Assert.areSame('Bar', footerButton.get('text'), '`footerButton` text is not "Bar".');
},
'`buttons` should move to the correct position': function () {

No commit comments for this range

Something went wrong with that request. Please try again.