Permalink
Browse files

Merge remote-tracking branch 'upstream/master'

  • Loading branch information...
2 parents ff0441a + 3a601e5 commit 30b21764ed876fae35ca28f850fd3e850e53b838 @tripp tripp committed Mar 16, 2012
View
70 build/selector-css2/selector-css2-debug.js
@@ -22,11 +22,6 @@ var PARENT_NODE = 'parentNode',
SelectorCSS2 = {
_reRegExpTokens: /([\^\$\?\[\]\*\+\-\.\(\)\|\\])/,
SORT_RESULTS: true,
- _re: {
- attr: /(\[[^\]]*\])/g,
- esc: /\\[:\[\]\(\)#\.\'\>+~"]/gi,
- pseudos: /(\([^\)]*\))/g
- },
// TODO: better detection, document specific
_isXML: (function() {
@@ -319,7 +314,7 @@ var PARENT_NODE = 'parentNode',
*/
_tokenize: function(selector) {
selector = selector || '';
- selector = Selector._replaceShorthand(Y.Lang.trim(selector));
+ selector = Selector._parseSelector(Y.Lang.trim(selector));
var token = Selector._getToken(), // one token per simple selector (left selector holds combinator)
query = selector, // original query for debug report
tokens = [], // array of tokens
@@ -380,58 +375,43 @@ var PARENT_NODE = 'parentNode',
return tokens;
},
- _replaceShorthand: function(selector) {
- var shorthand = Selector.shorthand,
- esc = selector.match(Selector._re.esc), // pull escaped colon, brackets, etc.
- attrs,
- pseudos,
- re, i, len;
-
- if (esc) {
- selector = selector.replace(Selector._re.esc, '\uE000');
- }
-
- pseudos = selector.match(Selector._re.pseudos);
-
- if (pseudos) {
- selector = selector.replace(Selector._re.pseudos, '\uE002');
- }
+ _replaceMarkers: function(selector) {
+ selector = selector.replace(/\[/g, '\uE003');
+ selector = selector.replace(/\]/g, '\uE004');
- attrs = selector.match(Selector._re.attr);
+ selector = selector.replace(/\(/g, '\uE005');
+ selector = selector.replace(/\)/g, '\uE006');
+ return selector;
+ },
- if (attrs) {
- selector = selector.replace(Selector._re.attr, '\uE001');
- }
+ _replaceShorthand: function(selector) {
+ var shorthand = Y.Selector.shorthand,
+ re;
for (re in shorthand) {
if (shorthand.hasOwnProperty(re)) {
selector = selector.replace(new RegExp(re, 'gi'), shorthand[re]);
}
}
- if (attrs) {
- for (i = 0, len = attrs.length; i < len; ++i) {
- selector = selector.replace(/\uE001/, attrs[i]);
- }
- }
+ return selector;
+ },
- if (pseudos) {
- for (i = 0, len = pseudos.length; i < len; ++i) {
- selector = selector.replace(/\uE002/, pseudos[i]);
- }
- }
+ _parseSelector: function(selector) {
+ var replaced = Y.Selector._replaceSelector(selector),
+ selector = replaced.selector;
- selector = selector.replace(/\[/g, '\uE003');
- selector = selector.replace(/\]/g, '\uE004');
+ // replace shorthand (".foo, #bar") after pseudos and attrs
+ // to avoid replacing unescaped chars
+ selector = Y.Selector._replaceShorthand(selector);
- selector = selector.replace(/\(/g, '\uE005');
- selector = selector.replace(/\)/g, '\uE006');
+ selector = Y.Selector._restore('attr', selector, replaced.attrs);
+ selector = Y.Selector._restore('pseudo', selector, replaced.pseudos);
- if (esc) {
- for (i = 0, len = esc.length; i < len; ++i) {
- selector = selector.replace('\uE000', esc[i]);
- }
- }
+ // replace braces and parens before restoring escaped chars
+ // to avoid replacing ecaped markers
+ selector = Y.Selector._replaceMarkers(selector);
+ selector = Y.Selector._restore('esc', selector, replaced.esc);
return selector;
},
View
2 build/selector-css2/selector-css2-min.js
@@ -1 +1 @@
-YUI.add("selector-css2",function(g){var h="parentNode",d="tagName",e="attributes",a="combinator",f="pseudos",c=g.Selector,b={_reRegExpTokens:/([\^\$\?\[\]\*\+\-\.\(\)\|\\])/,SORT_RESULTS:true,_re:{attr:/(\[[^\]]*\])/g,esc:/\\[:\[\]\(\)#\.\'\>+~"]/gi,pseudos:/(\([^\)]*\))/g},_isXML:(function(){var i=(g.config.doc.createElement("div").tagName!=="DIV");return i;}()),shorthand:{"\\#(-?[_a-z0-9]+[-\\w\\uE000]*)":"[id=$1]","\\.(-?[_a-z]+[-\\w\\uE000]*)":"[className~=$1]"},operators:{"":function(j,i){return g.DOM.getAttribute(j,i)!=="";},"~=":"(?:^|\\s+){val}(?:\\s+|$)","|=":"^{val}-?"},pseudos:{"first-child":function(i){return g.DOM._children(i[h])[0]===i;}},_bruteQuery:function(n,r,t){var o=[],i=[],q=c._tokenize(n),m=q[q.length-1],s=g.DOM._getDoc(r),k,j,p,l;if(m){j=m.id;p=m.className;l=m.tagName||"*";if(r.getElementsByTagName){if(j&&(r.all||(r.nodeType===9||g.DOM.inDoc(r)))){i=g.DOM.allById(j,r);}else{if(p){i=r.getElementsByClassName(p);}else{i=r.getElementsByTagName(l);}}}else{k=r.firstChild;while(k){if(k.tagName&&(l==="*"||k.tagName===l)){i.push(k);}k=k.nextSibling||k.firstChild;}}if(i.length){o=c._filterNodes(i,q,t);}}return o;},_filterNodes:function(u,q,s){var z=0,y,A=q.length,t=A-1,p=[],w=u[0],D=w,B=g.Selector.getters,o,x,m,r,k,v,l,C;for(z=0;(D=w=u[z++]);){t=A-1;r=null;testLoop:while(D&&D.tagName){m=q[t];l=m.tests;y=l.length;if(y&&!k){while((C=l[--y])){o=C[1];if(B[C[0]]){v=B[C[0]](D,C[0]);}else{v=D[C[0]];if(C[0]==="tagName"&&!c._isXML){v=v.toUpperCase();}if(typeof v!="string"&&v!==undefined&&v.toString){v=v.toString();}else{if(v===undefined&&D.getAttribute){v=D.getAttribute(C[0],2);}}}if((o==="="&&v!==C[2])||(typeof o!=="string"&&o.test&&!o.test(v))||(!o.test&&typeof o==="function"&&!o(D,C[0],C[2]))){if((D=D[r])){while(D&&(!D.tagName||(m.tagName&&m.tagName!==D.tagName))){D=D[r];}}continue testLoop;}}}t--;if(!k&&(x=m.combinator)){r=x.axis;D=D[r];while(D&&!D.tagName){D=D[r];}if(x.direct){r=null;}}else{p.push(w);if(s){return p;}break;}}}w=D=null;return p;},combinators:{" ":{axis:"parentNode"},">":{axis:"parentNode",direct:true},"+":{axis:"previousSibling",direct:true}},_parsers:[{name:e,re:/^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i,fn:function(l,m){var k=l[2]||"",i=c.operators,j=(l[3])?l[3].replace(/\\/g,""):"",n;if((l[1]==="id"&&k==="=")||(l[1]==="className"&&g.config.doc.documentElement.getElementsByClassName&&(k==="~="||k==="="))){m.prefilter=l[1];l[3]=j;m[l[1]]=(l[1]==="id")?l[3]:j;}if(k in i){n=i[k];if(typeof n==="string"){l[3]=j.replace(c._reRegExpTokens,"\\$1");n=new RegExp(n.replace("{val}",l[3]));}l[2]=n;}if(!m.last||m.prefilter!==l[1]){return l.slice(1);}}},{name:d,re:/^((?:-?[_a-z]+[\w-]*)|\*)/i,fn:function(j,k){var i=j[1];if(!c._isXML){i=i.toUpperCase();}k.tagName=i;if(i!=="*"&&(!k.last||k.prefilter)){return[d,"=",i];}if(!k.prefilter){k.prefilter="tagName";}}},{name:a,re:/^\s*([>+~]|\s)\s*/,fn:function(i,j){}},{name:f,re:/^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i,fn:function(i,j){var k=c[f][i[1]];if(k){if(i[2]){i[2]=i[2].replace(/\\/g,"");}return[i[2],k];}else{return false;}}}],_getToken:function(i){return{tagName:null,id:null,className:null,attributes:{},combinator:null,tests:[]};},_tokenize:function(l){l=l||"";l=c._replaceShorthand(g.Lang.trim(l));var k=c._getToken(),q=l,p=[],r=false,n,o,m,j;outer:do{r=false;for(m=0;(j=c._parsers[m++]);){if((n=j.re.exec(l))){if(j.name!==a){k.selector=l;}l=l.replace(n[0],"");if(!l.length){k.last=true;}if(c._attrFilters[n[1]]){n[1]=c._attrFilters[n[1]];}o=j.fn(n,k);if(o===false){r=false;break outer;}else{if(o){k.tests.push(o);}}if(!l.length||j.name===a){p.push(k);k=c._getToken(k);if(j.name===a){k.combinator=g.Selector.combinators[n[1]];}}r=true;}}}while(r&&l.length);if(!r||l.length){p=[];}return p;},_replaceShorthand:function(k){var m=c.shorthand,l=k.match(c._re.esc),n,q,p,o,j;if(l){k=k.replace(c._re.esc,"\uE000");}q=k.match(c._re.pseudos);if(q){k=k.replace(c._re.pseudos,"\uE002");}n=k.match(c._re.attr);if(n){k=k.replace(c._re.attr,"\uE001");}for(p in m){if(m.hasOwnProperty(p)){k=k.replace(new RegExp(p,"gi"),m[p]);}}if(n){for(o=0,j=n.length;o<j;++o){k=k.replace(/\uE001/,n[o]);}}if(q){for(o=0,j=q.length;o<j;++o){k=k.replace(/\uE002/,q[o]);}}k=k.replace(/\[/g,"\uE003");k=k.replace(/\]/g,"\uE004");k=k.replace(/\(/g,"\uE005");k=k.replace(/\)/g,"\uE006");if(l){for(o=0,j=l.length;o<j;++o){k=k.replace("\uE000",l[o]);}}return k;},_attrFilters:{"class":"className","for":"htmlFor"},getters:{href:function(j,i){return g.DOM.getAttribute(j,i);},id:function(j,i){return g.DOM.getId(j);}}};g.mix(g.Selector,b,true);g.Selector.getters.src=g.Selector.getters.rel=g.Selector.getters.href;if(g.Selector.useNative&&g.config.doc.querySelector){g.Selector.shorthand["\\.(-?[_a-z]+[-\\w]*)"]="[class~=$1]";}},"@VERSION@",{requires:["selector-native"]});
+YUI.add("selector-css2",function(g){var h="parentNode",d="tagName",e="attributes",a="combinator",f="pseudos",c=g.Selector,b={_reRegExpTokens:/([\^\$\?\[\]\*\+\-\.\(\)\|\\])/,SORT_RESULTS:true,_isXML:(function(){var i=(g.config.doc.createElement("div").tagName!=="DIV");return i;}()),shorthand:{"\\#(-?[_a-z0-9]+[-\\w\\uE000]*)":"[id=$1]","\\.(-?[_a-z]+[-\\w\\uE000]*)":"[className~=$1]"},operators:{"":function(j,i){return g.DOM.getAttribute(j,i)!=="";},"~=":"(?:^|\\s+){val}(?:\\s+|$)","|=":"^{val}-?"},pseudos:{"first-child":function(i){return g.DOM._children(i[h])[0]===i;}},_bruteQuery:function(n,r,t){var o=[],i=[],q=c._tokenize(n),m=q[q.length-1],s=g.DOM._getDoc(r),k,j,p,l;if(m){j=m.id;p=m.className;l=m.tagName||"*";if(r.getElementsByTagName){if(j&&(r.all||(r.nodeType===9||g.DOM.inDoc(r)))){i=g.DOM.allById(j,r);}else{if(p){i=r.getElementsByClassName(p);}else{i=r.getElementsByTagName(l);}}}else{k=r.firstChild;while(k){if(k.tagName&&(l==="*"||k.tagName===l)){i.push(k);}k=k.nextSibling||k.firstChild;}}if(i.length){o=c._filterNodes(i,q,t);}}return o;},_filterNodes:function(u,q,s){var z=0,y,A=q.length,t=A-1,p=[],w=u[0],D=w,B=g.Selector.getters,o,x,m,r,k,v,l,C;for(z=0;(D=w=u[z++]);){t=A-1;r=null;testLoop:while(D&&D.tagName){m=q[t];l=m.tests;y=l.length;if(y&&!k){while((C=l[--y])){o=C[1];if(B[C[0]]){v=B[C[0]](D,C[0]);}else{v=D[C[0]];if(C[0]==="tagName"&&!c._isXML){v=v.toUpperCase();}if(typeof v!="string"&&v!==undefined&&v.toString){v=v.toString();}else{if(v===undefined&&D.getAttribute){v=D.getAttribute(C[0],2);}}}if((o==="="&&v!==C[2])||(typeof o!=="string"&&o.test&&!o.test(v))||(!o.test&&typeof o==="function"&&!o(D,C[0],C[2]))){if((D=D[r])){while(D&&(!D.tagName||(m.tagName&&m.tagName!==D.tagName))){D=D[r];}}continue testLoop;}}}t--;if(!k&&(x=m.combinator)){r=x.axis;D=D[r];while(D&&!D.tagName){D=D[r];}if(x.direct){r=null;}}else{p.push(w);if(s){return p;}break;}}}w=D=null;return p;},combinators:{" ":{axis:"parentNode"},">":{axis:"parentNode",direct:true},"+":{axis:"previousSibling",direct:true}},_parsers:[{name:e,re:/^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i,fn:function(l,m){var k=l[2]||"",i=c.operators,j=(l[3])?l[3].replace(/\\/g,""):"",n;if((l[1]==="id"&&k==="=")||(l[1]==="className"&&g.config.doc.documentElement.getElementsByClassName&&(k==="~="||k==="="))){m.prefilter=l[1];l[3]=j;m[l[1]]=(l[1]==="id")?l[3]:j;}if(k in i){n=i[k];if(typeof n==="string"){l[3]=j.replace(c._reRegExpTokens,"\\$1");n=new RegExp(n.replace("{val}",l[3]));}l[2]=n;}if(!m.last||m.prefilter!==l[1]){return l.slice(1);}}},{name:d,re:/^((?:-?[_a-z]+[\w-]*)|\*)/i,fn:function(j,k){var i=j[1];if(!c._isXML){i=i.toUpperCase();}k.tagName=i;if(i!=="*"&&(!k.last||k.prefilter)){return[d,"=",i];}if(!k.prefilter){k.prefilter="tagName";}}},{name:a,re:/^\s*([>+~]|\s)\s*/,fn:function(i,j){}},{name:f,re:/^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i,fn:function(i,j){var k=c[f][i[1]];if(k){if(i[2]){i[2]=i[2].replace(/\\/g,"");}return[i[2],k];}else{return false;}}}],_getToken:function(i){return{tagName:null,id:null,className:null,attributes:{},combinator:null,tests:[]};},_tokenize:function(l){l=l||"";l=c._parseSelector(g.Lang.trim(l));var k=c._getToken(),q=l,p=[],r=false,n,o,m,j;outer:do{r=false;for(m=0;(j=c._parsers[m++]);){if((n=j.re.exec(l))){if(j.name!==a){k.selector=l;}l=l.replace(n[0],"");if(!l.length){k.last=true;}if(c._attrFilters[n[1]]){n[1]=c._attrFilters[n[1]];}o=j.fn(n,k);if(o===false){r=false;break outer;}else{if(o){k.tests.push(o);}}if(!l.length||j.name===a){p.push(k);k=c._getToken(k);if(j.name===a){k.combinator=g.Selector.combinators[n[1]];}}r=true;}}}while(r&&l.length);if(!r||l.length){p=[];}return p;},_replaceMarkers:function(i){i=i.replace(/\[/g,"\uE003");i=i.replace(/\]/g,"\uE004");i=i.replace(/\(/g,"\uE005");i=i.replace(/\)/g,"\uE006");return i;},_replaceShorthand:function(i){var j=g.Selector.shorthand,k;for(k in j){if(j.hasOwnProperty(k)){i=i.replace(new RegExp(k,"gi"),j[k]);}}return i;},_parseSelector:function(i){var j=g.Selector._replaceSelector(i),i=j.selector;i=g.Selector._replaceShorthand(i);i=g.Selector._restore("attr",i,j.attrs);i=g.Selector._restore("pseudo",i,j.pseudos);i=g.Selector._replaceMarkers(i);i=g.Selector._restore("esc",i,j.esc);return i;},_attrFilters:{"class":"className","for":"htmlFor"},getters:{href:function(j,i){return g.DOM.getAttribute(j,i);},id:function(j,i){return g.DOM.getId(j);}}};g.mix(g.Selector,b,true);g.Selector.getters.src=g.Selector.getters.rel=g.Selector.getters.href;if(g.Selector.useNative&&g.config.doc.querySelector){g.Selector.shorthand["\\.(-?[_a-z]+[-\\w]*)"]="[class~=$1]";}},"@VERSION@",{requires:["selector-native"]});
View
70 build/selector-css2/selector-css2.js
@@ -22,11 +22,6 @@ var PARENT_NODE = 'parentNode',
SelectorCSS2 = {
_reRegExpTokens: /([\^\$\?\[\]\*\+\-\.\(\)\|\\])/,
SORT_RESULTS: true,
- _re: {
- attr: /(\[[^\]]*\])/g,
- esc: /\\[:\[\]\(\)#\.\'\>+~"]/gi,
- pseudos: /(\([^\)]*\))/g
- },
// TODO: better detection, document specific
_isXML: (function() {
@@ -319,7 +314,7 @@ var PARENT_NODE = 'parentNode',
*/
_tokenize: function(selector) {
selector = selector || '';
- selector = Selector._replaceShorthand(Y.Lang.trim(selector));
+ selector = Selector._parseSelector(Y.Lang.trim(selector));
var token = Selector._getToken(), // one token per simple selector (left selector holds combinator)
query = selector, // original query for debug report
tokens = [], // array of tokens
@@ -379,58 +374,43 @@ var PARENT_NODE = 'parentNode',
return tokens;
},
- _replaceShorthand: function(selector) {
- var shorthand = Selector.shorthand,
- esc = selector.match(Selector._re.esc), // pull escaped colon, brackets, etc.
- attrs,
- pseudos,
- re, i, len;
-
- if (esc) {
- selector = selector.replace(Selector._re.esc, '\uE000');
- }
-
- pseudos = selector.match(Selector._re.pseudos);
-
- if (pseudos) {
- selector = selector.replace(Selector._re.pseudos, '\uE002');
- }
+ _replaceMarkers: function(selector) {
+ selector = selector.replace(/\[/g, '\uE003');
+ selector = selector.replace(/\]/g, '\uE004');
- attrs = selector.match(Selector._re.attr);
+ selector = selector.replace(/\(/g, '\uE005');
+ selector = selector.replace(/\)/g, '\uE006');
+ return selector;
+ },
- if (attrs) {
- selector = selector.replace(Selector._re.attr, '\uE001');
- }
+ _replaceShorthand: function(selector) {
+ var shorthand = Y.Selector.shorthand,
+ re;
for (re in shorthand) {
if (shorthand.hasOwnProperty(re)) {
selector = selector.replace(new RegExp(re, 'gi'), shorthand[re]);
}
}
- if (attrs) {
- for (i = 0, len = attrs.length; i < len; ++i) {
- selector = selector.replace(/\uE001/, attrs[i]);
- }
- }
+ return selector;
+ },
- if (pseudos) {
- for (i = 0, len = pseudos.length; i < len; ++i) {
- selector = selector.replace(/\uE002/, pseudos[i]);
- }
- }
+ _parseSelector: function(selector) {
+ var replaced = Y.Selector._replaceSelector(selector),
+ selector = replaced.selector;
- selector = selector.replace(/\[/g, '\uE003');
- selector = selector.replace(/\]/g, '\uE004');
+ // replace shorthand (".foo, #bar") after pseudos and attrs
+ // to avoid replacing unescaped chars
+ selector = Y.Selector._replaceShorthand(selector);
- selector = selector.replace(/\(/g, '\uE005');
- selector = selector.replace(/\)/g, '\uE006');
+ selector = Y.Selector._restore('attr', selector, replaced.attrs);
+ selector = Y.Selector._restore('pseudo', selector, replaced.pseudos);
- if (esc) {
- for (i = 0, len = esc.length; i < len; ++i) {
- selector = selector.replace('\uE000', esc[i]);
- }
- }
+ // replace braces and parens before restoring escaped chars
+ // to avoid replacing ecaped markers
+ selector = Y.Selector._replaceMarkers(selector);
+ selector = Y.Selector._restore('esc', selector, replaced.esc);
return selector;
},
View
84 build/selector-native/selector-native-debug.js
@@ -21,6 +21,23 @@ var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
OWNER_DOCUMENT = 'ownerDocument';
var Selector = {
+ _types: {
+ esc: {
+ token: '\uE000',
+ re: /\\[:\[\]\(\)#\.\'\>+~"]/gi
+ },
+
+ attr: {
+ token: '\uE001',
+ re: /(\[[^\]]*\])/g
+ },
+
+ pseudo: {
+ token: '\uE002',
+ re: /(\([^\)]*\))/g
+ }
+ },
+
useNative: true,
_escapeId: function(id) {
@@ -142,10 +159,55 @@ var Selector = {
},
+ _replaceSelector: function(selector) {
+ var esc = Y.Selector._parse('esc', selector), // pull escaped colon, brackets, etc.
+ attrs,
+ pseudos;
+
+ // first replace escaped chars, which could be present in attrs or pseudos
+ selector = Y.Selector._replace('esc', selector);
+
+ // then replace pseudos before attrs to avoid replacing :not([foo])
+ pseudos = Y.Selector._parse('pseudo', selector);
+ selector = Selector._replace('pseudo', selector);
+
+ attrs = Y.Selector._parse('attr', selector);
+ selector = Y.Selector._replace('attr', selector);
+
+ return {
+ esc: esc,
+ attrs: attrs,
+ pseudos: pseudos,
+ selector: selector
+ }
+ },
+
+ _restoreSelector: function(replaced) {
+ var selector = replaced.selector;
+ selector = Y.Selector._restore('attr', selector, replaced.attrs);
+ selector = Y.Selector._restore('pseudo', selector, replaced.pseudos);
+ selector = Y.Selector._restore('esc', selector, replaced.esc);
+ return selector;
+ },
+
+ _replaceCommas: function(selector) {
+ var replaced = Y.Selector._replaceSelector(selector),
+ selector = replaced.selector;
+
+ if (selector) {
+ selector = selector.replace(',', '\uE007', 'g');
+ replaced.selector = selector;
+ selector = Y.Selector._restoreSelector(replaced);
+ }
+ return selector;
+ },
+
// allows element scoped queries to begin with combinator
// e.g. query('> p', document.body) === query('body > p')
_splitQueries: function(selector, node) {
- var groups = selector.split(','),
+ // avoid picking up from attrs and pseudos
+ selector = Y.Selector._replaceCommas(selector);
+ var groups = selector.split('\uE007'),
queries = [],
prefix = '',
id,
@@ -280,6 +342,26 @@ var Selector = {
return Y.DOM.ancestor(element, function(n) {
return Y.Selector.test(n, selector);
}, testSelf);
+ },
+
+ _parse: function(name, selector) {
+ return selector.match(Y.Selector._types[name].re);
+ },
+
+ _replace: function(name, selector) {
+ var o = Y.Selector._types[name];
+ return selector.replace(o.re, o.token);
+ },
+
+ _restore: function(name, selector, items) {
+ if (items) {
+ var token = Y.Selector._types[name].token,
+ i, len;
+ for (i = 0, len = items.length; i < len; ++i) {
+ selector = selector.replace(token, items[i]);
+ }
+ }
+ return selector;
}
};
View
2 build/selector-native/selector-native-min.js
@@ -1 +1 @@
-YUI.add("selector-native",function(a){(function(e){e.namespace("Selector");var c="compareDocumentPosition",d="ownerDocument";var b={useNative:true,_escapeId:function(f){if(f){f=f.replace(/([:\[\]\(\)#\.'<>+~"])/g,"\\$1");}return f;},_compare:("sourceIndex" in e.config.doc.documentElement)?function(i,h){var g=i.sourceIndex,f=h.sourceIndex;if(g===f){return 0;}else{if(g>f){return 1;}}return -1;}:(e.config.doc.documentElement[c]?function(g,f){if(g[c](f)&4){return -1;}else{return 1;}}:function(j,i){var h,f,g;if(j&&i){h=j[d].createRange();h.setStart(j,0);f=i[d].createRange();f.setStart(i,0);g=h.compareBoundaryPoints(1,f);}return g;}),_sort:function(f){if(f){f=e.Array(f,0,true);if(f.sort){f.sort(b._compare);}}return f;},_deDupe:function(f){var g=[],h,j;for(h=0;(j=f[h++]);){if(!j._found){g[g.length]=j;j._found=true;}}for(h=0;(j=g[h++]);){j._found=null;j.removeAttribute("_found");}return g;},query:function(g,o,p,f){o=o||e.config.doc;var l=[],h=(e.Selector.useNative&&e.config.doc.querySelector&&!f),k=[[g,o]],m,q,j,n=(h)?e.Selector._nativeQuery:e.Selector._bruteQuery;if(g&&n){if(!f&&(!h||o.tagName)){k=b._splitQueries(g,o);}for(j=0;(m=k[j++]);){q=n(m[0],m[1],p);if(!p){q=e.Array(q,0,true);}if(q){l=l.concat(q);}}if(k.length>1){l=b._sort(b._deDupe(l));}}return(p)?(l[0]||null):l;},_splitQueries:function(h,l){var g=h.split(","),j=[],m="",n,k,f;if(l){if(l.nodeType===1){n=e.Selector._escapeId(e.DOM.getId(l));if(!n){n=e.guid();e.DOM.setId(l,n);}m='[id="'+n+'"] ';}for(k=0,f=g.length;k<f;++k){h=m+g[k];j.push([h,l]);}}return j;},_nativeQuery:function(f,g,h){if(e.UA.webkit&&f.indexOf(":checked")>-1&&(e.Selector.pseudos&&e.Selector.pseudos.checked)){return e.Selector.query(f,g,h,true);}try{return g["querySelector"+(h?"":"All")](f);}catch(i){return e.Selector.query(f,g,h,true);}},filter:function(g,f){var h=[],j,k;if(g&&f){for(j=0;(k=g[j++]);){if(e.Selector.test(k,f)){h[h.length]=k;}}}else{}return h;},test:function(k,l,q){var o=false,g=false,h,r,u,p,t,f,n,m,s;if(k&&k.tagName){if(typeof l=="function"){o=l.call(k,k);}else{h=l.split(",");if(!q&&!e.DOM.inDoc(k)){r=k.parentNode;if(r){q=r;}else{t=k[d].createDocumentFragment();t.appendChild(k);q=t;g=true;}}q=q||k[d];f=e.Selector._escapeId(e.DOM.getId(k));if(!f){f=e.guid();e.DOM.setId(k,f);}for(n=0;(s=h[n++]);){s+='[id="'+f+'"]';p=e.Selector.query(s,q);for(m=0;u=p[m++];){if(u===k){o=true;break;}}if(o){break;}}if(g){t.removeChild(k);}}}return o;},ancestor:function(g,f,h){return e.DOM.ancestor(g,function(i){return e.Selector.test(i,f);},h);}};e.mix(e.Selector,b,true);})(a);},"@VERSION@",{requires:["dom-base"]});
+YUI.add("selector-native",function(a){(function(e){e.namespace("Selector");var c="compareDocumentPosition",d="ownerDocument";var b={_types:{esc:{token:"\uE000",re:/\\[:\[\]\(\)#\.\'\>+~"]/gi},attr:{token:"\uE001",re:/(\[[^\]]*\])/g},pseudo:{token:"\uE002",re:/(\([^\)]*\))/g}},useNative:true,_escapeId:function(f){if(f){f=f.replace(/([:\[\]\(\)#\.'<>+~"])/g,"\\$1");}return f;},_compare:("sourceIndex" in e.config.doc.documentElement)?function(i,h){var g=i.sourceIndex,f=h.sourceIndex;if(g===f){return 0;}else{if(g>f){return 1;}}return -1;}:(e.config.doc.documentElement[c]?function(g,f){if(g[c](f)&4){return -1;}else{return 1;}}:function(j,i){var h,f,g;if(j&&i){h=j[d].createRange();h.setStart(j,0);f=i[d].createRange();f.setStart(i,0);g=h.compareBoundaryPoints(1,f);}return g;}),_sort:function(f){if(f){f=e.Array(f,0,true);if(f.sort){f.sort(b._compare);}}return f;},_deDupe:function(f){var g=[],h,j;for(h=0;(j=f[h++]);){if(!j._found){g[g.length]=j;j._found=true;}}for(h=0;(j=g[h++]);){j._found=null;j.removeAttribute("_found");}return g;},query:function(g,o,p,f){o=o||e.config.doc;var l=[],h=(e.Selector.useNative&&e.config.doc.querySelector&&!f),k=[[g,o]],m,q,j,n=(h)?e.Selector._nativeQuery:e.Selector._bruteQuery;if(g&&n){if(!f&&(!h||o.tagName)){k=b._splitQueries(g,o);}for(j=0;(m=k[j++]);){q=n(m[0],m[1],p);if(!p){q=e.Array(q,0,true);}if(q){l=l.concat(q);}}if(k.length>1){l=b._sort(b._deDupe(l));}}return(p)?(l[0]||null):l;},_replaceSelector:function(f){var g=e.Selector._parse("esc",f),h,i;f=e.Selector._replace("esc",f);i=e.Selector._parse("pseudo",f);f=b._replace("pseudo",f);h=e.Selector._parse("attr",f);f=e.Selector._replace("attr",f);return{esc:g,attrs:h,pseudos:i,selector:f};},_restoreSelector:function(g){var f=g.selector;f=e.Selector._restore("attr",f,g.attrs);f=e.Selector._restore("pseudo",f,g.pseudos);f=e.Selector._restore("esc",f,g.esc);return f;},_replaceCommas:function(f){var g=e.Selector._replaceSelector(f),f=g.selector;if(f){f=f.replace(",","\uE007","g");g.selector=f;f=e.Selector._restoreSelector(g);}return f;},_splitQueries:function(h,l){h=e.Selector._replaceCommas(h);var g=h.split("\uE007"),j=[],m="",n,k,f;if(l){if(l.nodeType===1){n=e.Selector._escapeId(e.DOM.getId(l));if(!n){n=e.guid();e.DOM.setId(l,n);}m='[id="'+n+'"] ';}for(k=0,f=g.length;k<f;++k){h=m+g[k];j.push([h,l]);}}return j;},_nativeQuery:function(f,g,h){if(e.UA.webkit&&f.indexOf(":checked")>-1&&(e.Selector.pseudos&&e.Selector.pseudos.checked)){return e.Selector.query(f,g,h,true);}try{return g["querySelector"+(h?"":"All")](f);}catch(i){return e.Selector.query(f,g,h,true);}},filter:function(g,f){var h=[],j,k;if(g&&f){for(j=0;(k=g[j++]);){if(e.Selector.test(k,f)){h[h.length]=k;}}}else{}return h;},test:function(k,l,q){var o=false,g=false,h,r,u,p,t,f,n,m,s;if(k&&k.tagName){if(typeof l=="function"){o=l.call(k,k);}else{h=l.split(",");if(!q&&!e.DOM.inDoc(k)){r=k.parentNode;if(r){q=r;}else{t=k[d].createDocumentFragment();t.appendChild(k);q=t;g=true;}}q=q||k[d];f=e.Selector._escapeId(e.DOM.getId(k));if(!f){f=e.guid();e.DOM.setId(k,f);}for(n=0;(s=h[n++]);){s+='[id="'+f+'"]';p=e.Selector.query(s,q);for(m=0;u=p[m++];){if(u===k){o=true;break;}}if(o){break;}}if(g){t.removeChild(k);}}}return o;},ancestor:function(g,f,h){return e.DOM.ancestor(g,function(i){return e.Selector.test(i,f);},h);},_parse:function(g,f){return f.match(e.Selector._types[g].re);},_replace:function(g,f){var h=e.Selector._types[g];return f.replace(h.re,h.token);},_restore:function(j,g,h){if(h){var l=e.Selector._types[j].token,k,f;for(k=0,f=h.length;k<f;++k){g=g.replace(l,h[k]);}}return g;}};e.mix(e.Selector,b,true);})(a);},"@VERSION@",{requires:["dom-base"]});
View
84 build/selector-native/selector-native.js
@@ -21,6 +21,23 @@ var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
OWNER_DOCUMENT = 'ownerDocument';
var Selector = {
+ _types: {
+ esc: {
+ token: '\uE000',
+ re: /\\[:\[\]\(\)#\.\'\>+~"]/gi
+ },
+
+ attr: {
+ token: '\uE001',
+ re: /(\[[^\]]*\])/g
+ },
+
+ pseudo: {
+ token: '\uE002',
+ re: /(\([^\)]*\))/g
+ }
+ },
+
useNative: true,
_escapeId: function(id) {
@@ -141,10 +158,55 @@ var Selector = {
},
+ _replaceSelector: function(selector) {
+ var esc = Y.Selector._parse('esc', selector), // pull escaped colon, brackets, etc.
+ attrs,
+ pseudos;
+
+ // first replace escaped chars, which could be present in attrs or pseudos
+ selector = Y.Selector._replace('esc', selector);
+
+ // then replace pseudos before attrs to avoid replacing :not([foo])
+ pseudos = Y.Selector._parse('pseudo', selector);
+ selector = Selector._replace('pseudo', selector);
+
+ attrs = Y.Selector._parse('attr', selector);
+ selector = Y.Selector._replace('attr', selector);
+
+ return {
+ esc: esc,
+ attrs: attrs,
+ pseudos: pseudos,
+ selector: selector
+ }
+ },
+
+ _restoreSelector: function(replaced) {
+ var selector = replaced.selector;
+ selector = Y.Selector._restore('attr', selector, replaced.attrs);
+ selector = Y.Selector._restore('pseudo', selector, replaced.pseudos);
+ selector = Y.Selector._restore('esc', selector, replaced.esc);
+ return selector;
+ },
+
+ _replaceCommas: function(selector) {
+ var replaced = Y.Selector._replaceSelector(selector),
+ selector = replaced.selector;
+
+ if (selector) {
+ selector = selector.replace(',', '\uE007', 'g');
+ replaced.selector = selector;
+ selector = Y.Selector._restoreSelector(replaced);
+ }
+ return selector;
+ },
+
// allows element scoped queries to begin with combinator
// e.g. query('> p', document.body) === query('body > p')
_splitQueries: function(selector, node) {
- var groups = selector.split(','),
+ // avoid picking up from attrs and pseudos
+ selector = Y.Selector._replaceCommas(selector);
+ var groups = selector.split('\uE007'),
queries = [],
prefix = '',
id,
@@ -275,6 +337,26 @@ var Selector = {
return Y.DOM.ancestor(element, function(n) {
return Y.Selector.test(n, selector);
}, testSelf);
+ },
+
+ _parse: function(name, selector) {
+ return selector.match(Y.Selector._types[name].re);
+ },
+
+ _replace: function(name, selector) {
+ var o = Y.Selector._types[name];
+ return selector.replace(o.re, o.token);
+ },
+
+ _restore: function(name, selector, items) {
+ if (items) {
+ var token = Y.Selector._types[name].token,
+ i, len;
+ for (i = 0, len = items.length; i < len; ++i) {
+ selector = selector.replace(token, items[i]);
+ }
+ }
+ return selector;
}
};
View
219 src/base/tests/base-core.html
@@ -219,7 +219,7 @@
initOnly : {
writeOnce:"initOnly"
- }
+ }
};
Y.extend(AttrHost, Y.BaseCore);
@@ -344,9 +344,220 @@
}
});
+ function CoreTestsHost(config) {
+ CoreTestsHost.superclass.constructor.apply(this, arguments);
+ }
+
+ CoreTestsHost.NAME = "coreTestsHost";
+ CoreTestsHost.ATTRS = {
+ cloneDefaultObject : {
+ value : {
+ a:1,
+ b:2,
+ c:3
+ }
+ },
+
+ cloneDefaultArray : {
+ value : ["foo", "bar", "foobar"]
+ },
+
+ cloneDefaultString : {
+ value : "foo"
+ },
+
+ cloneDefaultOverride : {
+ value : {
+ a:1, b:2, c:3
+ },
+ cloneDefaultValue : false
+ },
+
+ cloneDefaultShallow : {
+ value : {
+ a: {foo: "bar"}
+ },
+ cloneDefaultValue : "shallow"
+ },
+
+ cloneDefaultDeep : {
+ value : {
+ a: {foo: "bar"}
+ },
+ cloneDefaultValue : "deep"
+ },
+
+ cloneDefaultComplex : {
+ value : new Y.BaseCore()
+ }
+ };
+ Y.extend(CoreTestsHost, Y.BaseCore);
+
+ var coreTemplate = {
+
+ name: "Core Tests",
+
+ testInit : function() {
+ var h = new CoreTestsHost();
+ Y.Assert.isTrue(h.get("initialized"));
+ },
+
+ testDestroy : function() {
+ var h = new CoreTestsHost();
+
+ Y.Assert.isFalse(h.get("destroyed"));
+
+ h.destroy();
+
+ Y.Assert.isTrue(h.get("destroyed"));
+ },
+
+ testToString : function() {
+ var h = new CoreTestsHost(),
+ re = /^coreTestsHost\[.*?\]$/,
+ str = h.toString();
+
+ Y.Assert.isTrue(re.test(str));
+ },
+
+ testCloneDefaultValueObject : function() {
+ var h = new CoreTestsHost(),
+ val = h.get("cloneDefaultObject");
+
+ Y.Assert.isTrue(CoreTestsHost.ATTRS.cloneDefaultObject.value !== val);
+
+ Y.ObjectAssert.areEqual({
+ a:1,
+ b:2,
+ c:3
+ }, val);
+ },
+
+ testCloneDefaultValueArray : function() {
+ var h = new CoreTestsHost(),
+ val = h.get("cloneDefaultArray");
+
+ Y.Assert.isTrue(CoreTestsHost.ATTRS.cloneDefaultArray.value !== val);
+ Y.ArrayAssert.itemsAreEqual(["foo", "bar", "foobar"], val);
+ },
+
+ testCloneDefaultValueString : function() {
+ var h = new CoreTestsHost(),
+ val = h.get("cloneDefaultString");
+
+ Y.Assert.isTrue(CoreTestsHost.ATTRS.cloneDefaultString.value === val);
+ },
+
+ testCloneDefaultComplex : function() {
+ var h = new CoreTestsHost(),
+ val = h.get("cloneDefaultComplex");
+
+ // Don't try to clone by default. We may hurt our backs
+ Y.Assert.isTrue(CoreTestsHost.ATTRS.cloneDefaultComplex.value === val);
+ },
+
+ testCloneDefaultShallow : function() {
+ var h = new CoreTestsHost(),
+ val = h.get("cloneDefaultShallow");
+
+ Y.Assert.isTrue(CoreTestsHost.ATTRS.cloneDefaultShallow.value !== val);
+ Y.Assert.isTrue(CoreTestsHost.ATTRS.cloneDefaultShallow.value.a === val.a);
+
+ Y.ObjectAssert.areEqual({
+ foo:"bar"
+ }, val.a);
+ },
+
+ testCloneDefaultDeep : function() {
+ var h = new CoreTestsHost(),
+ val = h.get("cloneDefaultDeep");
+
+ Y.Assert.isTrue(CoreTestsHost.ATTRS.cloneDefaultDeep.value !== val);
+ Y.Assert.isTrue(CoreTestsHost.ATTRS.cloneDefaultDeep.value.a !== val.a);
+
+ Y.ObjectAssert.areEqual({
+ foo:"bar"
+ }, val.a);
+ },
+
+ testCloneDefaultOverride : function() {
+ var h = new CoreTestsHost(),
+ val = h.get("cloneDefaultOverride");
+
+ Y.Assert.isTrue(CoreTestsHost.ATTRS.cloneDefaultOverride.value === val);
+
+ Y.ObjectAssert.areEqual({
+ a:1,
+ b:2,
+ c:3
+ }, val);
+ },
+
+ testInitializerDestructorInvocation : function() {
+
+ var expected = ["beforeConstructorTwo", "beforeConstructorOne", "initializerOne", "initializerTwo", "afterConstructorOne", "afterConstructorTwo", "destructorTwo", "destructorOne"],
+ actual = [],
+ initCfg = {
+ foo: 1
+ };
+
+ function One(cfg) {
+ actual.push("beforeConstructorOne");
+ One.superclass.constructor.apply(this, arguments);
+ actual.push("afterConstructorOne");
+ }
+
+ Y.extend(One, Y.BaseCore, {
+ initializer : function(cfg) {
+ Y.Assert.areSame(initCfg, cfg);
+ actual.push("initializerOne");
+ },
+ destructor : function() {
+ actual.push("destructorOne");
+ }
+ }, {
+ NAME : "one",
+ ATTRS : {
+ "a" : {
+ value: 1
+ }
+ }
+ });
+
+ function Two(cfg) {
+ actual.push("beforeConstructorTwo");
+ Two.superclass.constructor.apply(this, arguments);
+ actual.push("afterConstructorTwo");
+ }
+
+ Y.extend(Two, One, {
+ initializer : function(cfg) {
+ Y.Assert.areSame(initCfg, cfg);
+ actual.push("initializerTwo");
+ },
+ destructor : function() {
+ actual.push("destructorTwo");
+ }
+ }, {
+ NAME : "two",
+ ATTRS : {
+ "b" : {
+ value: 2
+ }
+ }
+ });
+
+ var o = new Two(initCfg);
+ o.destroy();
+
+ Y.ArrayAssert.itemsAreEqual(expected, actual);
+
+ }
+ };
+
var basicTemplate = {
- name: "Core Base Class Tests",
+ name: "Base Class Tests",
createHost : function(cfg) {
return new AttrHost(cfg);
@@ -654,7 +865,7 @@
var extendedTemplate = {
- name: "Core Extended Class Tests",
+ name: "Extended Class Tests",
createHost : function(cfg) {
return new ExtendedAttrHost(cfg);
@@ -969,6 +1180,8 @@
var suite = new Y.Test.Suite({name:"Base Core Unit Tests"});
+ suite.add(new Y.Test.Case(coreTemplate));
+
suite.add(new Y.Test.Case(basicTemplate));
suite.add(new Y.Test.Case(extendedTemplate));
View
472 src/base/tests/base.html
@@ -43,9 +43,134 @@
// NOTE: Attribute's unit tests cover a large section of Base's functionality when it comes to dealing with attributes.
+ function EventTests(config) {
+ EventTests.superclass.constructor.apply(this, arguments);
+ }
+
+ EventTests.NAME = "eventTestsHost";
+
+ EventTests.ATTRS = {
+ attr1 : {
+ value: "foo"
+ }
+ };
+
+ Y.extend(EventTests, Y.Base);
+
var suite = new Y.Test.Suite("Base Tests");
suite.add(new Y.Test.Case({
+ name : "Base Event Tests",
+
+ testEventPrefix : function() {
+ var h = new EventTests();
+ Y.Assert.areEqual("eventTestsHost", h._eventPrefix);
+ },
+
+ testEventRegistrationThroughConstructor : function() {
+
+ var expectedEvents = ["OnInit", "AfterInit", "OnAttr1Change", "AfterAttr1Change"];
+ var actualEvents = [];
+
+ var h = new EventTests({
+ on: {
+ "attr1Change" : function() {
+ actualEvents.push("OnAttr1Change");
+ },
+
+ "init" : function() {
+ actualEvents.push("OnInit");
+ }
+ },
+
+ after: {
+ "attr1Change" : function() {
+ actualEvents.push("AfterAttr1Change");
+ },
+
+ "init" : function() {
+ actualEvents.push("AfterInit");
+ }
+ }
+ });
+
+ h.set("attr1", "bar");
+
+ Y.ArrayAssert.itemsAreEqual(expectedEvents, actualEvents);
+ },
+
+ testBubbleTargetsThroughConstructor : function() {
+
+ var expectedEvents = ["bubbleTargetOne", "bubbleTargetTwo", "bubbleTargetThree"];
+ var actualEvents = [];
+
+ var bubbleTargetOne = new Y.EventTarget();
+ bubbleTargetOne.on("eventTestsHost:attr1Change", function() {
+ actualEvents.push("bubbleTargetOne");
+ });
+
+ var bubbleTargetTwo = new Y.EventTarget();
+ bubbleTargetTwo.on("eventTestsHost:attr1Change", function() {
+ actualEvents.push("bubbleTargetTwo");
+ });
+
+ var bubbleTargetThree = new Y.EventTarget();
+ bubbleTargetThree.on("eventTestsHost:attr1Change", function() {
+ actualEvents.push("bubbleTargetThree");
+ });
+
+ var h1 = new EventTests({
+ bubbleTargets : [bubbleTargetOne, bubbleTargetTwo]
+ });
+
+ h1.set("attr1", "bar");
+
+ var h2 = new EventTests({
+ bubbleTargets : bubbleTargetThree
+ });
+
+ h2.set("attr1", "foobar");
+
+ Y.ArrayAssert.itemsAreEqual(expectedEvents, actualEvents);
+ },
+
+ testInitEvent : function() {
+ var actual = [];
+ var expected = ["onInit", "afterInit"];
+
+ var h = new EventTests({
+ on : {
+ init : function() {
+ actual.push("onInit");
+ }
+ },
+ after : {
+ init : function() {
+ actual.push("afterInit");
+ }
+ }
+ });
+
+ Y.ArrayAssert.itemsAreEqual(expected, actual);
+ },
+
+ testDestroyEvent : function() {
+ var actual = [];
+ var expected = ["onDestroy", "afterDestroy"];
+
+ var h = new EventTests();
+
+ h.on("destroy", function() {
+ actual.push("onDestroy");
+ });
+
+ h.after("destroy", function() {
+ actual.push("afterDestroy");
+ });
+ }
+ }));
+
+ suite.add(new Y.Test.Case({
name : "BaseBuild",
@@ -828,6 +953,7 @@
});
var o = new MyClassTwo();
+
o.methodOne();
o.methodTwo();
o.extOne();
@@ -836,6 +962,304 @@
Y.ArrayAssert.itemsAreEqual(expectedMethodCalls, actualMethodCalls, "Unexpected method calls");
},
+ "test:mainclass-statics" : function() {
+
+ function Ext1() {}
+
+ Ext1.prototype.extOne = function() {};
+ Ext1.prototype.initializer = function() {};
+ Ext1.prototype.methodOne = function() {
+ return "methodOne";
+ };
+
+ Ext1.STATIC_ONE = "static_one";
+ Ext1.STATIC_TWO = "static_two";
+ Ext1.STATIC_THREE = "static_three";
+
+ var MyClass = Y.extend(function() {
+ MyClass.superclass.constructor.apply(this, arguments);
+ }, Y.Base, null, {
+ NAME : "myClass",
+ _buildCfg : {
+ statics : ["STATIC_ONE", "STATIC_TWO"]
+ }
+ });
+
+ var MyBuiltClass = Y.Base.create("myBuiltClass", MyClass, [Ext1]);
+
+ var o = new MyBuiltClass();
+
+ Y.Assert.isTrue(o instanceof MyBuiltClass);
+
+ Y.Assert.isFunction(o.methodOne); // prototype properties copied
+ Y.Assert.isFunction(o.init); // but prototype not switched completely by mistake
+
+ Y.Assert.areEqual("static_one", MyBuiltClass.STATIC_ONE);
+ Y.Assert.areEqual("static_two", MyBuiltClass.STATIC_TWO);
+ Y.Assert.isFalse("STATIC_THREE" in MyBuiltClass);
+
+ Y.Assert.isFalse(MyBuiltClass.ATTRS === Ext1.ATTRS, "Ext1.ATTRS shouldn't have been copied over, it should be aggregated");
+ },
+
+ "test:mainclass-statics" : function() {
+
+ function Ext1() {}
+
+ Ext1.prototype.extOne = function() {};
+ Ext1.prototype.initializer = function() {};
+ Ext1.prototype.methodOne = function() {
+ return "methodOne";
+ };
+
+ Ext1.STATIC_ONE = "static_one";
+ Ext1.STATIC_TWO = "static_two";
+ Ext1.STATIC_THREE = "static_three";
+
+ var MyClass = Y.extend(function() {
+ MyClass.superclass.constructor.apply(this, arguments);
+ }, Y.Base, null, {
+ NAME : "myClass",
+ _buildCfg : {
+ statics : ["STATIC_ONE", "STATIC_TWO"]
+ }
+ });
+
+ var MyBuiltClass = Y.Base.create("myBuiltClass", MyClass, [Ext1]);
+
+ var o = new MyBuiltClass();
+
+ Y.Assert.isTrue(o instanceof MyBuiltClass);
+
+ Y.Assert.isFunction(o.methodOne); // prototype properties copied
+ Y.Assert.isFunction(o.init); // but prototype not switched completely by mistake
+
+ Y.Assert.areEqual("static_one", MyBuiltClass.STATIC_ONE);
+ Y.Assert.areEqual("static_two", MyBuiltClass.STATIC_TWO);
+ Y.Assert.isFalse("STATIC_THREE" in MyBuiltClass);
+
+ Y.Assert.isFalse(MyBuiltClass.ATTRS === Ext1.ATTRS, "Ext1.ATTRS shouldn't have been copied over, it should be aggregated");
+ },
+
+ "test:mainclass-aggregates" : function() {
+
+ function Ext1() {}
+ Ext1.AGG = {
+ "foo": true
+ };
+
+ function Ext2() {}
+ Ext2.AGG = {
+ "bar": true
+ };
+
+ var MyClass = Y.extend(function() {
+ MyClass.superclass.constructor.apply(this, arguments);
+ }, Y.Base, null, {
+ NAME : "myClass",
+ _buildCfg : {
+ aggregates : ["AGG"]
+ }
+ });
+
+ var MyBuiltClass = Y.Base.create("myBuiltClass", MyClass, [Ext1, Ext2]);
+
+ var o = new MyBuiltClass();
+
+ Y.ObjectAssert.areEqual({
+ foo:true,
+ bar:true
+ }, MyBuiltClass.AGG);
+ },
+
+ "test:mainclass-custom" : function() {
+
+ function Ext1() {}
+
+ Ext1.prototype.extOne = function() {};
+ Ext1.prototype.methodOne = function() {
+ return "methodOne";
+ };
+
+ Ext1.CUST = {
+ foo: [1],
+ bar: [1]
+ };
+
+ function Ext2() {}
+
+ Ext2.prototype.extTwo = function() {};
+ Ext2.prototype.methodTwo = function() {
+ return "methodOne";
+ };
+
+ Ext2.CUST = {
+ foo: [2, 3],
+ bar: [2, 3, 4]
+ };
+
+ // ---
+
+ var MyClass = Y.extend(function() {
+ MyClass.superclass.constructor.apply(this, arguments);
+ }, Y.Base, null, {
+ NAME : "myClass",
+ _buildCfg : {
+ custom : {
+ CUST : function(p, r, s) {
+
+ r[p] = r[p] || {
+ foo:[],
+ bar:[]
+ };
+
+ if (s[p]) {
+ if (s[p].foo) {
+ r[p].foo = r[p].foo.concat(s[p].foo);
+ }
+ if (s[p].bar) {
+ r[p].bar = r[p].bar.concat(s[p].bar);
+ }
+ }
+
+ }
+ }
+ }
+ });
+
+ var MyClass1 = Y.Base.create("myClass1", MyClass, [Ext1]);
+ var myclass1 = new MyClass1();
+
+ Y.Assert.isTrue(myclass1 instanceof MyClass1);
+
+ Y.Assert.isFunction(myclass1.methodOne);
+ Y.Assert.isFunction(myclass1.init);
+
+ // ObjectAssert.areEqual doesn't work: values don't == compare
+ Y.ObjectAssert.hasKeys({
+ bar:[1],
+ foo:[1]
+ }, MyClass1.CUST, "Class1 - object assert");
+
+ Y.ArrayAssert.itemsAreEqual([1], MyClass1.CUST.foo, "Class1 foo assert");
+ Y.ArrayAssert.itemsAreEqual([1], MyClass1.CUST.bar, "Class1 bar assert");
+
+ Y.Assert.isFalse(MyClass1.CUST === Ext1.CUST, "Ext1.CUST shouldn't have been copied over");
+
+ // ---
+
+ var MyClass2 = Y.Base.create("myClass2", MyClass, [Ext1, Ext2]);
+ var myclass2 = new MyClass2();
+
+ Y.Assert.isTrue(myclass2 instanceof MyClass2);
+
+ Y.Assert.isFunction(myclass2.methodTwo); // prototype properties copied
+ Y.Assert.isFunction(myclass2.init); // but prototype not switched completely by mistake
+
+ // ObjectAssert.areEqual doesn't work: values don't == compare
+ Y.ObjectAssert.hasKeys({
+ foo:[1,2,3],
+ bar:[1,2,3,4]
+ }, MyClass2.CUST);
+
+ Y.ArrayAssert.itemsAreEqual([1,2,3], MyClass2.CUST.foo);
+ Y.ArrayAssert.itemsAreEqual([1,2,3,4], MyClass2.CUST.bar);
+
+ Y.Assert.isFalse(MyClass2.CUST === Ext1.CUST, "Ext1.CUST shouldn't have been copied over");
+ Y.Assert.isFalse(MyClass2.CUST === Ext2.CUST, "Ext2.CUST shouldn't have been copied over");
+ },
+
+ "test:extCfg-custom" : function() {
+
+ function Ext1() {}
+
+ Ext1.prototype.extOne = function() {};
+ Ext1.prototype.methodOne = function() {
+ return "methodOne";
+ };
+
+ Ext1.CUST = {
+ foo: [1],
+ bar: [1]
+ };
+
+ Ext1._buildCfg = {
+ custom : {
+ CUST : function(p, r, s) {
+
+ r[p] = r[p] || {
+ foo:[],
+ bar:[]
+ };
+
+ if (s[p]) {
+ if (s[p].foo) {
+ r[p].foo = r[p].foo.concat(s[p].foo);
+ }
+ if (s[p].bar) {
+ r[p].bar = r[p].bar.concat(s[p].bar);
+ }
+ }
+
+ }
+ }
+ };
+
+ function Ext2() {}
+
+ Ext2.prototype.extTwo = function() {};
+ Ext2.prototype.methodTwo = function() {
+ return "methodOne";
+ };
+
+ Ext2.CUST = {
+ foo: [2, 3],
+ bar: [2, 3, 4]
+ };
+
+ // ---
+
+ var MyClass1 = Y.Base.create("myClass1", Y.Base, [Ext1]);
+ var myclass1 = new MyClass1();
+
+ Y.Assert.isTrue(myclass1 instanceof MyClass1);
+
+ Y.Assert.isFunction(myclass1.methodOne);
+ Y.Assert.isFunction(myclass1.init);
+
+ // ObjectAssert.areEqual doesn't work: values don't == compare
+ Y.ObjectAssert.hasKeys({
+ bar:[1],
+ foo:[1]
+ }, MyClass1.CUST, "Class1 - object assert");
+
+ Y.ArrayAssert.itemsAreEqual([1], MyClass1.CUST.foo, "Class1 foo assert");
+ Y.ArrayAssert.itemsAreEqual([1], MyClass1.CUST.bar, "Class1 bar assert");
+
+ Y.Assert.isFalse(MyClass1.CUST === Ext1.CUST, "Ext1.CUST shouldn't have been copied over");
+
+ // ---
+
+ var MyClass2 = Y.Base.create("myClass2", Y.Base, [Ext1, Ext2]);
+ var myclass2 = new MyClass2();
+
+ Y.Assert.isTrue(myclass2 instanceof MyClass2);
+
+ Y.Assert.isFunction(myclass2.methodTwo); // prototype properties copied
+ Y.Assert.isFunction(myclass2.init); // but prototype not switched completely by mistake
+
+ // ObjectAssert.areEqual doesn't work: values don't == compare
+ Y.ObjectAssert.hasKeys({
+ foo:[1,2,3],
+ bar:[1,2,3,4]
+ }, MyClass2.CUST);
+
+ Y.ArrayAssert.itemsAreEqual([1,2,3], MyClass2.CUST.foo);
+ Y.ArrayAssert.itemsAreEqual([1,2,3,4], MyClass2.CUST.bar);
+
+ Y.Assert.isFalse(MyClass2.CUST === Ext1.CUST, "Ext1.CUST shouldn't have been copied over");
+ Y.Assert.isFalse(MyClass2.CUST === Ext2.CUST, "Ext2.CUST shouldn't have been copied over");
+ },
+
"test:extCfg-statics" : function() {
function Ext1() {}
@@ -1025,6 +1449,54 @@
Y.Assert.isFalse(MyClass2.CUST === Ext1.CUST, "Ext1.CUST shouldn't have been copied over");
Y.Assert.isFalse(MyClass2.CUST === Ext2.CUST, "Ext2.CUST shouldn't have been copied over");
+ },
+
+ "test:deprecated" : function() {
+
+ function Ext1() {}
+
+ Ext1.MY_AGG = {a:1};
+ Ext1.MY_STATICS = "a";
+ Ext1.MY_CUSTOM = "a";
+
+ function Ext2() {}
+
+ Ext2.MY_AGG = {b:2};
+ Ext2.MY_STATICS = "b";
+ Ext2.MY_CUSTOM = "b";
+
+ function Ext3() {}
+
+ Ext3.MY_AGG = {c:3};
+ Ext3.MY_STATICS = "c";
+ Ext3.MY_CUSTOM = "c";
+
+ function MyMainClass(cfg) {
+ MyMainClass.superclass.constructor.apply(this, arguments);
+ }
+
+ var MyBuiltClass = Y.Base.build("foo", MyMainClass, [Ext1, Ext2, Ext3], {
+ dynamic: true,
+ aggregates : ["MY_AGG"],
+ statics : ["MY_STATICS"],
+ custom : {
+ MY_CUSTOM : function(prop, r, s) {
+ r[prop] = r[prop] || "";
+ r[prop] = r[prop] + s[prop];
+ }
+ }
+ });
+
+ Y.ObjectAssert.areEqual({
+ a:1,
+ b:2,
+ c:3
+ }, MyBuiltClass.MY_AGG);
+
+ Y.Assert.areEqual("c", MyBuiltClass.MY_STATICS);
+
+ /* Currently broken. Will fix in 3.6.0pr1. Too late at this point, given the extreme edge caseness. */
+ // Y.Assert.areEqual("abc", MyBuiltClass.MY_CUSTOM);
}
}));
View
594 src/datatable/docs/index.mustache
@@ -1,4 +1,10 @@
<style type="text/css">
+ td code {
+ white-space: nowrap;
+ background: #fcfbfa;
+ border: 1px solid #d0d5ec;
+ padding: 0 3px;
+ }
.yui3-datatable table {
width: auto;
}
@@ -11,6 +17,11 @@
white-space: nowrap;
}
+ .yui3-skin-sam .yui3-datatable-message-content {
+ background: #fff;
+ border-bottom: 0 none;
+ }
+
.notice {
background: #faf3d1;
border: 1px solid #eac9a9;
@@ -588,12 +599,6 @@ var cols = [
<h2 id="data">Table Data Configuration</h2>
<p>
- In each of the examples above, the DataTable's `data` attribute was
- assigned an array of objects. This is a convenient way of initializing the
- table, but not the only way.
-</p>
-
-<p>
Each record in the table is stored as a
<a href="../model/index.html">Model</a> instance, where the
keys of the record objects become Model attributes. This allows you to
@@ -656,7 +661,7 @@ applePie.eatSlice();
```
<p>
- Alternately, `recordType` will accept an array of attribute strings, or an
+ Alternately, `recordType` will accept an array of attribute strings or an
`ATTRS` configuration object to make it easier to create custom attribute
behaviors without needing to explicitly build the Model subclass.
</p>
@@ -708,184 +713,535 @@ var table = new Y.DataTable({
<h3 id="modellist">The `data` ModelList</h3>
<p>
-</p>
-
-<!-- TODO: describe data as array, data as ModelList instance, assigned and dynamic recordTypes -->
-
-<p>
- Integrate with the <a href="../datasource/">DataSource</a> data abstraction
- utility to easily load data from remote sources and implement features such
- as caching and polling.
+ The record Models are stored in a
+ <a href="../model-list/index.html">ModelList</a>, which is assigned to the
+ `data` <em>property</em> on the instance (for easier access than going through `table.get('data')`).
</p>
```
-var cols = [
- "Title",
- "Phone",
- "Rating"
+var records = [
+ { item: "widget", cost: 23.57, price: 47.5 },
+ { item: "gadget", cost: 0.11, price: 6.99 },
+ { item: "sprocket", cost: 4.08, price: 3.75 }
];
-var myDataSource = new Y.DataSource.Get({
- source: "http://query.yahooapis.com/v1/public/yql?&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys"
-});
-
-myDataSource.plug(Y.Plugin.DataSourceJSONSchema, {
- schema: {
- resultListLocator: "query.results.Result",
- resultFields: [
- "Title",
- "Phone",
- {
- key: "Rating",
- locator: "Rating.AverageRating"
- }
- ]
- }
-}),
-
var table = new Y.DataTable({
- columns: cols,
- summary: "Pizza places near 98089"
+ columns: ["item", "cost", "price"],
+ data : records
});
-table.plug(Y.Plugin.DataTableDataSource, {
- datasource: myDataSource
-})
+// Add a new Model using the ModelList API. This will fire
+// events and change the table if rendered.
+table.data.add({ item: "nut", cost: 0.01, price: 0.25 });
+```
-table.render("#pizza");
+<p>
+ When assigning the DataTable's `data` attribute with an array, a ModelList
+ is created for you. But you can also pass a ModelList instance if you are
+ sharing a ModelList between widgets on the page, or you have created custom
+ Model and ModelList classes with additional logic, such as adding a
+ <a href="../model-list/#implementing-a-list-sync-layer">data sync layer</a>.
+</p>
-// Load the data into the table
-table.datasource.load({
- request: "&q=select%20*%20from%20local.search%20where%20zip%3D%2794089%27%20and%20query%3D%27pizza%27"
+```
+var table = new Y.DataTable({
+ columns: ['type', 'slices'],
+ data: new Y.PieList()
});
-// Make another request later
-table.datasource.load({
- request: "&q=select%20*%20from%20local.search%20where%20zip%3D%2794089%27%20and%20query%3D%27chinese%27"
+// The Y.PieList class implements a sync layer, enabling its load() method
+table.data.load(function () {
+ table.render('#pies');
});
```
+<h3 id="getting-data">Getting Remote Table Data</h3>
+
<p>
- Enable DataSource caching.
+ To fetch remote data, you have three options:
</p>
-```
-var cols = [
- "Title",
- "Phone",
- { key: "Rating.AverageRating", label: "Rating" }
-];
+<ol>
+ <li>
+ <p>
+ <strong>For quick one-offs</strong>, you can load and parse the
+ data manually, using `Y.io(...)`, `Y.jsonp(...)`, etc., then assign
+ that data to the DataTable's `data` attribute. This isn't very
+ elegant or maintainable, so is best avoided for anything other than
+ proofs of concept.
+ </p>
+ </li>
+ <li>
+ <p>
+ <strong>For the most control</strong>, better maintainability, and
+ better encapsulation of business logic, create Model and ModelList
+ subclasses that
+ <a href="../model-list/#implementing-a-list-sync-layer">implement a
+ sync layer</a> as suggested above.
+ </p>
+ </li>
+ <li>
+ <p>
+ <strong>For common read-only scenarios</strong>, use the
+ <a href="{{apiDocs}}/classes/Plugin.DataTableDataSource.html">`Y.Plugin.DataTableDataSource`</a>
+ plugin to bind your table to a
+ <a href="../datasource/index.html">`DataSource`</a> instance. Use
+ plugins to add DataSource features.
+ </p>
+ </li>
+</ol>
+
+```
+// Create a JSONP DataSource to query YQL
var myDataSource = new Y.DataSource.Get({
- source: "http://query.yahooapis.com/v1/public/yql?format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys"
+ source: 'http://query.yahooapis.com/v1/public/yql?format=json&' +
+ 'env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&q='
});
-myDataSource
- .plug(Y.Plugin.DataSourceJSONSchema, {
+myDataSource.plug(Y.Plugin.DataSourceJSONSchema, {
schema: {
- resultListLocator: "query.results.Result",
- resultFields: ["Title", "Phone", "Rating.AverageRating"]
+ resultListLocator: 'query.results.Result',
+ resultFields: [
+ 'Title',
+ 'Phone',
+ {
+ // Important that record fields NOT include ".", so
+ // extract nested data with locators
+ key: 'Rating',
+ locator: "Rating.AverageRating"
+ }
+ ]
}
})
.plug(Y.Plugin.DataSourceCache, {
max: 3
});
+// No data is provided at construction because it will load via the
+// DataTableDataSource plugin
var table = new Y.DataTable({
- columns: cols,
- summary: "Pizza places near 98089",
- caption: "Table with JSON data from YQL"
+ columns: ['Title', 'Phone', 'Rating'],
+ summary: 'Pizza places near 98089'
});
-table
- .plug(Y.Plugin.DataTableDataSource, {
- datasource: myDataSource
- })
- .render("#pizza");
+table.plug(Y.Plugin.DataTableDataSource, {
+ datasource: myDataSource
+})
+
+// Initially render an empty table and show a loading message
+table.render('#pizza')
+ .showMessage('loadingMessage');
+// Load the data into the table
table.datasource.load({
- request: "&q=select%20*%20from%20local.search%20where%20zip%3D%2794089%27%20and%20query%3D%27chinese%27"
+ request: encodeURIComponent(
+ 'select *' +
+ ' from local.search' +
+ ' where zip="94089"' +
+ ' and query="pizza"');
});
```
+<h2 id="features">DataTable Modules and Features</h2>
+
+<p>
+ For a basic, stripped down `Y.DataTable` class, include the `datatable-base`
+ module in your `use()`.
+</p>
+
<p>
- Enable DataSource polling.
+ Feature modules, such as `datatable-sort`, will bring in `datatable-base`
+ automatically. By including only feature modules in your `use()`, you will
+ get a `Y.DataTable` that supports specifically those features, without
+ extra code for other features you won't be using.
+</p>
+
+<p>
+ The `datatable` module is a bundle of `datatable-base` plus a set of common
+ feature modules. Other feature modules need to be included explicitly in
+ `use()`.
+</p>
+
+<table>
+<thead>
+ <tr>
+ <th>Module</th>
+ <th>Description</th>
+ <th>In `datatable`?</th>
+ </tr>
+</thead>
+<tbody>
+ <tr>
+ <td><a href="{{apiDocs}}/modules/datatable-core.html">`datatable-core`</a></td>
+ <td>
+ The core API for DataTable, implemented as a class extension, used
+ by `datatable-base` to create `Y.DataTable` and `Y.DataTable.Base`.
+ </td>
+ <td>yes</td>
+ </tr>
+ <tr>
+ <td><a href="{{apiDocs}}/modules/datatable-base.html">`datatable-base`</a></td>
+ <td>
+ Creates the `Y.DataTable` and `Y.DataTable.Base` classes, and
+ defaults the `headerView` and `bodyView` to `Y.DataTable.HeaderView`
+ and `Y.DataTable.BodyView` respectively.
+ </td>
+ <td>yes</td>
+ </tr>
+ <tr>
+ <td><a href="{{apiDocs}}/modules/datatable-head.html">`datatable-head`</a></td>
+ <td>
+ Creates the `Y.DataTable.HeaderView` class as a subclass of
+ `Y.View`. DataTable defers rendering of the `<thead>` content to
+ this View when it is passed as the DataTable's `headerView`
+ attribute (the default, as set by `datatable-base`).
+ </td>
+ <td>yes</td>
+ </tr>
+ <tr>
+ <td><a href="{{apiDocs}}/modules/datatable-body.html">`datatable-body`</a></td>
+ <td>
+ Creates the `Y.DataTable.BodyView` class as a subclass of
+ `Y.View`. DataTable defers rendering of the `<tbody>` content to
+ this View when it is passed as the DataTable's `bodyView`
+ attribute (the default, as set by `datatable-base`).
+ </td>
+ <td>yes</td>
+ </tr>
+ <tr>
+ <td><a href="{{apiDocs}}/modules/datatable-message.html">`datatable-message`</a></td>
+ <td>
+ Creates the `Y.DataTable.Message` class extension and adds
+ `showMessage` and `hideMessage` methods to `Y.DataTable`.
+ </td>
+ <td>yes</td>
+ </tr>
+ <tr>
+ <td><a href="{{apiDocs}}/modules/datatable-column-widths.html">`datatable-column-widths`</a></td>
+ <td>
+ Creates the `Y.DataTable.ColumnWidths` class extension, and adds
+ support for the `width` property in column configuration objects
+ to `Y.DataTable`.
+ </td>
+ <td>yes</td>
+ </tr>
+ <tr>
+ <td><a href="{{apiDocs}}/modules/datatable-mutable.html">`datatable-mutable`</a></td>
+ <td>
+ Creates the `Y.DataTable.Mutable` class extension and adds methods
+ such as `addRow`, `removeRow`, and `moveColumn` to `Y.DataTable`.
+ </td>
+ <td>yes</td>
+ </tr>
+ <tr>
+ <td><a href="{{apiDocs}}/modules/datatable-sort.html">`datatable-sort`</a></td>
+ <td>
+ Creates the `Y.DataTable.Sort` class extension and adds methods
+ `sort` and `toggleSort` as well as attributes `sortable` and
+ `sortBy` to `Y.DataTable`. Enables sorting the table rows by
+ clicking on column headers.
+ </td>
+ <td>yes</td>
+ </tr>
+ <tr>
+ <td><a href="{{apiDocs}}/modules/datatable-datasource.html">`datatable-datasource`</a></td>
+ <td>
+ Creates the `Y.Plugin.DataTableDataSource` plugin for binding a
+ DataSource instance to the table as its source of record data.
+ </td>
+ <td>yes</td>
+ </tr>
+ <tr>
+ <td><a href="{{apiDocs}}/modules/datatable-scroll.html">`datatable-scroll`</a></td>
+ <td>
+ Creates the `Y.DataTable.Scroll` class extension and adds attribute
+ `scrollable` to `Y.DataTable`. Adds support for vertically and/or
+ horizontally scrolling table rows within fixed table dimensions.
+ </td>
+ <td>no</td>
+ </tr>
+</tbody>
+</table>
+
+<h3 id="base">Features in `DataTable.Base`</h3>
+
+<p>
+ By including only `datatable-base` in your `use()` line, you get both
+ `Y.DataTable` and `Y.DataTable.Base` classes. With no other module
+ inclusion, these classes are effectively the same. When additional
+ DataTable related modules are included, those modules' features will
+ usually be added to `Y.DataTable`, but <strong>never</strong> to
+ `Y.DataTable.Base`.
+</p>
+
+<p>
+ Though it can be instantiated, the purpose of `Y.DataTable.Base` is
+ primarily as a superclass to a custom DataTable implementation that has a
+ locked set of features that will not be modified, as `Y.DataTable` can be,
+ by the inclusion of other modules.
</p>
```
-var cols = ["Title", "Phone", "Rating"];
+// Create a custom DataTable that includes only the core set of APIs, plus
+// sorting and message support.
+Y.MyDataTable = Y.Base.create('myTable', Y.DataTable.Base,
+ [ Y.DataTable.Sort, Y.DataTable.Message ]);
-var myDataSource = new Y.DataSource.IO({
- source: "/path/to/service.php?"
-});
+Y.use('datatable-scroll', function (Y) {
+ // Y.DataTable now has support for scrolling
+ var table = new Y.DataTable({ scrollable: 'y', ... });
-myDataSource.plug(Y.Plugin.DataSourceXMLSchema, {
- schema: {
- resultListLocator: "Result",
- resultFields: [
- { key: "Title", locator: "*[local-name() ='Title']" },
- { key: "Phone", locator: "*[local-name() ='Phone']" },
- { key: "Rating", locator: "*[local-name()='Rating']/*[local-name()='AverageRating']" }
- ]
- }
+ // Y.MyDataTable does not (the config does nothing)
+ var myTable = new Y.MyDataTable({ scrollable: 'y', ... });
});
+```
+<p>
+ `Y.DataTable.Base` includes the `columns`, `data`, `caption`, and other
+ basic table attributes, the underlying ModelList and View rendering
+ architecture, as well as methods to fetch rows and cells or columns and
+ records.
+</p>
+
+<h3 id="datatable-message">Table Messages</h3>
+
+<p>
+ The `datatable-message` module adds the ability to display a message in the
+ table body. By default, the "emptyMessage" will display when the table's
+ ModelList has no data records. The message will hide when data is added.
+</p>
+
+```
var table = new Y.DataTable({
- columns: cols,
- summary: "Chinese restaurants near 98089",
- caption: "Table with XML data from same-domain script"
-});
+ columns: ["id", "name", "price"],
+ data: []
+}).render('#example');
+```
-table
- .plug(Y.Plugin.DataTableDataSource, {
- datasource: myDataSource,
- initialRequest: "zip=94089&query=chinese"
- })
- .render("#chinese");
+<p>This code produces this table:</p>
-myDataSource.setInterval(5000, {
- request: "zip=94089&query=chinese",
- callback: {
- success: Y.bind(table.datasource.onDataReturnInitializeTable, table.datasource),
- failure: Y.bind(table.datasource.onDataReturnInitializeTable, table.datasource)
- }
+<div id="message-example" class="yui3-skin-sam"></div>
+
+<script>
+YUI({ filter: 'raw' }).use('datatable-message', function (Y) {
+ var table = new Y.DataTable({
+ columns: ["id", "name", "price"],
+ data: []
+ }).render('#message-example');
});
+</script>
+
+<p>
+ Use `table.showMessage("message")` and `table.hideMessage()` to toggle the
+ message display.
+</p>
+
+<p>
+ `showMessage` supports internationalized strings by using a few named
+ strings, which are registered in the language packs for the
+ `datatable-message` module . These strings are currently:
+</p>
+
+<ul>
+ <li>
+ `table.showMessage("emptyMessage")` defaults to "No data to display".
+ </li>
+ <li>
+ `table.showMessage("loadingMessage")` defaults to "Loading...".
+ </li>
+</ul>
+
+<p>
+ Other values passed to `showMessage` will pass that content directly
+ through to the message Node.
+</p>
+
+<h3 id="colwidths">Column Width Configuration</h3>
+
+<p>
+ The `datatable-column-widths` module adds basic support for specifying
+ column widths.
+</p>
+
+```
+var table = new Y.DataTable({
+ columns: [
+ { key: 'item', width: '125px' },
+ { key: 'cost', formatter: '${value}' },
+ ...
+ ],
+ data : data
+}).render("#example");
```
+<p>This code produces this table:</p>
+
+<div id="colwidths-example" class="yui3-skin-sam"></div>
+
+<script>
+YUI({ filter: 'raw' }).use('datatable-column-widths', function (Y) {
+ var data = [
+ { item: "widget", cost: 23.57, price: 47.5 },
+ { item: "gadget", cost: 0.11, price: 6.99 },
+ { item: "sprocket", cost: 4.08, price: 3.75 },
+ { item: "nut", cost: 0.01, price: 0.25 }
+ ];
+
+ var table = new Y.DataTable({
+ columns: [
+ { key: 'item', width: '125px' },
+ { key: 'cost', formatter: '${value}' },
+ { key: 'price', formatter: '${value}' },
+ { key: 'profit', formatter: function (o) {
+ var price = o.data.price,
+ cost = o.data.cost;
+
+ return (((price - cost) / cost) * 100).toFixed(2) + '%';
+ }
+ }
+ ],
+ data : data
+ }).render("#colwidths-example");
+});
+</script>
+
+<p>
+ <strong>CAVEAT</strong>: Column widths will expand beyond the configured
+ value if column cells contain data that is long and can't line-wrap. Also,
+ column widths may be reduced below the configured value if the table width
+ (by configuring the DataTable's `width` attribute, or constrained by a
+ narrow containing element) is too narrow to fit all data at the configured
+ widths.
+</p>
+
+<p>
+ To force column widths, including cell data truncation and allowing the
+ table to spill beyond its configured or inherited width, wrap the cell
+ content in a `<div>` either by configuring the column's `formatter` or
+ `cellTemplate`, then assign the `<div>`'s CSS style with the desired width
+ (or "inherit"), plus `overflow: hidden;`. Then set the DataTable column's
+ `width` configuration accordingly.
+</p>
+
<h3 id="sorting">Column sorting</h3>
<p>
- Column sorting functionality can be added with the `datatable-sort` module
- (included in the `datatable` rollup module).
- Enable sorting for all columns by setting `sortable` to true during
- instantiation. Alternately, pass `sortable` an array of column keys to
- enable sortability of those specific columns, `false` to disable sorting,
- or the string "auto" (the default) to determine which columns to make
- sortable by reading the `sortable` property from the column configurations.
+ The `datatable-sort` module adds support for sorting the table rows either
+ through the added APIs or by clicking on the table headers.
</p>
+<p>
+ By default, when `datatable-sort` is included, DataTables will inspects
+ the `columns` objects, looking for `sortable: true` to enable table sorting
+ by those columns, triggered by clicking on their respective headers.
+</p>
```
var cols = [
{ key: "Company", sortable: true },
{ key: "Phone" },
{ key: "Contact", sortable: true }
];
+```
-var data = [
- { Company: "Company Bee", Phone: "415-555-1234", Contact: "Sally Spencer"},
- { Company: "Acme Company", Phone: "650-555-4444", Contact: "John Jones"},
- { Company: "Indutrial Industries", Phone: "408-555-5678", Contact: "Robin Smith"}
-];
+<p>
+ For convenience, you can enable header-click sorting for all columns by
+ setting the `sortable` attribute to `true`, or pass an array of column keys
+ to enable just those column's headers.
+</p>
-// Alternately, include sortable: true or sortable: ["Company", "Contact"]
-// in the DataTable configuration
+```
+// Set all columns to be sortable
var table = new Y.DataTable({
- columns: cols,
- data: data,
- summary: "Contacts list",
- caption: "Table with simple column sorting"
-}).render("#sort");
+ columns: ["Company", "Phone", "Contact"],
+ data: ...
+ sortable: true
+}).render("#example");
+```
+
+<p>This code produces this table:</p>
+
+<div id="sort-example1" class="yui3-skin-sam"></div>
+
+<script>
+YUI({ filter: 'raw' }).use('datatable-sort', function (Y) {
+ var table = new Y.DataTable({
+ columns: ['Company', 'Phone', 'Contact'],
+ data: [
+ { Company: "Company Bee", Phone: "415-555-1234", Contact: "Sally Spencer"},
+ { Company: "Acme Company", Phone: "650-555-4444", Contact: "John Jones"},
+ { Company: "Indutrial Industries", Phone: "408-555-5678", Contact: "Robin Smith"}
+ ],
+ sortable: true
+ }).render('#sort-example1');
+});
+</script>
+
+<p>
+ As long as the `datatable-sort` module has been included, you will always
+ be able to sort the table data through the API, even by columns that aren't
+ configured to accept header-click sorting.
+</p>
+
+<p>
+ When a table is sorted, any new records added to the DataTable's ModelList
+ will be inserted at the proper sorted index, as will the created table
+ rows.
+</p>
+
+<h4 id="customsort">Custom Sorting</h4>
+
+<p>
+ Assign a function to a column's `sortFn` to support customized sorting. The
+ function will receive the two records being compared and a boolean flag
+ indicating a descending sort was requested.
+</p>
+
```
+var columns = [
+ {
+ key: 'id',
+ label: '&#9679;', // a big dot
+ formatter: function (o) {
+ return o.value ? '' : '&#9679;'; // only new records have a dot
+ },
+ sortable: true,
+ sortFn: function (a, b, desc) {
+ var aid = a.get('id'),
+ bid = b.get('id'),
+ acid = a.get('clientId'),
+ bcid = b.get('clientId'),
+ order = // existing records are equivalent
+ (aid && bid) ? 0 :
+ // new records are grouped apart from existing records
+ (aid && -1) || (bid && 1) ||
+ // new records are sorted by insertion order
+ (acid > bcid) ? 1 : -(acid < bcid);
+
+ return desc ? -order : order;
+ }
+ },
+ ...
+```
+
+<p>
+ The function must return 1, 0, or -1. 1 specifies that the Model passed as
+ the first parameter should sort below the Model passed as the second
+ parameter. -1 for above, and 0 if they are equivalent for the purpose of
+ this sort.
+</p>
+
+<h4 id="multisort">Multi-Column Sorting</h4>
+shift-click, or by API
+
+<h4 id="multisort">Sorting APIs</h4>
+sort, toggleSort, sortBy
+
+<p>
+ Disable header-click sorting by setting `sortable` to `false`.
+</p>
+
+note sort event, point to events section.
<h3 id="scrolling">Scrolling</h3>
View
12 src/datatable/js/body.js
@@ -249,15 +249,13 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], {
},
/**
- Returns the Model associated to the record `id`, `clientId`, or index (not
- row index). If a DOM element id or Node for a table row or child of a row
- is passed, that will work, too.
+ Returns the Model associated to the row Node or id provided. Passing the
+ Node or id for a descendant of the row also works.
If no Model can be found, `null` is returned.
@method getRecord
- @param {Number|String|Node} seed Record `id`, `clientId`, index, Node, or
- identifier for a row or child element
+ @param {String|Node} seed Row Node or `id`, or one for a descendant of a row
@return {Model}
@since 3.5.0
**/
@@ -269,9 +267,7 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], {
if (tbody) {
if (isString(seed)) {
- if (!record) {
- seed = tbody.one('#' + seed);
- }
+ seed = tbody.one('#' + seed);
}
if (Y.instanceOf(seed, Y.Node)) {
View
2 src/dial/docs/duck.mustache
@@ -19,7 +19,7 @@
{{>duck-markup}}
```
<h3>The JavaScript</h3>
-<p>The stepsPerRevolution attribute of the `Dial`
+<p>The `stepsPerRevolution` attribute of the `Dial`
is given a value equal to the number of frames in the sprite.
</p>
<p>
View
4 src/dial/docs/index.mustache
@@ -93,12 +93,12 @@ dial.render("#demo");
```
<h4>Attributes</h4>
- <p>The following configuration properties are provided to define default values for each Dial widget:</p>
+ <p>The following configuration attributes are provided to define default values for each Dial widget:</p>
<table>
<thead>
<tr>
- <th>Property</th>
+ <th>Attribute</th>
<th>Description</th>
<th>Default</th>
</tr>
View
70 src/dom/js/selector-css2.js
@@ -20,11 +20,6 @@ var PARENT_NODE = 'parentNode',
SelectorCSS2 = {
_reRegExpTokens: /([\^\$\?\[\]\*\+\-\.\(\)\|\\])/,
SORT_RESULTS: true,
- _re: {
- attr: /(\[[^\]]*\])/g,
- esc: /\\[:\[\]\(\)#\.\'\>+~"]/gi,
- pseudos: /(\([^\)]*\))/g
- },
// TODO: better detection, document specific
_isXML: (function() {
@@ -317,7 +312,7 @@ var PARENT_NODE = 'parentNode',
*/
_tokenize: function(selector) {
selector = selector || '';
- selector = Selector._replaceShorthand(Y.Lang.trim(selector));
+ selector = Selector._parseSelector(Y.Lang.trim(selector));
var token = Selector._getToken(), // one token per simple selector (left selector holds combinator)
query = selector, // original query for debug report
tokens = [], // array of tokens
@@ -378,58 +373,43 @@ var PARENT_NODE = 'parentNode',
return tokens;
},
- _replaceShorthand: function(selector) {
- var shorthand = Selector.shorthand,
- esc = selector.match(Selector._re.esc), // pull escaped colon, brackets, etc.
- attrs,
- pseudos,
- re, i, len;
-
- if (esc) {
- selector = selector.replace(Selector._re.esc, '\uE000');
- }
-
- pseudos = selector.match(Selector._re.pseudos);
-
- if (pseudos) {
- selector = selector.replace(Selector._re.pseudos, '\uE002');
- }
+ _replaceMarkers: function(selector) {
+ selector = selector.replace(/\[/g, '\uE003');
+ selector = selector.replace(/\]/g, '\uE004');
- attrs = selector.match(Selector._re.attr);
+ selector = selector.replace(/\(/g, '\uE005');
+ selector = selector.replace(/\)/g, '\uE006');
+ return selector;
+ },
- if (attrs) {
- selector = selector.replace(Selector._re.attr, '\uE001');
- }
+ _replaceShorthand: function(selector) {
+ var shorthand = Y.Selector.shorthand,
+ re;
for (re in shorthand) {
if (shorthand.hasOwnProperty(re)) {
selector = selector.replace(new RegExp(re, 'gi'), shorthand[re]);
}
}
- if (attrs) {
- for (i = 0, len = attrs.length; i < len; ++i) {
- selector = selector.replace(/\uE001/, attrs[i]);
- }
- }
+ return selector;
+ },
- if (pseudos) {
- for (i = 0, len = pseudos.length; i < len; ++i) {
- selector = selector.replace(/\uE002/, pseudos[i]);
- }
- }
+ _parseSelector: function(selector) {
+ var replaced = Y.Selector._replaceSelector(selector),
+ selector = replaced.selector;
- selector = selector.replace(/\[/g, '\uE003');
- selector = selector.replace(/\]/g, '\uE004');
+ // replace shorthand (".foo, #bar") after pseudos and attrs
+ // to avoid replacing unescaped chars
+ selector = Y.Selector._replaceShorthand(selector);
- selector = selector.replace(/\(/g, '\uE005');
- selector = selector.replace(/\)/g, '\uE006');
+ selector = Y.Selector._restore('attr', selector, replaced.attrs);
+ selector = Y.Selector._restore('pseudo', selector, replaced.pseudos);
- if (esc) {
- for (i = 0, len = esc.length; i < len; ++i) {
- selector = selector.replace('\uE000', esc[i]);
- }
- }
+ // replace braces and parens before restoring escaped chars
+ // to avoid replacing ecaped markers
+ selector = Y.Selector._replaceMarkers(selector);
+ selector = Y.Selector._restore('esc', selector, replaced.esc);
return selector;
},
View
84 src/dom/js/selector-native.js
@@ -19,6 +19,23 @@ var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
OWNER_DOCUMENT = 'ownerDocument';
var Selector = {
+ _types: {
+ esc: {
+ token: '\uE000',
+ re: /\\[:\[\]\(\)#\.\'\>+~"]/gi
+ },
+
+ attr: {
+ token: '\uE001',
+ re: /(\[[^\]]*\])/g
+ },
+
+ pseudo: {
+ token: '\uE002',
+ re: /(\([^\)]*\))/g
+ }
+ },
+
useNative: true,
_escapeId: function(id) {
@@ -140,10 +157,55 @@ var Selector = {
},
+ _replaceSelector: function(selector) {
+ var esc = Y.Selector._parse('esc', selector), // pull escaped colon, brackets, etc.
+ attrs,
+ pseudos;
+