From 9ca9a7ac1b30b592586e3aeea10cf941209540cd Mon Sep 17 00:00:00 2001 From: YUI Builder Date: Fri, 23 Mar 2012 11:08:17 -0700 Subject: [PATCH] Gallery Build Tag: gallery-2012.03.23-18-00 --- .../sam/gallery-accordion-horiz-vert-skin.css | 1 + .../sam/gallery-accordion-horiz-vert.css | 2 +- .../gallery-accordion-horiz-vert-debug.js | 2 +- .../gallery-accordion-horiz-vert-min.js | 2 +- .../gallery-accordion-horiz-vert.js | 2 +- .../gallery-anim-class-debug.js | 216 +++ .../gallery-anim-class-min.js | 1 + .../gallery-anim-class/gallery-anim-class.js | 216 +++ .../gallery-any-base-converter-debug.js | 164 +++ .../gallery-any-base-converter-min.js | 1 + .../gallery-any-base-converter.js | 164 +++ .../gallery-array-iterate-debug.js | 48 + .../gallery-array-iterate-min.js | 1 + .../gallery-array-iterate.js | 48 + .../gallery-async-command-timeout-debug.js | 85 ++ .../gallery-async-command-timeout-min.js | 1 + .../gallery-async-command-timeout.js | 85 ++ .../gallery-async-progress-debug.js | 55 + .../gallery-async-progress-min.js | 1 + .../gallery-async-progress.js | 55 + .../skins/sam/gallery-bulkedit-skin.css | 1 + .../gallery-bulkedit-debug.js | 9 +- .../gallery-bulkedit/gallery-bulkedit-min.js | 4 +- build/gallery-bulkedit/gallery-bulkedit.js | 9 +- .../gallery-carousel-anim-debug.js | 4 +- .../gallery-carousel-anim-min.js | 2 +- .../gallery-carousel-anim.js | 4 +- .../assets/gallery-carousel-circular.css | 48 + .../gallery-carousel-circular-debug.js | 224 +++ .../gallery-carousel-circular-min.js | 1 + .../gallery-carousel-circular.js | 215 +++ .../assets/skins/sam/gallery-carousel.css | 2 +- .../gallery-carousel-debug.js | 36 +- .../gallery-carousel/gallery-carousel-min.js | 4 +- build/gallery-carousel/gallery-carousel.js | 36 +- .../assets/gallery-console-test-core.css | 1 + .../assets/skins/sam/gallery-console-test.css | 2 +- .../gallery-console-test-debug.js | 2 +- .../gallery-console-test-min.js | 2 +- .../gallery-console-test.js | 2 +- .../gallery-datatable-350-preview-debug.js | 603 ++++++-- .../gallery-datatable-350-preview-min.js | 13 +- .../gallery-datatable-350-preview.js | 603 ++++++-- .../gallery-dimensions-debug.js | 5 +- .../gallery-dimensions-min.js | 2 +- .../gallery-dimensions/gallery-dimensions.js | 5 +- .../gallery-expiration-cache-debug.js | 191 +++ .../gallery-expiration-cache-min.js | 1 + .../gallery-expiration-cache.js | 191 +++ .../gallery-formmgr/gallery-formmgr-debug.js | 13 +- build/gallery-formmgr/gallery-formmgr-min.js | 4 +- build/gallery-formmgr/gallery-formmgr.js | 13 +- .../gallery-funcprog-debug.js | 15 +- .../gallery-funcprog/gallery-funcprog-min.js | 2 +- build/gallery-funcprog/gallery-funcprog.js | 15 +- .../gallery-instancemanager-debug.js | 32 +- .../gallery-instancemanager-min.js | 2 +- .../gallery-instancemanager.js | 32 +- .../gallery-intl-arabicnumerals-debug.js | 33 + .../gallery-intl-arabicnumerals-min.js | 1 + .../gallery-intl-arabicnumerals.js | 33 + .../gallery-iterable-extras-debug.js | 259 ++++ .../gallery-iterable-extras-min.js | 1 + .../gallery-iterable-extras.js | 259 ++++ .../gallery-layout-cols-debug.js | 383 +++++ .../gallery-layout-cols-min.js | 1 + .../gallery-layout-cols.js | 383 +++++ .../gallery-layout-rows-debug.js | 403 ++++++ .../gallery-layout-rows-min.js | 1 + .../gallery-layout-rows.js | 403 ++++++ .../assets/gallery-layout-core.css | 46 + .../assets/skins/sam/gallery-layout-skin.css | 1 + .../assets/skins/sam/gallery-layout.css | 1 + build/gallery-layout/gallery-layout-debug.js | 1126 +++++++++++++++ build/gallery-layout/gallery-layout-min.js | 2 + build/gallery-layout/gallery-layout.js | 1125 +++++++++++++++ .../gallery-lazy-load-debug.js | 79 + .../gallery-lazy-load-min.js | 1 + build/gallery-lazy-load/gallery-lazy-load.js | 79 + .../gallery-linkedlist-debug.js | 675 +++++++++ .../gallery-linkedlist-min.js | 1 + .../gallery-linkedlist/gallery-linkedlist.js | 675 +++++++++ .../gallery-md-model-debug.js | 1268 +++++++++++++++++ .../gallery-md-model/gallery-md-model-min.js | 2 + build/gallery-md-model/gallery-md-model.js | 1268 +++++++++++++++++ .../gallery-model-list-difference-debug.js | 73 + .../gallery-model-list-difference-min.js | 1 + .../gallery-model-list-difference.js | 73 + .../gallery-model-sync-rest-debug.js | 161 ++- .../gallery-model-sync-rest-min.js | 2 +- .../gallery-model-sync-rest.js | 161 ++- .../gallery-model-sync-yql-debug.js | 64 +- .../gallery-model-sync-yql-min.js | 2 +- .../gallery-model-sync-yql.js | 64 +- .../gallery-mru-cache-debug.js | 196 +++ .../gallery-mru-cache-min.js | 1 + build/gallery-mru-cache/gallery-mru-cache.js | 196 +++ .../gallery-node-optimizations-debug.js | 91 +- .../gallery-node-optimizations-min.js | 2 +- .../gallery-node-optimizations.js | 78 +- .../gallery-nodelist-extras2-debug.js | 15 +- .../gallery-nodelist-extras2-min.js | 2 +- .../gallery-nodelist-extras2.js | 15 +- .../gallery-object-extras-debug.js | 49 +- .../gallery-object-extras-min.js | 2 +- .../gallery-object-extras.js | 49 +- .../skins/sam/gallery-paginator-skin.css | 56 +- .../assets/skins/sam/gallery-paginator.css | 2 +- .../gallery-paginator-debug.js | 44 +- .../gallery-paginator-min.js | 2 +- build/gallery-paginator/gallery-paginator.js | 44 +- .../gallery-picklist-debug.js | 1076 ++++++++++++++ .../gallery-picklist/gallery-picklist-min.js | 2 + build/gallery-picklist/gallery-picklist.js | 1076 ++++++++++++++ .../assets/gallery-querybuilder-core.css | 1 + .../gallery-querybuilder-debug.js | 2 +- .../gallery-querybuilder-min.js | 2 +- .../gallery-querybuilder.js | 2 +- .../assets/gallery-quickedit-core.css | 1 + .../assets/skins/sam/gallery-quickedit.css | 2 +- .../gallery-quickedit-debug.js | 271 ++-- .../gallery-quickedit-min.js | 6 +- build/gallery-quickedit/gallery-quickedit.js | 271 ++-- build/gallery-soon/gallery-soon-debug.js | 257 ++++ build/gallery-soon/gallery-soon-min.js | 7 + build/gallery-soon/gallery-soon.js | 257 ++++ .../assets/gallery-timeline-core.css | 5 + .../assets/skins/sam/gallery-timeline.css | 2 +- .../gallery-timeline-debug.js | 185 ++- .../gallery-timeline/gallery-timeline-min.js | 4 +- build/gallery-timeline/gallery-timeline.js | 185 ++- .../gallery-timeline/lang/gallery-timeline.js | 2 +- .../lang/gallery-timeline_en.js | 2 +- .../lang/gallery-timeline_es.js | 2 +- .../assets/gallery-treeble-core.css | 1 + .../assets/skins/sam/gallery-treeble.css | 2 +- .../gallery-treeble/gallery-treeble-debug.js | 58 +- build/gallery-treeble/gallery-treeble-min.js | 4 +- build/gallery-treeble/gallery-treeble.js | 58 +- .../gallery-widget-inherit-css-debug.js | 44 + .../gallery-widget-inherit-css-min.js | 1 + .../gallery-widget-inherit-css.js | 44 + 142 files changed, 16188 insertions(+), 1098 deletions(-) create mode 100644 build/gallery-anim-class/gallery-anim-class-debug.js create mode 100644 build/gallery-anim-class/gallery-anim-class-min.js create mode 100644 build/gallery-anim-class/gallery-anim-class.js create mode 100644 build/gallery-any-base-converter/gallery-any-base-converter-debug.js create mode 100644 build/gallery-any-base-converter/gallery-any-base-converter-min.js create mode 100644 build/gallery-any-base-converter/gallery-any-base-converter.js create mode 100644 build/gallery-array-iterate/gallery-array-iterate-debug.js create mode 100644 build/gallery-array-iterate/gallery-array-iterate-min.js create mode 100644 build/gallery-array-iterate/gallery-array-iterate.js create mode 100644 build/gallery-async-command-timeout/gallery-async-command-timeout-debug.js create mode 100644 build/gallery-async-command-timeout/gallery-async-command-timeout-min.js create mode 100644 build/gallery-async-command-timeout/gallery-async-command-timeout.js create mode 100644 build/gallery-async-progress/gallery-async-progress-debug.js create mode 100644 build/gallery-async-progress/gallery-async-progress-min.js create mode 100644 build/gallery-async-progress/gallery-async-progress.js create mode 100644 build/gallery-carousel-circular/assets/gallery-carousel-circular.css create mode 100644 build/gallery-carousel-circular/gallery-carousel-circular-debug.js create mode 100644 build/gallery-carousel-circular/gallery-carousel-circular-min.js create mode 100644 build/gallery-carousel-circular/gallery-carousel-circular.js create mode 100644 build/gallery-expiration-cache/gallery-expiration-cache-debug.js create mode 100644 build/gallery-expiration-cache/gallery-expiration-cache-min.js create mode 100644 build/gallery-expiration-cache/gallery-expiration-cache.js create mode 100644 build/gallery-intl-arabicnumerals/gallery-intl-arabicnumerals-debug.js create mode 100644 build/gallery-intl-arabicnumerals/gallery-intl-arabicnumerals-min.js create mode 100644 build/gallery-intl-arabicnumerals/gallery-intl-arabicnumerals.js create mode 100644 build/gallery-iterable-extras/gallery-iterable-extras-debug.js create mode 100644 build/gallery-iterable-extras/gallery-iterable-extras-min.js create mode 100644 build/gallery-iterable-extras/gallery-iterable-extras.js create mode 100644 build/gallery-layout-cols/gallery-layout-cols-debug.js create mode 100644 build/gallery-layout-cols/gallery-layout-cols-min.js create mode 100644 build/gallery-layout-cols/gallery-layout-cols.js create mode 100644 build/gallery-layout-rows/gallery-layout-rows-debug.js create mode 100644 build/gallery-layout-rows/gallery-layout-rows-min.js create mode 100644 build/gallery-layout-rows/gallery-layout-rows.js create mode 100644 build/gallery-layout/assets/gallery-layout-core.css create mode 100644 build/gallery-layout/assets/skins/sam/gallery-layout-skin.css create mode 100644 build/gallery-layout/assets/skins/sam/gallery-layout.css create mode 100644 build/gallery-layout/gallery-layout-debug.js create mode 100644 build/gallery-layout/gallery-layout-min.js create mode 100644 build/gallery-layout/gallery-layout.js create mode 100644 build/gallery-lazy-load/gallery-lazy-load-debug.js create mode 100644 build/gallery-lazy-load/gallery-lazy-load-min.js create mode 100644 build/gallery-lazy-load/gallery-lazy-load.js create mode 100644 build/gallery-linkedlist/gallery-linkedlist-debug.js create mode 100644 build/gallery-linkedlist/gallery-linkedlist-min.js create mode 100644 build/gallery-linkedlist/gallery-linkedlist.js create mode 100644 build/gallery-md-model/gallery-md-model-debug.js create mode 100644 build/gallery-md-model/gallery-md-model-min.js create mode 100644 build/gallery-md-model/gallery-md-model.js create mode 100644 build/gallery-model-list-difference/gallery-model-list-difference-debug.js create mode 100644 build/gallery-model-list-difference/gallery-model-list-difference-min.js create mode 100644 build/gallery-model-list-difference/gallery-model-list-difference.js create mode 100644 build/gallery-mru-cache/gallery-mru-cache-debug.js create mode 100644 build/gallery-mru-cache/gallery-mru-cache-min.js create mode 100644 build/gallery-mru-cache/gallery-mru-cache.js create mode 100644 build/gallery-picklist/gallery-picklist-debug.js create mode 100644 build/gallery-picklist/gallery-picklist-min.js create mode 100644 build/gallery-picklist/gallery-picklist.js create mode 100644 build/gallery-soon/gallery-soon-debug.js create mode 100644 build/gallery-soon/gallery-soon-min.js create mode 100644 build/gallery-soon/gallery-soon.js create mode 100644 build/gallery-widget-inherit-css/gallery-widget-inherit-css-debug.js create mode 100644 build/gallery-widget-inherit-css/gallery-widget-inherit-css-min.js create mode 100644 build/gallery-widget-inherit-css/gallery-widget-inherit-css.js diff --git a/build/gallery-accordion-horiz-vert/assets/skins/sam/gallery-accordion-horiz-vert-skin.css b/build/gallery-accordion-horiz-vert/assets/skins/sam/gallery-accordion-horiz-vert-skin.css index e69de29bb2..e5aa092a43 100644 --- a/build/gallery-accordion-horiz-vert/assets/skins/sam/gallery-accordion-horiz-vert-skin.css +++ b/build/gallery-accordion-horiz-vert/assets/skins/sam/gallery-accordion-horiz-vert-skin.css @@ -0,0 +1 @@ +/* nothing to see here */ \ No newline at end of file diff --git a/build/gallery-accordion-horiz-vert/assets/skins/sam/gallery-accordion-horiz-vert.css b/build/gallery-accordion-horiz-vert/assets/skins/sam/gallery-accordion-horiz-vert.css index b991e84873..bcd060d349 100644 --- a/build/gallery-accordion-horiz-vert/assets/skins/sam/gallery-accordion-horiz-vert.css +++ b/build/gallery-accordion-horiz-vert/assets/skins/sam/gallery-accordion-horiz-vert.css @@ -1 +1 @@ -.yui3-accordion-horiz{position:relative;overflow:hidden}.yui3-accordion-horiz .yui3-accordion-title{position:relative;overflow:hidden;float:left}.yui3-accordion-horiz .yui3-accordion-section-clip{position:relative;overflow:hidden;float:left}.yui3-accordion-horiz .yui3-accordion-section{position:relative;overflow-x:hidden;overflow-y:auto}.yui3-accordion-vert{position:relative;overflow:hidden}.yui3-accordion-vert .yui3-accordion-title{position:relative;overflow:hidden;width:100%}.yui3-accordion-vert .yui3-accordion-section-clip{position:relative;overflow:hidden;width:100%}.yui3-accordion-vert .yui3-accordion-section{position:relative;width:100%;overflow:auto}.yui3-accordion ul.yui3-accordion-content{margin:0;border:0;padding:0} +.yui3-accordion-horiz{position:relative;overflow:hidden}.yui3-accordion-horiz .yui3-accordion-title{position:relative;overflow:hidden;float:left}.yui3-accordion-horiz .yui3-accordion-section-clip{position:relative;overflow:hidden;float:left}.yui3-accordion-horiz .yui3-accordion-section{position:relative;overflow-x:hidden;overflow-y:auto}.yui3-accordion-vert{position:relative;overflow:hidden}.yui3-accordion-vert .yui3-accordion-title{position:relative;overflow:hidden;width:100%}.yui3-accordion-vert .yui3-accordion-section-clip{position:relative;overflow:hidden;width:100%}.yui3-accordion-vert .yui3-accordion-section{position:relative;width:100%;overflow:auto}.yui3-accordion ul.yui3-accordion-content{margin:0;border:0;padding:0}#yui3-css-stamp.skin-sam-gallery-accordion-horiz-vert{display:none} diff --git a/build/gallery-accordion-horiz-vert/gallery-accordion-horiz-vert-debug.js b/build/gallery-accordion-horiz-vert/gallery-accordion-horiz-vert-debug.js index 2ce05c728a..6600634400 100644 --- a/build/gallery-accordion-horiz-vert/gallery-accordion-horiz-vert-debug.js +++ b/build/gallery-accordion-horiz-vert/gallery-accordion-horiz-vert-debug.js @@ -1245,4 +1245,4 @@ Y.namespace("Plugin"); Y.Plugin.FixedSizeAccordion = FixedSizeAccordionPlugin; -}, 'gallery-2011.07.06-19-30' ,{skinnable:true, optional:['anim-base'], requires:['widget','selector-css3','plugin','gallery-dimensions']}); +}, 'gallery-2012.03.23-18-00' ,{skinnable:true, optional:['anim-base'], requires:['widget','selector-css3','plugin','gallery-dimensions']}); diff --git a/build/gallery-accordion-horiz-vert/gallery-accordion-horiz-vert-min.js b/build/gallery-accordion-horiz-vert/gallery-accordion-horiz-vert-min.js index 94be4b82a8..3f19be223e 100644 --- a/build/gallery-accordion-horiz-vert/gallery-accordion-horiz-vert-min.js +++ b/build/gallery-accordion-horiz-vert/gallery-accordion-horiz-vert-min.js @@ -1,2 +1,2 @@ YUI.add("gallery-accordion-horiz-vert",function(b){var l=(0 div:nth-child(1)");},sections:function(r){return r.all("li > div:nth-child(2)");}};var a=b.ClassNameManager.getClassName(m.NAME,"open");var f=b.ClassNameManager.getClassName(m.NAME,"closed");function o(r){b.Event.purgeElement(r,true);while(r.hasChildNodes()){r.removeChild(r.lastChild);}}b.extend(m,b.Widget,{initializer:function(r){this.section_list=[];this.get("allowAllClosed");if(this.get("horizontal")){this.slide_style_name="width";this.slide_size_name="offsetWidth";this.fixed_style_name="height";this.fixed_size_name="offsetHeight";}else{this.slide_style_name="height";this.slide_size_name="offsetHeight";this.fixed_style_name="width";this.fixed_size_name="offsetWidth";}this.after("allowMultipleOpenChange",function(s){if(this.section_list&&this.section_list.length>0&&!s.newVal){this.closeAllSections();}});this.after("allowAllClosedChange",function(s){if(this.section_list&&this.section_list.length>0&&!s.newVal&&this.allSectionsClosed()){this.toggleSection(0);}});},renderUI:function(){this.get("boundingBox").addClass(this.getClassName(this.get("horizontal")?"horiz":"vert"));var u=this.get("titles");if(b.Lang.isString(u)){u=b.all(u);}var v=this.get("sections");if(b.Lang.isString(v)){v=b.all(v);}if(u instanceof b.NodeList&&v instanceof b.NodeList&&u.size()==v.size()){var s=this.get("animateInsertRemove");this.set("animateInsertRemove",this.get("animateRender"));var t=u.size();for(var r=0;r li").remove();},getSectionCount:function(){return this.section_list.length;},getTitle:function(r){return this.section_list[r].title;},setTitle:function(r,w){var s=this.section_list[r].title;o(s);var u;if(b.Lang.isString(w)){var u=b.one(w);if(!u){s.set("innerHTML",w);}}else{u=w;}if(u&&this.get("replaceTitleContainer")){var v=s.get("parentNode");var x=s.get("nextSibling");v.removeChild(s);if(x){v.insertBefore(u,x);}else{v.appendChild(u);}this.section_list[r].title=u;u.addClass(this.getClassName("title"));u.addClass(this.section_list[r].open?a:f);}else{if(u){s.appendChild(u);}}if(l){s.setStyle("display",s.get("innerHTML")?"":"none");}},getSection:function(r){return this.section_list[r].content;},setSection:function(r,t){var w=this.section_list[r].content;o(w);var s;if(b.Lang.isString(t)){var s=b.one(t);if(!s){w.set("innerHTML",t);}}else{s=t;}if(s&&this.get("replaceSectionContainer")){var v=w.getStyle("display");var u=w.get("parentNode");var x=w.get("nextSibling");u.removeChild(w);if(x){u.insertBefore(s,x);}else{u.appendChild(s);}this.section_list[r].content=s;s.addClass(this.getClassName("section"));s.addClass(this.section_list[r].open?a:f);s.setStyle("display",v);}else{if(s){w.appendChild(s);}}},_getClip:function(r){return this.section_list[r].clip;},prependSection:function(s,r){return this.insertSection(0,s,r);},appendSection:function(s,r){return this.insertSection(this.section_list.length,s,r);},insertSection:function(u,y,v){this.fire("beforeInsert",u);var z=b.Node.create("
");z.addClass(this.getClassName("title"));z.addClass(f);var x=b.Node.create("
");x.addClass(this.getClassName("section-clip"));x.setStyle(this.slide_style_name,q+"px");if(this.get("animateOpenClose")){x.setStyle("opacity",0);}var w=b.Node.create("
");w.addClass(this.getClassName("section"));w.addClass(f);w.setStyle("display","none");x.appendChild(w);this.section_list.splice(u,0,{title:z,clip:x,content:w,open:false,anim:null});if(uAdds CSS class animation to Y.Anim, so you can specify cssClass in + * from and/or to. At the end of the animation, the from class is replaced + * by the to class, and all the individual styles used during the animation + * are removed.

+ * + *

Explicit entries in from or to override values set by cssClass.

+ * + * @module gallery-anim-class + */ + +var css_attribute = +[ + "top","bottom","left","right","width","height", + "maxHeight","maxWidth","minHeight","minWidth", + + "color","fontSize","fontSizeAdjust","fontWeight", + "textIndent","textShadow","wordSpacing", + + "backgroundColor","backgroundPosition","backgroundSize", + "outlineColor","outlineWidth", + + "marginTop","marginRight","marginBottom","marginLeft", + + "borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth", + "borderTopLeftRadius","borderTopRightRadius","borderBottomLeftRadius","borderBottomRightRadius", + "borderTopColor","borderRightColor","borderBottomColor","borderLeftColor", + "borderSpacing", + + "paddingTop","paddingRight","paddingBottom","paddingLeft", + + "zIndex","opacity", + + "boxShadow", + "letterSpacing","lineHeight", + "markerOffset", + "orphans","widows", + "size", + + "fillOpacity", + "outlineOffset", + "floodColor","floodOpacity","lightingColor","stopColor","stopOpacity", + "stroke","strokeDashoffset","strokeMiterlimit","strokeOpacity","strokeWidth" +]; + +function updateBehaviors() +{ + if (!Y.Anim.behaviors.outlineColor) + { + Y.Anim.behaviors.outlineColor = Y.Anim.behaviors.color; + } +} + +function getStyles(node) +{ + return Y.map(css_attribute, function(attr) + { + return node.getStyle(attr); + }); +} + +function isValidAttributeValue(s) +{ + return /[#0-9]/.test(s); // # covers colors like #AABBCC +} + +function initialState(node, from, to) +{ + if (to.cssClass) + { + node.removeClass(to.cssClass); + } + if (from.cssClass) + { + node.addClass(from.cssClass); + } +} + +function finalState(node, from, to) +{ + if (from.cssClass) + { + node.removeClass(from.cssClass); + } + if (to.cssClass) + { + node.addClass(to.cssClass); + } +} + +var orig_start = Y.Anim.prototype._start; +Y.Anim.prototype._start = function() +{ + var node = this.get('node'), + from = this.get('from') || {}, + to = this.get('to') || {}; + + updateBehaviors(); // patch after anim extensions are loaded + + delete this._class_diff_attr; + if (from.cssClass || to.cssClass) + { + finalState(node, from, to); + var new_style = getStyles(node); + + // second, so initial state is correct in forward case + + initialState(node, from, to); + var orig_style = getStyles(node); + + if (this.get('reverse')) + { + finalState(node, from, to); + } + + this._class_diff_attr = + { + fromClass: from.cssClass, + from: [], + toClass: to.cssClass, + to: [] + }; + Y.each(new_style, function(style, i) + { + var orig = orig_style[i]; + if (style !== orig) + { + var attr = css_attribute[i]; + if (!from[attr] && isValidAttributeValue(orig)) + { + this._class_diff_attr.from.push(attr); + from[attr] = orig; + } + if (!to[attr] && isValidAttributeValue(style)) + { + this._class_diff_attr.to.push(attr); + to[attr] = style; + } + } + }, + this); + + delete from.cssClass; + this.set('from', from); + + delete to.cssClass; + this.set('to', to); + } + + orig_start.apply(this, arguments); +}; + +var orig_runFrame = Y.Anim.prototype._runFrame; +Y.Anim.prototype._runFrame = function() +{ + // The first frame doesn't happen immediately, so _start() has to leave + // the original class in place. + + var reverse = this.get('reverse'); + if (!reverse && this._class_diff_attr && this._class_diff_attr.fromClass) + { + this.get('node').removeClass(this._class_diff_attr.fromClass); + } + else if (reverse && this._class_diff_attr && this._class_diff_attr.toClass) + { + this.get('node').removeClass(this._class_diff_attr.toClass); + } + + orig_runFrame.apply(this, arguments); +}; + +var orig_end = Y.Anim.prototype._end; +Y.Anim.prototype._end = function() +{ + if (this._class_diff_attr) + { + var node = this.get('node'), + from = this.get('from') || {}, + to = this.get('to') || {}; + + Y.each(this._class_diff_attr.from, function(attr) + { + delete from[attr]; + }); + + from.cssClass = this._class_diff_attr.fromClass; + this.set('from', from); + + Y.each(this._class_diff_attr.to, function(attr) + { + delete to[attr]; + node.setStyle(attr, ''); + }); + + to.cssClass = this._class_diff_attr.toClass; + this.set('to', to); + + if (this.get('reverse')) + { + initialState(node, from, to); + } + else + { + finalState(node, from, to); + } + } + + orig_end.apply(this, arguments); +}; + + +}, 'gallery-2012.03.23-18-00' ,{requires:['anim-base','node-style','gallery-funcprog']}); diff --git a/build/gallery-anim-class/gallery-anim-class-min.js b/build/gallery-anim-class/gallery-anim-class-min.js new file mode 100644 index 0000000000..4b351b7ce5 --- /dev/null +++ b/build/gallery-anim-class/gallery-anim-class-min.js @@ -0,0 +1 @@ +YUI.add("gallery-anim-class",function(b){var i=["top","bottom","left","right","width","height","maxHeight","maxWidth","minHeight","minWidth","color","fontSize","fontSizeAdjust","fontWeight","textIndent","textShadow","wordSpacing","backgroundColor","backgroundPosition","backgroundSize","outlineColor","outlineWidth","marginTop","marginRight","marginBottom","marginLeft","borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth","borderTopLeftRadius","borderTopRightRadius","borderBottomLeftRadius","borderBottomRightRadius","borderTopColor","borderRightColor","borderBottomColor","borderLeftColor","borderSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","zIndex","opacity","boxShadow","letterSpacing","lineHeight","markerOffset","orphans","widows","size","fillOpacity","outlineOffset","floodColor","floodOpacity","lightingColor","stopColor","stopOpacity","stroke","strokeDashoffset","strokeMiterlimit","strokeOpacity","strokeWidth"];function d(){if(!b.Anim.behaviors.outlineColor){b.Anim.behaviors.outlineColor=b.Anim.behaviors.color;}}function g(k){return b.map(i,function(l){return k.getStyle(l);});}function e(k){return/[#0-9]/.test(k);}function c(k,m,l){if(l.cssClass){k.removeClass(l.cssClass);}if(m.cssClass){k.addClass(m.cssClass);}}function f(k,m,l){if(m.cssClass){k.removeClass(m.cssClass);}if(l.cssClass){k.addClass(l.cssClass);}}var a=b.Anim.prototype._start;b.Anim.prototype._start=function(){var l=this.get("node"),o=this.get("from")||{},n=this.get("to")||{};d();delete this._class_diff_attr;if(o.cssClass||n.cssClass){f(l,o,n);var k=g(l);c(l,o,n);var m=g(l);if(this.get("reverse")){f(l,o,n);}this._class_diff_attr={fromClass:o.cssClass,from:[],toClass:n.cssClass,to:[]};b.each(k,function(r,q){var s=m[q];if(r!==s){var p=i[q];if(!o[p]&&e(s)){this._class_diff_attr.from.push(p);o[p]=s;}if(!n[p]&&e(r)){this._class_diff_attr.to.push(p);n[p]=r;}}},this);delete o.cssClass;this.set("from",o);delete n.cssClass;this.set("to",n);}a.apply(this,arguments);};var h=b.Anim.prototype._runFrame;b.Anim.prototype._runFrame=function(){var k=this.get("reverse");if(!k&&this._class_diff_attr&&this._class_diff_attr.fromClass){this.get("node").removeClass(this._class_diff_attr.fromClass);}else{if(k&&this._class_diff_attr&&this._class_diff_attr.toClass){this.get("node").removeClass(this._class_diff_attr.toClass);}}h.apply(this,arguments);};var j=b.Anim.prototype._end;b.Anim.prototype._end=function(){if(this._class_diff_attr){var k=this.get("node"),m=this.get("from")||{},l=this.get("to")||{};b.each(this._class_diff_attr.from,function(n){delete m[n];});m.cssClass=this._class_diff_attr.fromClass;this.set("from",m);b.each(this._class_diff_attr.to,function(n){delete l[n];k.setStyle(n,"");});l.cssClass=this._class_diff_attr.toClass;this.set("to",l);if(this.get("reverse")){c(k,m,l);}else{f(k,m,l);}}j.apply(this,arguments);};},"gallery-2012.03.23-18-00",{requires:["anim-base","node-style","gallery-funcprog"]}); \ No newline at end of file diff --git a/build/gallery-anim-class/gallery-anim-class.js b/build/gallery-anim-class/gallery-anim-class.js new file mode 100644 index 0000000000..466b3bb141 --- /dev/null +++ b/build/gallery-anim-class/gallery-anim-class.js @@ -0,0 +1,216 @@ +YUI.add('gallery-anim-class', function(Y) { + +"use strict"; + +/********************************************************************** + *

Adds CSS class animation to Y.Anim, so you can specify cssClass in + * from and/or to. At the end of the animation, the from class is replaced + * by the to class, and all the individual styles used during the animation + * are removed.

+ * + *

Explicit entries in from or to override values set by cssClass.

+ * + * @module gallery-anim-class + */ + +var css_attribute = +[ + "top","bottom","left","right","width","height", + "maxHeight","maxWidth","minHeight","minWidth", + + "color","fontSize","fontSizeAdjust","fontWeight", + "textIndent","textShadow","wordSpacing", + + "backgroundColor","backgroundPosition","backgroundSize", + "outlineColor","outlineWidth", + + "marginTop","marginRight","marginBottom","marginLeft", + + "borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth", + "borderTopLeftRadius","borderTopRightRadius","borderBottomLeftRadius","borderBottomRightRadius", + "borderTopColor","borderRightColor","borderBottomColor","borderLeftColor", + "borderSpacing", + + "paddingTop","paddingRight","paddingBottom","paddingLeft", + + "zIndex","opacity", + + "boxShadow", + "letterSpacing","lineHeight", + "markerOffset", + "orphans","widows", + "size", + + "fillOpacity", + "outlineOffset", + "floodColor","floodOpacity","lightingColor","stopColor","stopOpacity", + "stroke","strokeDashoffset","strokeMiterlimit","strokeOpacity","strokeWidth" +]; + +function updateBehaviors() +{ + if (!Y.Anim.behaviors.outlineColor) + { + Y.Anim.behaviors.outlineColor = Y.Anim.behaviors.color; + } +} + +function getStyles(node) +{ + return Y.map(css_attribute, function(attr) + { + return node.getStyle(attr); + }); +} + +function isValidAttributeValue(s) +{ + return /[#0-9]/.test(s); // # covers colors like #AABBCC +} + +function initialState(node, from, to) +{ + if (to.cssClass) + { + node.removeClass(to.cssClass); + } + if (from.cssClass) + { + node.addClass(from.cssClass); + } +} + +function finalState(node, from, to) +{ + if (from.cssClass) + { + node.removeClass(from.cssClass); + } + if (to.cssClass) + { + node.addClass(to.cssClass); + } +} + +var orig_start = Y.Anim.prototype._start; +Y.Anim.prototype._start = function() +{ + var node = this.get('node'), + from = this.get('from') || {}, + to = this.get('to') || {}; + + updateBehaviors(); // patch after anim extensions are loaded + + delete this._class_diff_attr; + if (from.cssClass || to.cssClass) + { + finalState(node, from, to); + var new_style = getStyles(node); + + // second, so initial state is correct in forward case + + initialState(node, from, to); + var orig_style = getStyles(node); + + if (this.get('reverse')) + { + finalState(node, from, to); + } + + this._class_diff_attr = + { + fromClass: from.cssClass, + from: [], + toClass: to.cssClass, + to: [] + }; + Y.each(new_style, function(style, i) + { + var orig = orig_style[i]; + if (style !== orig) + { + var attr = css_attribute[i]; + if (!from[attr] && isValidAttributeValue(orig)) + { + this._class_diff_attr.from.push(attr); + from[attr] = orig; + } + if (!to[attr] && isValidAttributeValue(style)) + { + this._class_diff_attr.to.push(attr); + to[attr] = style; + } + } + }, + this); + + delete from.cssClass; + this.set('from', from); + + delete to.cssClass; + this.set('to', to); + } + + orig_start.apply(this, arguments); +}; + +var orig_runFrame = Y.Anim.prototype._runFrame; +Y.Anim.prototype._runFrame = function() +{ + // The first frame doesn't happen immediately, so _start() has to leave + // the original class in place. + + var reverse = this.get('reverse'); + if (!reverse && this._class_diff_attr && this._class_diff_attr.fromClass) + { + this.get('node').removeClass(this._class_diff_attr.fromClass); + } + else if (reverse && this._class_diff_attr && this._class_diff_attr.toClass) + { + this.get('node').removeClass(this._class_diff_attr.toClass); + } + + orig_runFrame.apply(this, arguments); +}; + +var orig_end = Y.Anim.prototype._end; +Y.Anim.prototype._end = function() +{ + if (this._class_diff_attr) + { + var node = this.get('node'), + from = this.get('from') || {}, + to = this.get('to') || {}; + + Y.each(this._class_diff_attr.from, function(attr) + { + delete from[attr]; + }); + + from.cssClass = this._class_diff_attr.fromClass; + this.set('from', from); + + Y.each(this._class_diff_attr.to, function(attr) + { + delete to[attr]; + node.setStyle(attr, ''); + }); + + to.cssClass = this._class_diff_attr.toClass; + this.set('to', to); + + if (this.get('reverse')) + { + initialState(node, from, to); + } + else + { + finalState(node, from, to); + } + } + + orig_end.apply(this, arguments); +}; + + +}, 'gallery-2012.03.23-18-00' ,{requires:['anim-base','node-style','gallery-funcprog']}); diff --git a/build/gallery-any-base-converter/gallery-any-base-converter-debug.js b/build/gallery-any-base-converter/gallery-any-base-converter-debug.js new file mode 100644 index 0000000000..7e632124ca --- /dev/null +++ b/build/gallery-any-base-converter/gallery-any-base-converter-debug.js @@ -0,0 +1,164 @@ +YUI.add('gallery-any-base-converter', function(Y) { + +/** + * @module gallery-any-base-converter + */ +(function (Y, moduleName) { + 'use strict'; + + var _string__empty = '', + _string__fullStop = '.', + _string_alphabet = 'alphabet', + _string_lookup = 'lookup', + _string_minusSign = 'minusSign', + _string_radixPoint = 'radixPoint', + + _Base = Y.Base, + + _each = Y.each, + _floor = Math.floor, + _pow = Math.pow; + + /** + * AnyBaseConverter is an object that will convert numbers to and from a positional notation with a custom alphabet and base. + * @class AnyBaseConverter + * @extends Base + * @param {Object} config Configuration Object. + */ + Y.AnyBaseConverter = _Base.create(moduleName, _Base, [], { + /** + * Converts a string from a custom base and returns a number. + * @method from + * @param {String} any + * @returns {Number} value + */ + from: function (any) { + any = any.split(this.get(_string_radixPoint)); + + var base = this.get(_string_alphabet).length, + fractionalPart = any[1], + integerPart = any[0].split(_string__empty), + lookup = this.get(_string_lookup), + negative = false, + value = 0; + + if (integerPart[0] === this.get(_string_minusSign)) { + negative = true; + integerPart.shift(); + } + + _each(integerPart.reverse(), function (character, index) { + value += _pow(base, index) * lookup[character]; + }); + + if (fractionalPart) { + value = parseFloat(String(value) + _string__fullStop + String(this.from(fractionalPart)).split(_string__empty).reverse().join(_string__empty)); + } + + if (negative) { + value = -value; + } + + return value; + }, + /** + * Converts a number to a custom base and returns a string. + * @method to + * @param {Number} value + * @returns {String} any + */ + to: function (value) { + value = +value; + + var alphabet = this.get(_string_alphabet), + base = alphabet.length, + fractionalPart, + integerPart, + any = _string__empty, + negative = false; + + if (value < 0) { + negative = true; + value = -value; + } + + integerPart = _floor(value); + fractionalPart = String(value).split(_string__fullStop)[1]; + + do { + any = alphabet.charAt(integerPart % base) + any; + integerPart = _floor(integerPart / base); + } while (integerPart); + + if (fractionalPart) { + any += this.get(_string_radixPoint) + this.to(fractionalPart.split(_string__empty).reverse().join(_string__empty)); + } + + if (negative) { + any = this.get(_string_minusSign) + any; + } + + return any; + } + }, { + ATTRS: { + /** + * The string of characters to use as single-digit numbers. The length of this string determines + * the base of the result. Each character should be unique within the string or else it will be + * impossible to correctly convert a string back into a number. Currently, non-BMP characters are + * not supported. + * @attribute alphabet + * @default '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~' + * @type String + */ + alphabet: { + setter: function (value) { + var lookup = {}, + i, + length = value.length; + + for (i = 0; i < length; i += 1) { + lookup[value.charAt(i)] = i; + } + + this._set(_string_lookup, lookup); + }, + value: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~' + }, + /** + * Used as a reverse lookup for a character index in alphabet. + * @attribute lookup + * @protected + * @readOnly + * @type Object + */ + lookup: { + readOnly: true, + value: null + }, + /** + * A single character string to prepend to negative values. This character should not be in the alphabet. + * Currently, non-BMP characters are not supported. + * @attribute minusSign + * @default '-' + * @type String + */ + minusSign: { + value: '-' + }, + /** + * A single character string to insert between the integer and fractional parts of the number. + * This character should not be in the alphabet. Currently, non-BMP characters are not supported. + * @attribute radixPoint + * @default '.' + * @type String + */ + radixPoint: { + value: _string__fullStop + } + } + }); +}(Y, arguments[1])); + + +}, 'gallery-2012.03.23-18-00' ,{requires:['base'], skinnable:false}); diff --git a/build/gallery-any-base-converter/gallery-any-base-converter-min.js b/build/gallery-any-base-converter/gallery-any-base-converter-min.js new file mode 100644 index 0000000000..f8f8170274 --- /dev/null +++ b/build/gallery-any-base-converter/gallery-any-base-converter-min.js @@ -0,0 +1 @@ +YUI.add("gallery-any-base-converter",function(a){(function(d,c){var l="",b=".",k="alphabet",f="lookup",g="minusSign",i="radixPoint",e=d.Base,h=d.each,j=Math.floor,m=Math.pow;d.AnyBaseConverter=e.create(c,e,[],{from:function(r){r=r.split(this.get(i));var q=this.get(k).length,o=r[1],t=r[0].split(l),s=this.get(f),n=false,p=0;if(t[0]===this.get(g)){n=true;t.shift();}h(t.reverse(),function(v,u){p+=m(q,u)*s[v];});if(o){p=parseFloat(String(p)+b+String(this.from(o)).split(l).reverse().join(l));}if(n){p=-p;}return p;},to:function(r){r=+r;var s=this.get(k),q=s.length,o,t,p=l,n=false;if(r<0){n=true;r=-r;}t=j(r);o=String(r).split(b)[1];do{p=s.charAt(t%q)+p;t=j(t/q);}while(t);if(o){p+=this.get(i)+this.to(o.split(l).reverse().join(l));}if(n){p=this.get(g)+p;}return p;}},{ATTRS:{alphabet:{setter:function(p){var q={},n,o=p.length;for(n=0;n= 0 && i < length; i += incrementBy) { + if (i in array && iterationFunction.call(contextObject, array[i], i, array)) { + return true; + } + } + + return false; + }; + + Y.Array.iterate = iterate; +}(Y)); + + +}, 'gallery-2012.03.23-18-00' ,{requires:['yui'], skinnable:false}); diff --git a/build/gallery-array-iterate/gallery-array-iterate-min.js b/build/gallery-array-iterate/gallery-array-iterate-min.js new file mode 100644 index 0000000000..57b8f26566 --- /dev/null +++ b/build/gallery-array-iterate/gallery-array-iterate-min.js @@ -0,0 +1 @@ +YUI.add("gallery-array-iterate",function(a){(function(d){var c=d.Lang.isFunction,b=function(l,j,k,e,g){if(c(k)){return b(l,j<0?l.length-1:0,j,k,e);}var f=j,h=l.length;for(;f>=0&&f= 0 && i < length; i += incrementBy) { + if (i in array && iterationFunction.call(contextObject, array[i], i, array)) { + return true; + } + } + + return false; + }; + + Y.Array.iterate = iterate; +}(Y)); + + +}, 'gallery-2012.03.23-18-00' ,{requires:['yui'], skinnable:false}); diff --git a/build/gallery-async-command-timeout/gallery-async-command-timeout-debug.js b/build/gallery-async-command-timeout/gallery-async-command-timeout-debug.js new file mode 100644 index 0000000000..cea8e2d287 --- /dev/null +++ b/build/gallery-async-command-timeout/gallery-async-command-timeout-debug.js @@ -0,0 +1,85 @@ +YUI.add('gallery-async-command-timeout', function(Y) { + +/** + * @module gallery-async-command-timeout + */ +(function (Y) { + 'use strict'; + + var _invoke = Y.Array.invoke, + _later = Y.later, + + _class; + + /** + * Asynchronous command timeout plugin. + * @class AsyncCommandTimeout + * @extends Y.Plugin.Base + * @namespace Y.Plugin + * @param {Object} config Configuration Object. + */ + _class = Y.extend(function (config) { + _class.superclass.constructor.call(this, config); + }, Y.Plugin.Base, { + destructor: function () { + _invoke(this._subscriptions, 'detach'); + + if (this._timer) { + this._timer.cancel(); + delete this._timer; + } + }, + initializer: function () { + var me = this, + host = me.get('host'), + timeout = me.get('timeout'); + + if (!timeout) { + return; + } + + me._subscriptions = [ + host.on('start', function () { + me._timer = _later(timeout, host, host.fire, [ + 'failure', + { + error: 'timeout' + } + ]); + }), + host.on('success', function (eventFacade) { + if (host.get('completed')) { + eventFacade.preventDefault(); + } else if (me._timer) { + me._timer.cancel(); + } + + delete me._timer; + }) + ]; + } + }, { + ATTRS: { + /** + * Approximate timeout in milliseconds to wait for success before + * the command automatically fails. Must be a non-negative integer. + * A value of 0 disables the timeout. + * @attribute timeout + * @default 0 + * @initonly + * @type Number + */ + timeout: { + value: 0, + writeOnce: 'initOnly' + } + }, + NAME: 'async-command-timeout', + NS: 'timeout' + }); + + Y.Plugin.AsyncCommandTimeout = _class; +}(Y)); + + +}, 'gallery-2012.03.23-18-00' ,{requires:['array-invoke', 'gallery-async-command', 'plugin'], skinnable:false}); diff --git a/build/gallery-async-command-timeout/gallery-async-command-timeout-min.js b/build/gallery-async-command-timeout/gallery-async-command-timeout-min.js new file mode 100644 index 0000000000..386a2f7ae3 --- /dev/null +++ b/build/gallery-async-command-timeout/gallery-async-command-timeout-min.js @@ -0,0 +1 @@ +YUI.add("gallery-async-command-timeout",function(a){(function(e){var c=e.Array.invoke,d=e.later,b;b=e.extend(function(f){b.superclass.constructor.call(this,f);},e.Plugin.Base,{destructor:function(){c(this._subscriptions,"detach");if(this._timer){this._timer.cancel();delete this._timer;}},initializer:function(){var g=this,f=g.get("host"),h=g.get("timeout");if(!h){return;}g._subscriptions=[f.on("start",function(){g._timer=d(h,f,f.fire,["failure",{error:"timeout"}]);}),f.on("success",function(i){if(f.get("completed")){i.preventDefault();}else{if(g._timer){g._timer.cancel();}}delete g._timer;})];}},{ATTRS:{timeout:{value:0,writeOnce:"initOnly"}},NAME:"async-command-timeout",NS:"timeout"});e.Plugin.AsyncCommandTimeout=b;}(a));},"gallery-2012.03.23-18-00",{requires:["array-invoke","gallery-async-command","plugin"],skinnable:false}); \ No newline at end of file diff --git a/build/gallery-async-command-timeout/gallery-async-command-timeout.js b/build/gallery-async-command-timeout/gallery-async-command-timeout.js new file mode 100644 index 0000000000..cea8e2d287 --- /dev/null +++ b/build/gallery-async-command-timeout/gallery-async-command-timeout.js @@ -0,0 +1,85 @@ +YUI.add('gallery-async-command-timeout', function(Y) { + +/** + * @module gallery-async-command-timeout + */ +(function (Y) { + 'use strict'; + + var _invoke = Y.Array.invoke, + _later = Y.later, + + _class; + + /** + * Asynchronous command timeout plugin. + * @class AsyncCommandTimeout + * @extends Y.Plugin.Base + * @namespace Y.Plugin + * @param {Object} config Configuration Object. + */ + _class = Y.extend(function (config) { + _class.superclass.constructor.call(this, config); + }, Y.Plugin.Base, { + destructor: function () { + _invoke(this._subscriptions, 'detach'); + + if (this._timer) { + this._timer.cancel(); + delete this._timer; + } + }, + initializer: function () { + var me = this, + host = me.get('host'), + timeout = me.get('timeout'); + + if (!timeout) { + return; + } + + me._subscriptions = [ + host.on('start', function () { + me._timer = _later(timeout, host, host.fire, [ + 'failure', + { + error: 'timeout' + } + ]); + }), + host.on('success', function (eventFacade) { + if (host.get('completed')) { + eventFacade.preventDefault(); + } else if (me._timer) { + me._timer.cancel(); + } + + delete me._timer; + }) + ]; + } + }, { + ATTRS: { + /** + * Approximate timeout in milliseconds to wait for success before + * the command automatically fails. Must be a non-negative integer. + * A value of 0 disables the timeout. + * @attribute timeout + * @default 0 + * @initonly + * @type Number + */ + timeout: { + value: 0, + writeOnce: 'initOnly' + } + }, + NAME: 'async-command-timeout', + NS: 'timeout' + }); + + Y.Plugin.AsyncCommandTimeout = _class; +}(Y)); + + +}, 'gallery-2012.03.23-18-00' ,{requires:['array-invoke', 'gallery-async-command', 'plugin'], skinnable:false}); diff --git a/build/gallery-async-progress/gallery-async-progress-debug.js b/build/gallery-async-progress/gallery-async-progress-debug.js new file mode 100644 index 0000000000..9989b4a4d9 --- /dev/null +++ b/build/gallery-async-progress/gallery-async-progress-debug.js @@ -0,0 +1,55 @@ +YUI.add('gallery-async-progress', function(Y) { + +/** + * @module gallery-async-progress + */ +(function (Y) { + 'use strict'; + + var _invoke = Y.Array.invoke, + + _class; + + /** + * Asynchronous command runner progress plugin. + * @class Y.Plugin.AsyncProgress + * @extends Y.Plugin.Base + * @param {Object} config Configuration Object. + */ + _class = Y.extend(function (config) { + _class.superclass.constructor.call(this, config); + }, Y.Plugin.Base, { + destructor: function () { + _invoke(this._subscriptions, 'detach'); + }, + initializer: function () { + var completed = 0, + host = this.get('host'), + run = host.get('run'), + total = run.length; + + this._subscriptions = _invoke(run, 'on', 'complete', function () { + completed += 1; + + /** + * @event progress + * @for Y.Async + * @param {Number} completed + * @param {Number} total + */ + host.fire('progress', { + completed: completed, + total: total + }); + }); + } + }, { + NAME: 'async-progress', + NS: 'progress' + }); + + Y.Plugin.AsyncProgress = _class; +}(Y)); + + +}, 'gallery-2012.03.23-18-00' ,{requires:['array-invoke', 'gallery-async', 'plugin'], skinnable:false}); diff --git a/build/gallery-async-progress/gallery-async-progress-min.js b/build/gallery-async-progress/gallery-async-progress-min.js new file mode 100644 index 0000000000..5357f8512c --- /dev/null +++ b/build/gallery-async-progress/gallery-async-progress-min.js @@ -0,0 +1 @@ +YUI.add("gallery-async-progress",function(a){(function(d){var c=d.Array.invoke,b;b=d.extend(function(e){b.superclass.constructor.call(this,e);},d.Plugin.Base,{destructor:function(){c(this._subscriptions,"detach");},initializer:function(){var e=0,g=this.get("host"),h=g.get("run"),f=h.length;this._subscriptions=c(h,"on","complete",function(){e+=1;g.fire("progress",{completed:e,total:f});});}},{NAME:"async-progress",NS:"progress"});d.Plugin.AsyncProgress=b;}(a));},"gallery-2012.03.23-18-00",{requires:["array-invoke","gallery-async","plugin"],skinnable:false}); \ No newline at end of file diff --git a/build/gallery-async-progress/gallery-async-progress.js b/build/gallery-async-progress/gallery-async-progress.js new file mode 100644 index 0000000000..9989b4a4d9 --- /dev/null +++ b/build/gallery-async-progress/gallery-async-progress.js @@ -0,0 +1,55 @@ +YUI.add('gallery-async-progress', function(Y) { + +/** + * @module gallery-async-progress + */ +(function (Y) { + 'use strict'; + + var _invoke = Y.Array.invoke, + + _class; + + /** + * Asynchronous command runner progress plugin. + * @class Y.Plugin.AsyncProgress + * @extends Y.Plugin.Base + * @param {Object} config Configuration Object. + */ + _class = Y.extend(function (config) { + _class.superclass.constructor.call(this, config); + }, Y.Plugin.Base, { + destructor: function () { + _invoke(this._subscriptions, 'detach'); + }, + initializer: function () { + var completed = 0, + host = this.get('host'), + run = host.get('run'), + total = run.length; + + this._subscriptions = _invoke(run, 'on', 'complete', function () { + completed += 1; + + /** + * @event progress + * @for Y.Async + * @param {Number} completed + * @param {Number} total + */ + host.fire('progress', { + completed: completed, + total: total + }); + }); + } + }, { + NAME: 'async-progress', + NS: 'progress' + }); + + Y.Plugin.AsyncProgress = _class; +}(Y)); + + +}, 'gallery-2012.03.23-18-00' ,{requires:['array-invoke', 'gallery-async', 'plugin'], skinnable:false}); diff --git a/build/gallery-bulkedit/assets/skins/sam/gallery-bulkedit-skin.css b/build/gallery-bulkedit/assets/skins/sam/gallery-bulkedit-skin.css index e69de29bb2..e5aa092a43 100644 --- a/build/gallery-bulkedit/assets/skins/sam/gallery-bulkedit-skin.css +++ b/build/gallery-bulkedit/assets/skins/sam/gallery-bulkedit-skin.css @@ -0,0 +1 @@ +/* nothing to see here */ \ No newline at end of file diff --git a/build/gallery-bulkedit/gallery-bulkedit-debug.js b/build/gallery-bulkedit/gallery-bulkedit-debug.js index bd23c1ab86..55b211ed88 100644 --- a/build/gallery-bulkedit/gallery-bulkedit-debug.js +++ b/build/gallery-bulkedit/gallery-bulkedit-debug.js @@ -1128,16 +1128,13 @@ var default_page_size = 1e9, field_container_class_prefix = field_container_class + '-', field_class_prefix = Y.ClassNameManager.getClassName(BulkEditor.NAME, 'field') + '-', - class_re_prefix = '(?:^|\\s)(?:', - class_re_suffix = ')(?:\\s|$)', - status_prefix = 'bulkedit-has', status_pattern = status_prefix + '([a-z]+)', - status_re = new RegExp(class_re_prefix + status_pattern + class_re_suffix), + status_re = new RegExp(Y.Node.class_re_prefix + status_pattern + Y.Node.class_re_suffix), record_status_prefix = 'bulkedit-hasrecord', record_status_pattern = record_status_prefix + '([a-z]+)', - record_status_re = new RegExp(class_re_prefix + record_status_pattern + class_re_suffix), + record_status_re = new RegExp(Y.Node.class_re_prefix + record_status_pattern + Y.Node.class_re_suffix), message_container_class = Y.ClassNameManager.getClassName(BulkEditor.NAME, 'message-text'), @@ -2644,4 +2641,4 @@ Y.extend(HTMLTableBulkEditor, BulkEditor, Y.HTMLTableBulkEditor = HTMLTableBulkEditor; -}, 'gallery-2012.01.11-21-03' ,{skinnable:true, optional:['datasource','dataschema','gallery-paginator'], requires:['widget','datasource-local','gallery-busyoverlay','gallery-formmgr-css-validation','gallery-node-optimizations','gallery-scrollintoview','array-extras','gallery-object-extras']}); +}, 'gallery-2012.03.23-18-00' ,{skinnable:true, optional:['datasource','dataschema','gallery-paginator'], requires:['widget','datasource-local','gallery-busyoverlay','gallery-formmgr-css-validation','gallery-node-optimizations','gallery-scrollintoview','array-extras','gallery-object-extras']}); diff --git a/build/gallery-bulkedit/gallery-bulkedit-min.js b/build/gallery-bulkedit/gallery-bulkedit-min.js index 312328b7e7..5926e15092 100644 --- a/build/gallery-bulkedit/gallery-bulkedit-min.js +++ b/build/gallery-bulkedit/gallery-bulkedit-min.js @@ -1,6 +1,6 @@ YUI.add("gallery-bulkedit",function(Y){function BulkEditDataSource(){BulkEditDataSource.superclass.constructor.apply(this,arguments);}BulkEditDataSource.NAME="bulkEditDataSource";BulkEditDataSource.ATTRS={ds:{writeOnce:true},generateRequest:{validator:Y.Lang.isFunction,writeOnce:true},uniqueIdKey:{validator:Y.Lang.isString,writeOnce:true},generateUniqueId:{value:function(){idCounter++;return uniqueIdPrefix+idCounter;},validator:Y.Lang.isFunction,writeOnce:true},startIndexExpr:{validator:Y.Lang.isString,writeOnce:true},totalRecordsReturnExpr:{validator:Y.Lang.isString,writeOnce:true},extractTotalRecords:{validator:Y.Lang.isFunction,writeOnce:true}};var uniqueIdPrefix="bulk-edit-new-id-",idCounter=0,inserted_prefix="be-ds-i:",inserted_re=/^be-ds-i:/,removed_prefix="be-ds-r:",removed_re=/^be-ds-r:/;BulkEditDataSource.comparator={"string":function(a,b){return(Y.Lang.trim(a.toString())===Y.Lang.trim(b.toString()));},"integer":function(a,b){return(parseInt(a,10)===parseInt(b,10));},"decimal":function(a,b){return(parseFloat(a,10)===parseFloat(b,10));},"boolean":function(a,b){return(((a&&b)||(!a&&!b))?true:false);}};function fromDisplayIndex(index){var count=-1;for(var i=0;i=ds.getRecordCount()){pg.setPage(pg.getPreviousPage());return;}this._render(e.response);this._updatePaginator(e.response);this.scroll_to_index=-1;},this),failure:Y.bind(function(){this.busy.hide();this.scroll_to_index=-1;},this)}});},saveChanges:function(){var ds=this.get("ds");var records=ds.getCurrentRecords();var id_key=ds.get("uniqueIdKey");Y.Object.each(this.get("fields"),function(value,key){Y.Array.each(records,function(r){var node=this.getFieldElement(r,key);ds.updateValue(r[id_key],key,node.get("value"));},this);},this);},getAllValues:function(callback){var request={startIndex:0,resultCount:this.get("ds").getRecordCount()};Y.mix(request,this.get("requestExtra"));this.get("ds").sendRequest({request:request,callback:callback});},getChanges:function(){return this.get("ds").getChanges();},insertRecord:function(index,record){var record_id=this.get("ds").insertRecord(index,record);if(index<=this.server_errors.records.length){this.server_errors.records.splice(index,0,{id:record_id});this._updatePageStatus();}return record_id;},removeRecord:function(index){if(this.get("ds").removeRecord(index)){if(index0){return{record:this.get("ds").getCurrentRecordMap()[m[1]],field_key:m[2]};}},getRecordId:function(obj){if(Y.Lang.isObject(obj)&&!(obj instanceof Y.Node)){return obj[this.get("ds").get("uniqueIdKey")];}var node=obj.getAncestorByClassName(BulkEditor.record_container_class,true);if(node){var m=id_regex.exec(node.get("id"));if(m&&m.length>0){return m[1];}}},getRecordContainer:function(record){if(Y.Lang.isString(record)){var id=id_prefix+id_separator+record;}else{if(record instanceof Y.Node){return record.getAncestorByClassName(BulkEditor.record_container_class,true);}else{var id=this.getRecordContainerId(record);}}return Y.one("#"+id);},getFieldContainer:function(record,key){var field=this.getFieldElement(record,key);return field.getAncestorByClassName(field_container_class,true);},getFieldElement:function(record,key){if(record instanceof Y.Node){record=this.getRecordId(record);}return Y.one("#"+this.getFieldId(record,key));},showRecordIndex:function(index){if(index<0||this.get("ds").getRecordCount()<=index){return;}var pg=this.get("paginator");var start=pg?pg.getStartIndex():0;var count=pg?pg.getRowsPerPage():default_page_size;if(start<=index&&index=0){this.showRecordIndex(index);}},pingRecord:function(record){var ping=this.get("pingClass");if(ping){var node=this.getRecordContainer(record);node.addClass(ping);Y.later(this.get("pingTimeout")*1000,null,function(){node.removeClass(ping);});}},_render:function(response){var container=this.get("contentBox");Y.Event.purgeElement(container);this._renderContainer(container);container.set("scrollTop",0);container.set("scrollLeft",0);Y.Array.each(response.results,function(record){var node=this._renderRecordContainer(container,record);this._renderRecord(node,record);},this);if(this.auto_validate){this.validate();}if(this.scroll_to_index>=0){this.showRecordIndex(this.scroll_to_index);this.scroll_to_index=-1;}},_renderContainer:function(container){container.set("innerHTML","");},_renderRecordContainer:function(container,record){return null;},_renderRecord:function(container,record){Y.Object.each(this.get("fields"),function(field,key){this._renderField({container:container,key:key,value:record[key],field:field,record:record});},this);},_renderField:function(o){},_updatePaginator:function(response){var pg=this.get("paginator");if(pg){pg.setTotalRecords(this.get("ds").getRecordCount(),true);}},clearServerErrors:function(){if(this.server_errors&&this.server_errors.page&&this.server_errors.page.length){this.fire("clearErrorNotification");}this.server_errors={page:[],records:[],record_map:{}};var pg=this.get("paginator");if(pg){pg.set("pageStatus",[]);}this.first_error_page=-1;this._clearValidationMessages();},setServerErrors:function(page_errors,record_field_errors){if(this.server_errors.page.length&&(!page_errors||!page_errors.length)){this.fire("clearErrorNotification"); }this.server_errors={page:page_errors||[],records:record_field_errors||[],record_map:Y.Array.toObject(record_field_errors||[],"id")};this._updatePageStatus();var pg=this.get("paginator");if(!pg||pg.getCurrentPage()===this.first_error_page){this.validate();}else{this.auto_validate=true;pg.setPage(this.first_error_page);}},_updatePageStatus:function(){var pg=this.get("paginator");if(!pg){return;}var page_size=pg?pg.getRowsPerPage():default_page_size;var status=this.page_status.slice(0);this.first_error_page=-1;var r=this.server_errors.records;for(var i=0;i");}if(!this.validation_keys){this.validation_keys=[];Y.Object.each(this.get("fields"),function(value,key){if(value.validation){this.validation_keys.push(key);}},this);}var count=ds.getRecordCount();var page_size=pg.getRowsPerPage();for(var i=0;i0){m.item(0).set("innerHTML",msg);}}bd2.replaceClass(status_pattern,status_prefix+type);this.has_validation_messages=true;}if(changed&&scroll){bd1.scrollIntoView();}},displayRecordMessage:function(id,msg,type,scroll){if(Y.Lang.isUndefined(scroll)){scroll=!this.has_validation_messages;}var bd1=this.getRecordContainer(id);var changed=this._updateRecordStatus(bd1,type,status_pattern,status_re,status_prefix); if(this._updateRecordStatus(bd1,type,record_status_pattern,record_status_re,record_status_prefix)&&msg){var bd2=bd1.getElementsByClassName(BulkEditor.record_msg_container_class).item(0);if(bd2){var m=bd2.getElementsByClassName(message_container_class);if(m&&m.size()>0){m.item(0).set("innerHTML",msg);}}}if(changed&&scroll){bd1.scrollIntoView();}},_getElementStatus:function(n,r){var m=r.exec(n.get("className"));return(m&&m.length>1?m[1]:false);},_updateRecordStatus:function(bd,type,p,r,prefix){if(Y.FormManager.statusTakesPrecedence(this._getElementStatus(bd,r),type)){bd.replaceClass(p,prefix+type);this.has_validation_messages=true;return true;}return false;}});function cleanHTML(s){if(!s){return"";}return s.toString().replace(/<\/?script>/ig,"").replace(/&/g,"&").replace(//g,">");}BulkEditor.error_msg_markup=Y.Lang.sub('
',{c:message_container_class});BulkEditor.labelMarkup=function(o){var label='';return Y.Lang.sub(label,{id:this.getFieldId(o.record,o.key),label:o.field.label});};BulkEditor.markup={input:function(o){var input='
'+"{label}{msg1}"+''+"{msg2}"+"
";var label=o.field&&o.field.label?BulkEditor.labelMarkup.call(this,o):"";return Y.Lang.sub(input,{cont:field_container_class+" "+field_container_class_prefix,field:field_class_prefix,key:o.key,id:this.getFieldId(o.record,o.key),label:label,value:cleanHTML(o.value),yiv:(o.field&&o.field.validation&&o.field.validation.css)||"",msg1:label?BulkEditor.error_msg_markup:"",msg2:label?"":BulkEditor.error_msg_markup});},select:function(o){var select='
'+"{label}{msg1}"+''+"{msg2}"+"
";var option='';var options=Y.Array.reduce(o.field.values,"",function(s,v){return s+Y.Lang.sub(option,{value:v.value,text:cleanHTML(v.text),selected:o.value&&o.value.toString()===v.value?"selected":""});});var label=o.field&&o.field.label?BulkEditor.labelMarkup.call(this,o):"";return Y.Lang.sub(select,{cont:field_container_class+" "+field_container_class_prefix,field:field_class_prefix,key:o.key,id:this.getFieldId(o.record,o.key),label:label,options:options,yiv:(o.field&&o.field.validation&&o.field.validation.css)||"",msg1:label?BulkEditor.error_msg_markup:"",msg2:label?"":BulkEditor.error_msg_markup});},textarea:function(o){var textarea='
'+"{label}{msg1}"+''+"{msg2}"+"
";var label=o.field&&o.field.label?BulkEditor.labelMarkup.call(this,o):"";return Y.Lang.sub(textarea,{cont:field_container_class+" "+field_container_class_prefix,prefix:field_class_prefix,key:o.key,id:this.getFieldId(o.record,o.key),label:label,value:cleanHTML(o.value),yiv:(o.field&&o.field.validation&&o.field.validation.css)||"",msg1:label?BulkEditor.error_msg_markup:"",msg2:label?"":BulkEditor.error_msg_markup});}};BulkEditor.fieldMarkup=function(key,record){var field=this.getFieldConfig(key);return BulkEditor.markup[field.type||"input"].call(this,{key:key,value:record[key],field:field,record:record});};Y.BulkEditor=BulkEditor;function HTMLTableBulkEditor(){HTMLTableBulkEditor.superclass.constructor.apply(this,arguments);}HTMLTableBulkEditor.NAME="htmltablebulkedit";HTMLTableBulkEditor.ATTRS={columns:{validator:Y.Lang.isObject,writeOnce:true},events:{validator:Y.Lang.isObject,writeOnce:true}};var cell_class=Y.ClassNameManager.getClassName(HTMLTableBulkEditor.NAME,"cell"),cell_class_prefix=cell_class+"-",odd_class=Y.ClassNameManager.getClassName(HTMLTableBulkEditor.NAME,"odd"),even_class=Y.ClassNameManager.getClassName(HTMLTableBulkEditor.NAME,"even"),msg_class=Y.ClassNameManager.getClassName(HTMLTableBulkEditor.NAME,"record-message"),liner_class=Y.ClassNameManager.getClassName(HTMLTableBulkEditor.NAME,"liner");HTMLTableBulkEditor.inputFormatter=function(o){o.cell.set("innerHTML",BulkEditor.markup.input.call(this,o));};HTMLTableBulkEditor.textareaFormatter=function(o){o.cell.set("innerHTML",BulkEditor.markup.textarea.call(this,o));};HTMLTableBulkEditor.selectFormatter=function(o){o.cell.set("innerHTML",BulkEditor.markup.select.call(this,o));};HTMLTableBulkEditor.defaults={input:{formatter:HTMLTableBulkEditor.inputFormatter},select:{formatter:HTMLTableBulkEditor.selectFormatter},textarea:{formatter:HTMLTableBulkEditor.textareaFormatter}};function moveFocus(e){e.halt();var info=this.getRecordAndFieldKey(e.target);if(!info){return;}var bd=this.getRecordContainer(e.target);if(bd&&e.keyCode==38){bd=bd.previousSibling;}else{if(bd){bd=bd.nextSibling;}}var id=bd&&this.getRecordId(bd);if(id){var field=this.getFieldElement(id,info.field_key);if(field){try{field.focus();field.select();}catch(ex){}}}}Y.extend(HTMLTableBulkEditor,BulkEditor,{_renderContainer:function(container){var table_class=Y.ClassNameManager.getClassName(HTMLTableBulkEditor.NAME);if(!this.table||container.get("firstChild").get("tagName").toLowerCase()!="table"||!container.get("firstChild").hasClass(table_class)){var s=Y.Lang.sub('',{t:table_class,hd:Y.ClassNameManager.getClassName(HTMLTableBulkEditor.NAME,"hd")});var row_markup='';s=Y.Array.reduce(this.get("columns"),s,function(s,column){return s+Y.Lang.sub(row_markup,{cell:cell_class,prefix:cell_class_prefix,key:column.key,label:column.label||" "});});s+="
{label}
";container.set("innerHTML",s);this.table=container.get("firstChild");Y.on("key",moveFocus,this.table,"down:38,40+ctrl",this);Y.Object.each(this.get("events"),function(e){Y.delegate(e.type,e.fn,this.table,e.nodes,this);},this);}else{while(this.table.get("children").size()>1){this.table.get("lastChild").remove();}}},_renderRecordContainer:function(container,record){var body=Y.Node.create(""); -body.set("id",this.getRecordContainerId(record));body.set("className",BulkEditor.record_container_class+" "+((this.table.get("children").size()%2)?odd_class:even_class));var msg_row=Y.Node.create("");msg_row.set("className",BulkEditor.record_msg_container_class);var msg_cell=Y.Node.create("");msg_cell.set("colSpan",this.get("columns").length);msg_cell.set("className",msg_class);msg_cell.set("innerHTML",BulkEditor.error_msg_markup);msg_row.appendChild(msg_cell);body.appendChild(msg_row);var row=Y.Node.create("");body.appendChild(row);this.table.appendChild(body);return row;},_renderRecord:function(row,record){Y.Array.each(this.get("columns"),function(column){var key=column.key;var field=this.getFieldConfig(key);var cell=Y.Node.create("");cell.set("className",cell_class+" "+cell_class_prefix+key);var liner=Y.Node.create("
");liner.set("className",liner_class);var f=null;if(Y.Lang.isFunction(column.formatter)){f=column.formatter;}else{if(field.type&&HTMLTableBulkEditor.defaults[field.type]){f=HTMLTableBulkEditor.defaults[field.type].formatter;}else{if(field.type){}f=HTMLTableBulkEditor.defaults.input.formatter;}}if(f){f.call(this,{cell:liner,key:key,value:record[key],field:field,column:column,record:record});}cell.appendChild(liner);row.appendChild(cell);},this);}});Y.HTMLTableBulkEditor=HTMLTableBulkEditor;},"gallery-2012.01.11-21-03",{skinnable:true,optional:["datasource","dataschema","gallery-paginator"],requires:["widget","datasource-local","gallery-busyoverlay","gallery-formmgr-css-validation","gallery-node-optimizations","gallery-scrollintoview","array-extras","gallery-object-extras"]}); \ No newline at end of file +body.set("id",this.getRecordContainerId(record));body.set("className",BulkEditor.record_container_class+" "+((this.table.get("children").size()%2)?odd_class:even_class));var msg_row=Y.Node.create("");msg_row.set("className",BulkEditor.record_msg_container_class);var msg_cell=Y.Node.create("");msg_cell.set("colSpan",this.get("columns").length);msg_cell.set("className",msg_class);msg_cell.set("innerHTML",BulkEditor.error_msg_markup);msg_row.appendChild(msg_cell);body.appendChild(msg_row);var row=Y.Node.create("");body.appendChild(row);this.table.appendChild(body);return row;},_renderRecord:function(row,record){Y.Array.each(this.get("columns"),function(column){var key=column.key;var field=this.getFieldConfig(key);var cell=Y.Node.create("");cell.set("className",cell_class+" "+cell_class_prefix+key);var liner=Y.Node.create("
");liner.set("className",liner_class);var f=null;if(Y.Lang.isFunction(column.formatter)){f=column.formatter;}else{if(field.type&&HTMLTableBulkEditor.defaults[field.type]){f=HTMLTableBulkEditor.defaults[field.type].formatter;}else{if(field.type){}f=HTMLTableBulkEditor.defaults.input.formatter;}}if(f){f.call(this,{cell:liner,key:key,value:record[key],field:field,column:column,record:record});}cell.appendChild(liner);row.appendChild(cell);},this);}});Y.HTMLTableBulkEditor=HTMLTableBulkEditor;},"gallery-2012.03.23-18-00",{skinnable:true,optional:["datasource","dataschema","gallery-paginator"],requires:["widget","datasource-local","gallery-busyoverlay","gallery-formmgr-css-validation","gallery-node-optimizations","gallery-scrollintoview","array-extras","gallery-object-extras"]}); \ No newline at end of file diff --git a/build/gallery-bulkedit/gallery-bulkedit.js b/build/gallery-bulkedit/gallery-bulkedit.js index 24b57e84fb..8f985b71a2 100644 --- a/build/gallery-bulkedit/gallery-bulkedit.js +++ b/build/gallery-bulkedit/gallery-bulkedit.js @@ -1128,16 +1128,13 @@ var default_page_size = 1e9, field_container_class_prefix = field_container_class + '-', field_class_prefix = Y.ClassNameManager.getClassName(BulkEditor.NAME, 'field') + '-', - class_re_prefix = '(?:^|\\s)(?:', - class_re_suffix = ')(?:\\s|$)', - status_prefix = 'bulkedit-has', status_pattern = status_prefix + '([a-z]+)', - status_re = new RegExp(class_re_prefix + status_pattern + class_re_suffix), + status_re = new RegExp(Y.Node.class_re_prefix + status_pattern + Y.Node.class_re_suffix), record_status_prefix = 'bulkedit-hasrecord', record_status_pattern = record_status_prefix + '([a-z]+)', - record_status_re = new RegExp(class_re_prefix + record_status_pattern + class_re_suffix), + record_status_re = new RegExp(Y.Node.class_re_prefix + record_status_pattern + Y.Node.class_re_suffix), message_container_class = Y.ClassNameManager.getClassName(BulkEditor.NAME, 'message-text'), @@ -2641,4 +2638,4 @@ Y.extend(HTMLTableBulkEditor, BulkEditor, Y.HTMLTableBulkEditor = HTMLTableBulkEditor; -}, 'gallery-2012.01.11-21-03' ,{skinnable:true, optional:['datasource','dataschema','gallery-paginator'], requires:['widget','datasource-local','gallery-busyoverlay','gallery-formmgr-css-validation','gallery-node-optimizations','gallery-scrollintoview','array-extras','gallery-object-extras']}); +}, 'gallery-2012.03.23-18-00' ,{skinnable:true, optional:['datasource','dataschema','gallery-paginator'], requires:['widget','datasource-local','gallery-busyoverlay','gallery-formmgr-css-validation','gallery-node-optimizations','gallery-scrollintoview','array-extras','gallery-object-extras']}); diff --git a/build/gallery-carousel-anim/gallery-carousel-anim-debug.js b/build/gallery-carousel-anim/gallery-carousel-anim-debug.js index 7cec6068e3..84a8f31771 100644 --- a/build/gallery-carousel-anim/gallery-carousel-anim-debug.js +++ b/build/gallery-carousel-anim/gallery-carousel-anim-debug.js @@ -119,7 +119,7 @@ Y.CarouselAnimPlugin = Y.extend(CarouselAnimPlugin, Y.Plugin.Base, { if (carousel && animation.speed > 0) { index = carousel._getCorrectedIndex(index); if (isNaN(index)) { - return; + return new Y.Do.Prevent(); } cb = carousel.get("contentBox"); isVertical = carousel.get("isVertical"); @@ -194,4 +194,4 @@ Y.CarouselAnimPlugin = Y.extend(CarouselAnimPlugin, Y.Plugin.Base, { }); -}, 'gallery-2011.05.18-19-11' ,{requires:['gallery-carousel', 'anim', 'plugin', 'pluginhost']}); +}, 'gallery-2012.03.23-18-00' ,{requires:['gallery-carousel', 'anim', 'plugin', 'pluginhost']}); diff --git a/build/gallery-carousel-anim/gallery-carousel-anim-min.js b/build/gallery-carousel-anim/gallery-carousel-anim-min.js index f17caa9ad3..a8502174b1 100644 --- a/build/gallery-carousel-anim/gallery-carousel-anim-min.js +++ b/build/gallery-carousel-anim/gallery-carousel-anim-min.js @@ -1 +1 @@ -YUI.add("gallery-carousel-anim",function(e){function a(){a.superclass.constructor.apply(this,arguments);}var d=e.Lang,c="afterScroll",b="beforeScroll";a.NAME="carouselAnimPlugin";a.NS="anim";a.ATTRS={animation:{validator:"_validateAnimation",value:{speed:0,effect:e.Easing.easeOut}}};e.CarouselAnimPlugin=e.extend(a,e.Plugin.Base,{initializer:function(f){this.beforeHostMethod("scrollTo",this.animateAndScrollTo);},animateAndScrollTo:function(j){var o=this,n=o.get("host"),g,h,f,i,m,k,l;if(n.get("rendered")){h=o.get("animation");if(n&&h.speed>0){j=n._getCorrectedIndex(j);if(isNaN(j)){return;}f=n.get("contentBox");k=n.get("isVertical");if(k){m={top:n.get("top")};l={top:n._getOffsetForIndex(j)};}else{m={left:n.get("left")};l={left:n._getOffsetForIndex(j)};}i=n.getFirstVisible();o.fire(b,{first:i,last:i+n.get("numVisible")});g=new e.Anim({node:f,from:m,to:l,duration:h.speed,easing:h.effect});g.on("end",e.bind(o._afterAnimEnd,o,j));g.run();return new e.Do.Prevent();}}return false;},_afterAnimEnd:function(h){var f=this,g=f.get("host");g.set("selectedItem",h);},_validateAnimation:function(f){var g=false;if(d.isObject(f)){if(d.isNumber(f.speed)){g=true;}if(!d.isUndefined(f.effect)&&!d.isFunction(f.effect)){g=false;}}return g;},_animObj:null});},"gallery-2011.05.18-19-11",{requires:["gallery-carousel","anim","plugin","pluginhost"]}); \ No newline at end of file +YUI.add("gallery-carousel-anim",function(e){function a(){a.superclass.constructor.apply(this,arguments);}var d=e.Lang,c="afterScroll",b="beforeScroll";a.NAME="carouselAnimPlugin";a.NS="anim";a.ATTRS={animation:{validator:"_validateAnimation",value:{speed:0,effect:e.Easing.easeOut}}};e.CarouselAnimPlugin=e.extend(a,e.Plugin.Base,{initializer:function(f){this.beforeHostMethod("scrollTo",this.animateAndScrollTo);},animateAndScrollTo:function(j){var o=this,n=o.get("host"),g,h,f,i,m,k,l;if(n.get("rendered")){h=o.get("animation");if(n&&h.speed>0){j=n._getCorrectedIndex(j);if(isNaN(j)){return new e.Do.Prevent();}f=n.get("contentBox");k=n.get("isVertical");if(k){m={top:n.get("top")};l={top:n._getOffsetForIndex(j)};}else{m={left:n.get("left")};l={left:n._getOffsetForIndex(j)};}i=n.getFirstVisible();o.fire(b,{first:i,last:i+n.get("numVisible")});g=new e.Anim({node:f,from:m,to:l,duration:h.speed,easing:h.effect});g.on("end",e.bind(o._afterAnimEnd,o,j));g.run();return new e.Do.Prevent();}}return false;},_afterAnimEnd:function(h){var f=this,g=f.get("host");g.set("selectedItem",h);},_validateAnimation:function(f){var g=false;if(d.isObject(f)){if(d.isNumber(f.speed)){g=true;}if(!d.isUndefined(f.effect)&&!d.isFunction(f.effect)){g=false;}}return g;},_animObj:null});},"gallery-2012.03.23-18-00",{requires:["gallery-carousel","anim","plugin","pluginhost"]}); \ No newline at end of file diff --git a/build/gallery-carousel-anim/gallery-carousel-anim.js b/build/gallery-carousel-anim/gallery-carousel-anim.js index 9df3712433..9582bcf2b0 100644 --- a/build/gallery-carousel-anim/gallery-carousel-anim.js +++ b/build/gallery-carousel-anim/gallery-carousel-anim.js @@ -117,7 +117,7 @@ Y.CarouselAnimPlugin = Y.extend(CarouselAnimPlugin, Y.Plugin.Base, { if (carousel && animation.speed > 0) { index = carousel._getCorrectedIndex(index); if (isNaN(index)) { - return; + return new Y.Do.Prevent(); } cb = carousel.get("contentBox"); isVertical = carousel.get("isVertical"); @@ -190,4 +190,4 @@ Y.CarouselAnimPlugin = Y.extend(CarouselAnimPlugin, Y.Plugin.Base, { }); -}, 'gallery-2011.05.18-19-11' ,{requires:['gallery-carousel', 'anim', 'plugin', 'pluginhost']}); +}, 'gallery-2012.03.23-18-00' ,{requires:['gallery-carousel', 'anim', 'plugin', 'pluginhost']}); diff --git a/build/gallery-carousel-circular/assets/gallery-carousel-circular.css b/build/gallery-carousel-circular/assets/gallery-carousel-circular.css new file mode 100644 index 0000000000..af445e651e --- /dev/null +++ b/build/gallery-carousel-circular/assets/gallery-carousel-circular.css @@ -0,0 +1,48 @@ + +.yui3-circular-carousel{ + -webkit-perspective: 500px; +} + +.yui3-circular-carousel-item { + position: absolute; + height: 200px; + width: 200px; + border: 1px solid black; + -webkit-border-radius: 12px; + -webkit-box-sizing: border-box; + text-align: center; + font-family: Times, serif; + font-size: 124pt; + color: black; + background-color: rgba(148,116,172, 0.6); + -webkit-transition: -webkit-transform 2s, opacity 2s; + -webkit-backface-visibility: hidden; + position: absolute; + -webkit-transition: 0.3s ease; + -webkit-transform: rotate(0) scale(0.9) translateX(100%); + z-index: 0; + width: 200px; + overflow: visible; + opacity: 0; +} + +.yui3-circular-carousel .yui3-circular-carousel-selected{ + opacity: 1; + -webkit-transform: rotate(0) scale(1) translateX(54%); + z-index: 99; +} + +.yui3-circular-carousel .yui3-circular-carousel-left, +.yui3-circular-carousel .yui3-circular-carousel-right { + opacity: 0.4; + z-index: 88; +} + +.yui3-circular-carousel .yui3-circular-carousel-left { + -webkit-transform: rotate(0) scale(0.9) translateX(0%); +} + +.yui3-circular-carousel .yui3-circular-carousel-right { + -webkit-transform: rotate(0) scale(0.9) translateX(134%); +} + diff --git a/build/gallery-carousel-circular/gallery-carousel-circular-debug.js b/build/gallery-carousel-circular/gallery-carousel-circular-debug.js new file mode 100644 index 0000000000..ec64fe685d --- /dev/null +++ b/build/gallery-carousel-circular/gallery-carousel-circular-debug.js @@ -0,0 +1,224 @@ +YUI.add('gallery-carousel-circular', function(Y) { + + var getClassName = Y.ClassNameManager.getClassName, + CHILDREN = 'children', + CIRCULAR_CAROUSEL = 'circular-carousel', + FIRST_CHILD = 'first-child', + LAST_CHILD = 'last-child', + MIN_SWIPE = 10, + _classNames = { + circularCarousel: getClassName(CIRCULAR_CAROUSEL), + circularCarouselItem : getClassName(CIRCULAR_CAROUSEL, 'item'), + end: getClassName(CIRCULAR_CAROUSEL, 'end'), + left: getClassName(CIRCULAR_CAROUSEL, 'left'), + right: getClassName(CIRCULAR_CAROUSEL, 'right'), + selectedCarouselItem: getClassName(CIRCULAR_CAROUSEL, 'selected'), + start: getClassName(CIRCULAR_CAROUSEL, 'start') + }; + + Y.CircularCarousel = Y.Base.create("circularcarousel", Y.Widget, [Y.WidgetParent], { + + initializer : function(){ + this.list = this.get('srcNode'); + this.listNodes = this.list.all('li'); + this.totalNodes = this.get('totalNodes'); + this.numNodesToDisplay = this.get('numNodesToDisplay'); + this.publish("goPrev", {defaultFn: this._goPrev}, this); + this.publish("goNext", {defaultFn: this._goNext}, this); + }, + + _initScroll : function(){ + var currentNode,list, i, arr=[]; + + list = this.list; + this.currentNode = list.one('.' + _classNames.selectedCarouselItem); + this.firstNode = list.one('.' + _classNames.start); + this.lastNode = list.one('.' + _classNames.end); + + i=0; + if(typeof this.prevCount === 'undefined'){ + this.prevCount = this.totalNodes - this.numNodesToDisplay + 1; + } + + if(!this.nextCount){ + this.nextCount = this.numNodesToDisplay - 1; + } + currentNode = this.currentNode; + this.rightNode = currentNode.next(); + this.leftNode = currentNode.previous(); + if(!this.rightNode){ + this.rightNode = this.firstNode; + } + + if(!this.leftNode){ + this.leftNode = this.lastNode; + } + + this.rightNode.addClass(_classNames.right); + this.leftNode.addClass(_classNames.left); + + Y.each(this.listNodes, function(node){ + arr[i] = node; + if(!node.hasClass('yui3-circular-carousel-selected') && + !node.hasClass('yui3-circular-carousel-right') && + !node.hasClass('yui3-circular-carousel-left') && + !node.hasClass('yui3-circular-carousel-start') && + !node.hasClass('yui3-circular-carousel-end') + ){ + node.remove(); + } + i++; + }); + this.arr = arr; + }, + + renderUI: function () { + var srcNode = this.list; + srcNode.addClass( _classNames.circularCarousel); + srcNode.one(':'+ FIRST_CHILD).addClass(_classNames.start); + srcNode.one(':'+ FIRST_CHILD).addClass(_classNames.selectedCarouselItem); + srcNode.one(':'+ LAST_CHILD).addClass(_classNames.end); + srcNode.get(CHILDREN).addClass(_classNames.circularCarouselItem); + if(srcNode){ + this._initScroll(); + } + }, + + bindUI : function(){ + // Y.on('carousel:goPrev', this._goPrev, this); + // Y.on('carousel:goNext', this._goNext, this); + + + + this.list.on('gesturemovestart', Y.bind(this._detectSwipe, this)); + Y.on('swipedLeft', this._goPrev, this); + Y.on('swipedRight', this._goNext, this); + }, + + syncUI : function(){ + + }, + + _detectSwipe : function(e){ + Y.log('detectSwipe'); + var item = e.currentTarget, + swipeStart, + swipeEnd, + isSwipeLeft, + isSwipeRight; + + item.setData("swipeStart", e.pageX); + item.once("gesturemoveend", function(e) { + swipeStart = item.getData("swipeStart"); + swipeEnd = e.pageX; + isSwipeLeft = (swipeStart - swipeEnd) > MIN_SWIPE; + isSwipeRight = (swipeEnd - swipeStart) > MIN_SWIPE; + + if (isSwipeLeft) { + Y.log('swiped Right'); + Y.fire('swipedRight'); + }else if(isSwipeRight){ + Y.log('swiped left'); + Y.fire('swipedLeft'); + } + + }); + }, + _goPrev: function(){ + Y.log('calling goPrev'); + var leftNode, futureLeft; + if (this.prevCount < 0){ + this.prevCount = this.totalNodes - 1; + }else if(this.prevCount > (this.totalNodes-1)){ + this.prevCount = 0; + } + Y.log('this.prevCount:'+this.prevCount); + Y.log('this.nextCount:'+this.nextCount); + leftNode = this.leftNode; + + futureLeft = this.arr[this.prevCount]; + + this.list.prepend(futureLeft); + + this.currentNode.replaceClass(_classNames.selectedCarouselItem,_classNames.right); + this.rightNode.removeClass(_classNames.right); + + this.prevCount--; + this.nextCount--; + if(this.nextCount < 0){ + this.nextCount = this.totalNodes - 1; + } + + this.rightNode.remove(); + + this.rightNode = this.currentNode; + leftNode.replaceClass(_classNames.left, _classNames.selectedCarouselItem); + this.currentNode = leftNode; + if(!futureLeft){ + futureLeft = this.lastNode; + } + + futureLeft.addClass(_classNames.left); + this.leftNode = futureLeft; + }, + + _goNext : function(){ + Y.log('calling goNext'); + var rightNode, futureRight; + Y.log('this.prevCount:'+this.prevCount); + Y.log('this.nextCount:'+this.nextCount); + if(this.nextCount == this.totalNodes){ + this.nextCount = 0; + }else if( this.nextCount < 0){ + this.nextCount = this.totalNodes - 1; + } + rightNode = this.rightNode; + futureRight = this.arr[this.nextCount]; + this.list.append(futureRight); + + this.currentNode.replaceClass(_classNames.selectedCarouselItem,_classNames.left); + this.leftNode.removeClass(_classNames.left); + + this.nextCount++; + this.prevCount++; + if(this.prevCount > this.totalNodes - 1){ + this.prevCount = 0; + } + this.leftNode.remove(); //added + this.leftNode = this.currentNode; + rightNode.replaceClass(_classNames.right,_classNames.selectedCarouselItem); + this.currentNode = rightNode; + + if(!futureRight){ + futureRight = this.firstNode; + } + + futureRight.addClass(_classNames.right); + this.rightNode = futureRight; + + + } + + }, { + NAME: "circularcarousel", + ATTRS : { + label : { + validator: Y.Lang.isString + }, + tabIndex: { + value: -1 + }, + totalNodes : { + value: 20 + }, + numNodesToDisplay : { + value : 13 + } + } + + }); + + + + +}, 'gallery-2012.03.23-18-00' ,{requires:['node', 'event']}); diff --git a/build/gallery-carousel-circular/gallery-carousel-circular-min.js b/build/gallery-carousel-circular/gallery-carousel-circular-min.js new file mode 100644 index 0000000000..704ecc8688 --- /dev/null +++ b/build/gallery-carousel-circular/gallery-carousel-circular-min.js @@ -0,0 +1 @@ +YUI.add("gallery-carousel-circular",function(h){var b=h.ClassNameManager.getClassName,a="children",e="circular-carousel",g="first-child",f="last-child",c=10,d={circularCarousel:b(e),circularCarouselItem:b(e,"item"),end:b(e,"end"),left:b(e,"left"),right:b(e,"right"),selectedCarouselItem:b(e,"selected"),start:b(e,"start")};h.CircularCarousel=h.Base.create("circularcarousel",h.Widget,[h.WidgetParent],{initializer:function(){this.list=this.get("srcNode");this.listNodes=this.list.all("li");this.totalNodes=this.get("totalNodes");this.numNodesToDisplay=this.get("numNodesToDisplay");this.publish("goPrev",{defaultFn:this._goPrev},this);this.publish("goNext",{defaultFn:this._goNext},this);},_initScroll:function(){var l,m,k,j=[];m=this.list;this.currentNode=m.one("."+d.selectedCarouselItem);this.firstNode=m.one("."+d.start);this.lastNode=m.one("."+d.end);k=0;if(typeof this.prevCount==="undefined"){this.prevCount=this.totalNodes-this.numNodesToDisplay+1;}if(!this.nextCount){this.nextCount=this.numNodesToDisplay-1;}l=this.currentNode;this.rightNode=l.next();this.leftNode=l.previous();if(!this.rightNode){this.rightNode=this.firstNode;}if(!this.leftNode){this.leftNode=this.lastNode;}this.rightNode.addClass(d.right);this.leftNode.addClass(d.left);h.each(this.listNodes,function(i){j[k]=i;if(!i.hasClass("yui3-circular-carousel-selected")&&!i.hasClass("yui3-circular-carousel-right")&&!i.hasClass("yui3-circular-carousel-left")&&!i.hasClass("yui3-circular-carousel-start")&&!i.hasClass("yui3-circular-carousel-end")){i.remove();}k++;});this.arr=j;},renderUI:function(){var i=this.list;i.addClass(d.circularCarousel);i.one(":"+g).addClass(d.start);i.one(":"+g).addClass(d.selectedCarouselItem);i.one(":"+f).addClass(d.end);i.get(a).addClass(d.circularCarouselItem);if(i){this._initScroll();}},bindUI:function(){this.list.on("gesturemovestart",h.bind(this._detectSwipe,this));h.on("swipedLeft",this._goPrev,this);h.on("swipedRight",this._goNext,this);},syncUI:function(){},_detectSwipe:function(m){var k=m.currentTarget,n,j,l,i;k.setData("swipeStart",m.pageX);k.once("gesturemoveend",function(o){n=k.getData("swipeStart");j=o.pageX;l=(n-j)>c;i=(j-n)>c;if(l){h.fire("swipedRight");}else{if(i){h.fire("swipedLeft");}}});},_goPrev:function(){var i,j;if(this.prevCount<0){this.prevCount=this.totalNodes-1;}else{if(this.prevCount>(this.totalNodes-1)){this.prevCount=0;}}i=this.leftNode;j=this.arr[this.prevCount];this.list.prepend(j);this.currentNode.replaceClass(d.selectedCarouselItem,d.right);this.rightNode.removeClass(d.right);this.prevCount--;this.nextCount--;if(this.nextCount<0){this.nextCount=this.totalNodes-1;}this.rightNode.remove();this.rightNode=this.currentNode;i.replaceClass(d.left,d.selectedCarouselItem);this.currentNode=i;if(!j){j=this.lastNode;}j.addClass(d.left);this.leftNode=j;},_goNext:function(){var i,j;if(this.nextCount==this.totalNodes){this.nextCount=0;}else{if(this.nextCount<0){this.nextCount=this.totalNodes-1;}}i=this.rightNode;j=this.arr[this.nextCount];this.list.append(j);this.currentNode.replaceClass(d.selectedCarouselItem,d.left);this.leftNode.removeClass(d.left);this.nextCount++;this.prevCount++;if(this.prevCount>this.totalNodes-1){this.prevCount=0;}this.leftNode.remove();this.leftNode=this.currentNode;i.replaceClass(d.right,d.selectedCarouselItem);this.currentNode=i;if(!j){j=this.firstNode;}j.addClass(d.right);this.rightNode=j;}},{NAME:"circularcarousel",ATTRS:{label:{validator:h.Lang.isString},tabIndex:{value:-1},totalNodes:{value:20},numNodesToDisplay:{value:13}}});},"gallery-2012.03.23-18-00",{requires:["node","event"]}); \ No newline at end of file diff --git a/build/gallery-carousel-circular/gallery-carousel-circular.js b/build/gallery-carousel-circular/gallery-carousel-circular.js new file mode 100644 index 0000000000..a612126768 --- /dev/null +++ b/build/gallery-carousel-circular/gallery-carousel-circular.js @@ -0,0 +1,215 @@ +YUI.add('gallery-carousel-circular', function(Y) { + + var getClassName = Y.ClassNameManager.getClassName, + CHILDREN = 'children', + CIRCULAR_CAROUSEL = 'circular-carousel', + FIRST_CHILD = 'first-child', + LAST_CHILD = 'last-child', + MIN_SWIPE = 10, + _classNames = { + circularCarousel: getClassName(CIRCULAR_CAROUSEL), + circularCarouselItem : getClassName(CIRCULAR_CAROUSEL, 'item'), + end: getClassName(CIRCULAR_CAROUSEL, 'end'), + left: getClassName(CIRCULAR_CAROUSEL, 'left'), + right: getClassName(CIRCULAR_CAROUSEL, 'right'), + selectedCarouselItem: getClassName(CIRCULAR_CAROUSEL, 'selected'), + start: getClassName(CIRCULAR_CAROUSEL, 'start') + }; + + Y.CircularCarousel = Y.Base.create("circularcarousel", Y.Widget, [Y.WidgetParent], { + + initializer : function(){ + this.list = this.get('srcNode'); + this.listNodes = this.list.all('li'); + this.totalNodes = this.get('totalNodes'); + this.numNodesToDisplay = this.get('numNodesToDisplay'); + this.publish("goPrev", {defaultFn: this._goPrev}, this); + this.publish("goNext", {defaultFn: this._goNext}, this); + }, + + _initScroll : function(){ + var currentNode,list, i, arr=[]; + + list = this.list; + this.currentNode = list.one('.' + _classNames.selectedCarouselItem); + this.firstNode = list.one('.' + _classNames.start); + this.lastNode = list.one('.' + _classNames.end); + + i=0; + if(typeof this.prevCount === 'undefined'){ + this.prevCount = this.totalNodes - this.numNodesToDisplay + 1; + } + + if(!this.nextCount){ + this.nextCount = this.numNodesToDisplay - 1; + } + currentNode = this.currentNode; + this.rightNode = currentNode.next(); + this.leftNode = currentNode.previous(); + if(!this.rightNode){ + this.rightNode = this.firstNode; + } + + if(!this.leftNode){ + this.leftNode = this.lastNode; + } + + this.rightNode.addClass(_classNames.right); + this.leftNode.addClass(_classNames.left); + + Y.each(this.listNodes, function(node){ + arr[i] = node; + if(!node.hasClass('yui3-circular-carousel-selected') && + !node.hasClass('yui3-circular-carousel-right') && + !node.hasClass('yui3-circular-carousel-left') && + !node.hasClass('yui3-circular-carousel-start') && + !node.hasClass('yui3-circular-carousel-end') + ){ + node.remove(); + } + i++; + }); + this.arr = arr; + }, + + renderUI: function () { + var srcNode = this.list; + srcNode.addClass( _classNames.circularCarousel); + srcNode.one(':'+ FIRST_CHILD).addClass(_classNames.start); + srcNode.one(':'+ FIRST_CHILD).addClass(_classNames.selectedCarouselItem); + srcNode.one(':'+ LAST_CHILD).addClass(_classNames.end); + srcNode.get(CHILDREN).addClass(_classNames.circularCarouselItem); + if(srcNode){ + this._initScroll(); + } + }, + + bindUI : function(){ + // Y.on('carousel:goPrev', this._goPrev, this); + // Y.on('carousel:goNext', this._goNext, this); + + + + this.list.on('gesturemovestart', Y.bind(this._detectSwipe, this)); + Y.on('swipedLeft', this._goPrev, this); + Y.on('swipedRight', this._goNext, this); + }, + + syncUI : function(){ + + }, + + _detectSwipe : function(e){ + var item = e.currentTarget, + swipeStart, + swipeEnd, + isSwipeLeft, + isSwipeRight; + + item.setData("swipeStart", e.pageX); + item.once("gesturemoveend", function(e) { + swipeStart = item.getData("swipeStart"); + swipeEnd = e.pageX; + isSwipeLeft = (swipeStart - swipeEnd) > MIN_SWIPE; + isSwipeRight = (swipeEnd - swipeStart) > MIN_SWIPE; + + if (isSwipeLeft) { + Y.fire('swipedRight'); + }else if(isSwipeRight){ + Y.fire('swipedLeft'); + } + + }); + }, + _goPrev: function(){ + var leftNode, futureLeft; + if (this.prevCount < 0){ + this.prevCount = this.totalNodes - 1; + }else if(this.prevCount > (this.totalNodes-1)){ + this.prevCount = 0; + } + leftNode = this.leftNode; + + futureLeft = this.arr[this.prevCount]; + + this.list.prepend(futureLeft); + + this.currentNode.replaceClass(_classNames.selectedCarouselItem,_classNames.right); + this.rightNode.removeClass(_classNames.right); + + this.prevCount--; + this.nextCount--; + if(this.nextCount < 0){ + this.nextCount = this.totalNodes - 1; + } + + this.rightNode.remove(); + + this.rightNode = this.currentNode; + leftNode.replaceClass(_classNames.left, _classNames.selectedCarouselItem); + this.currentNode = leftNode; + if(!futureLeft){ + futureLeft = this.lastNode; + } + + futureLeft.addClass(_classNames.left); + this.leftNode = futureLeft; + }, + + _goNext : function(){ + var rightNode, futureRight; + if(this.nextCount == this.totalNodes){ + this.nextCount = 0; + }else if( this.nextCount < 0){ + this.nextCount = this.totalNodes - 1; + } + rightNode = this.rightNode; + futureRight = this.arr[this.nextCount]; + this.list.append(futureRight); + + this.currentNode.replaceClass(_classNames.selectedCarouselItem,_classNames.left); + this.leftNode.removeClass(_classNames.left); + + this.nextCount++; + this.prevCount++; + if(this.prevCount > this.totalNodes - 1){ + this.prevCount = 0; + } + this.leftNode.remove(); //added + this.leftNode = this.currentNode; + rightNode.replaceClass(_classNames.right,_classNames.selectedCarouselItem); + this.currentNode = rightNode; + + if(!futureRight){ + futureRight = this.firstNode; + } + + futureRight.addClass(_classNames.right); + this.rightNode = futureRight; + + + } + + }, { + NAME: "circularcarousel", + ATTRS : { + label : { + validator: Y.Lang.isString + }, + tabIndex: { + value: -1 + }, + totalNodes : { + value: 20 + }, + numNodesToDisplay : { + value : 13 + } + } + + }); + + + + +}, 'gallery-2012.03.23-18-00' ,{requires:['node', 'event']}); diff --git a/build/gallery-carousel/assets/skins/sam/gallery-carousel.css b/build/gallery-carousel/assets/skins/sam/gallery-carousel.css index 29f32ec43d..44c75ebd12 100644 --- a/build/gallery-carousel/assets/skins/sam/gallery-carousel.css +++ b/build/gallery-carousel/assets/skins/sam/gallery-carousel.css @@ -1 +1 @@ -.yui3-js-enabled .yui3-carousel-loading{left:-1000em;position:absolute;top:-1000em}.yui3-js-enabled .yui3-carousel-hidden{left:-10000em;position:absolute;top:-10000em}.yui3-js-enabled .yui3-carousel-content{margin:0 auto;overflow:hidden;padding:0;position:relative;text-align:left;*margin:0}.yui3-js-enabled .yui3-carousel-content li{list-style:none;margin:1px;overflow:hidden;padding:0;position:absolute;text-align:center}.yui3-js-enabled .yui3-carousel-vertical .yui3-carousel-content li{display:block}.yui3-js-enabled .yui3-carousel-horizontal .yui3-carousel-content{width:100000px}.yui3-js-enabled .yui3-carousel-vertical .yui3-carousel-content{height:100000px}.yui3-js-enabled .yui3-carousel-vertical{min-width:114px;*width:114px}.yui3-js-enabled .yui3-carousel-nav{position:relative;z-index:1}.yui3-skin-sam .yui3-carousel{border:1px solid #808080;overflow:hidden;position:relative;text-align:left}.yui3-skin-sam .yui3-carousel-content li{border:1px solid #ccc}.yui3-skin-sam .yui3-carousel-content li.yui3-carousel-selected{border:1px dotted #000}.yui3-skin-sam .yui3-carousel-nav{background:url("http://yui.yahooapis.com/2.8.1/build/assets/skins/sam/sprite.png") repeat-x scroll 0 0 transparent;margin:0;padding:3px;text-align:right}.yui3-skin-sam .yui3-carousel-nav:after{clear:both;content:".";display:block;height:0;visibility:hidden}.yui3-skin-sam .yui3-carousel-button{background:url("http://yui.yahooapis.com/2.8.1/build/assets/skins/sam/sprite.png") repeat-x scroll 0 -600px transparent;float:right;height:19px;margin:5px;overflow:hidden;width:40px}.yui3-skin-sam .yui3-carousel-vertical .yui3-carousel-button{background-position:0 -800px}.yui3-skin-sam .yui3-carousel-button-disabled{background-position:0 -2000px}.yui3-skin-sam .yui3-carousel-vertical .yui3-carousel-button-disabled{background-position:0 -2100px}.yui3-skin-sam .yui3-carousel-button button{background-color:transparent;border:0 none;cursor:pointer;display:block;height:44px;margin:-2px 0 0 -2px;padding:0 0 0 50px}.yui3-skin-sam .yui3-carousel-first-button{background-position:0 -550px;margin-left:-100px;margin-right:50px}.yui3-skin-sam .yui3-carousel-vertical .yui3-carousel-first-button{background-position:0 -750px}.yui3-skin-sam .yui3-carousel-first-button-disabled{background-position:0 -1950px}.yui3-skin-sam .yui3-carousel-vertical .yui3-carousel-first-button-disabled{background-position:0 -2050px}.yui3-skin-sam .yui3-carousel-nav ul{float:right;height:19px;margin:0 100px 0 -220px;padding:0}.yui3-skin-sam .yui3-carousel-vertical .yui3-carousel-nav ul{float:none;margin:0}.yui3-skin-sam .yui3-carousel-nav ul li{background:url("http://yui.yahooapis.com/2.8.1/build/assets/skins/sam/sprite.png") no-repeat scroll 0 -650px transparent;cursor:pointer;float:left;height:9px;list-style:none outside none;margin:10px 0 0 5px;overflow:hidden;padding:0;width:9px}.yui3-skin-sam .yui3-carousel-nav ul:after{clear:both;content:".";display:block;height:0;visibility:hidden}.yui3-skin-sam .yui3-carousel-nav ul li a{display:block;height:100%;overflow:hidden;text-align:left;text-indent:-10000px;width:100%}.yui3-skin-sam .yui3-carousel-nav ul li.yui3-carousel-nav-item-selected{background-position:0 -700px} +.yui3-js-enabled .yui3-carousel-loading{left:-1000em;position:absolute;top:-1000em}.yui3-js-enabled .yui3-carousel-hidden{left:-10000em;position:absolute;top:-10000em}.yui3-js-enabled .yui3-carousel-content{margin:0 auto;overflow:hidden;padding:0;position:relative;text-align:left;*margin:0}.yui3-js-enabled .yui3-carousel-content li{list-style:none;margin:1px;overflow:hidden;padding:0;position:absolute;text-align:center}.yui3-js-enabled .yui3-carousel-vertical .yui3-carousel-content li{display:block}.yui3-js-enabled .yui3-carousel-horizontal .yui3-carousel-content{width:100000px}.yui3-js-enabled .yui3-carousel-vertical .yui3-carousel-content{height:100000px}.yui3-js-enabled .yui3-carousel-vertical{min-width:114px;*width:114px}.yui3-js-enabled .yui3-carousel-nav{position:relative;z-index:1}.yui3-skin-sam .yui3-carousel{border:1px solid #808080;overflow:hidden;position:relative;text-align:left}.yui3-skin-sam .yui3-carousel-content li{border:1px solid #ccc}.yui3-skin-sam .yui3-carousel-content li.yui3-carousel-selected{border:1px dotted #000}.yui3-skin-sam .yui3-carousel-nav{background:url("http://yui.yahooapis.com/2.8.1/build/assets/skins/sam/sprite.png") repeat-x scroll 0 0 transparent;margin:0;padding:3px;text-align:right}.yui3-skin-sam .yui3-carousel-nav:after{clear:both;content:".";display:block;height:0;visibility:hidden}.yui3-skin-sam .yui3-carousel-button{background:url("http://yui.yahooapis.com/2.8.1/build/assets/skins/sam/sprite.png") repeat-x scroll 0 -600px transparent;float:right;height:19px;margin:5px;overflow:hidden;width:40px}.yui3-skin-sam .yui3-carousel-vertical .yui3-carousel-button{background-position:0 -800px}.yui3-skin-sam .yui3-carousel-button-disabled{background-position:0 -2000px}.yui3-skin-sam .yui3-carousel-vertical .yui3-carousel-button-disabled{background-position:0 -2100px}.yui3-skin-sam .yui3-carousel-button button{background-color:transparent;border:0 none;cursor:pointer;display:block;height:44px;margin:-2px 0 0 -2px;padding:0 0 0 50px}.yui3-skin-sam .yui3-carousel-first-button{background-position:0 -550px;margin-left:-100px;margin-right:50px}.yui3-skin-sam .yui3-carousel-vertical .yui3-carousel-first-button{background-position:0 -750px}.yui3-skin-sam .yui3-carousel-first-button-disabled{background-position:0 -1950px}.yui3-skin-sam .yui3-carousel-vertical .yui3-carousel-first-button-disabled{background-position:0 -2050px}.yui3-skin-sam .yui3-carousel-nav ul{float:right;height:19px;margin:0 100px 0 -220px;padding:0}.yui3-skin-sam .yui3-carousel-vertical .yui3-carousel-nav ul{float:none;margin:0}.yui3-skin-sam .yui3-carousel-nav ul li{background:url("http://yui.yahooapis.com/2.8.1/build/assets/skins/sam/sprite.png") no-repeat scroll 0 -650px transparent;cursor:pointer;float:left;height:9px;list-style:none outside none;margin:10px 0 0 5px;overflow:hidden;padding:0;width:9px}.yui3-skin-sam .yui3-carousel-nav ul:after{clear:both;content:".";display:block;height:0;visibility:hidden}.yui3-skin-sam .yui3-carousel-nav ul li a{display:block;height:100%;overflow:hidden;text-align:left;text-indent:-10000px;width:100%}.yui3-skin-sam .yui3-carousel-nav ul li.yui3-carousel-nav-item-selected{background-position:0 -700px}#yui3-css-stamp.skin-sam-gallery-carousel{display:none} diff --git a/build/gallery-carousel/gallery-carousel-debug.js b/build/gallery-carousel/gallery-carousel-debug.js index ed98342f55..aefc527cc5 100644 --- a/build/gallery-carousel/gallery-carousel-debug.js +++ b/build/gallery-carousel/gallery-carousel-debug.js @@ -868,21 +868,27 @@ Y.Carousel = Y.extend(Carousel, Y.Widget, { if (node && node.constructor.NAME === "node") { if (which === "height") { - sz = parseInt(node.getComputedStyle("marginTop"), 10) + - parseInt(node.getComputedStyle("paddingTop"), 10) + - parseInt(node.getComputedStyle("borderTopWidth"), 10) + - parseInt(node.getComputedStyle("height"), 10) + - parseInt(node.getComputedStyle("borderBottomWidth"), 10) + - parseInt(node.getComputedStyle("paddingBottom"), 10) + - parseInt(node.getComputedStyle("marginBottom"), 10); + sz = node.get("offsetHeight"); + if (sz === 0) { // height hasn't been computed yet + sz = parseInt(node.getStyle("marginTop"), 10) + + parseInt(node.getStyle("paddingTop"), 10) + + parseInt(node.getStyle("borderTopWidth"), 10) + + parseInt(node.getStyle("height"), 10) + + parseInt(node.getStyle("borderBottomWidth"), 10) + + parseInt(node.getStyle("paddingBottom"), 10) + + parseInt(node.getStyle("marginBottom"), 10); + } } else if (which == "width") { - sz = parseInt(node.getComputedStyle("marginLeft"), 10) + - parseInt(node.getComputedStyle("paddingLeft"), 10) + - parseInt(node.getComputedStyle("borderLeftWidth"), 10) + - parseInt(node.getComputedStyle("width"), 10) + - parseInt(node.getComputedStyle("borderRightWidth"), 10) + - parseInt(node.getComputedStyle("paddingRight"), 10) + - parseInt(node.getComputedStyle("marginRight"), 10); + sz = node.get("offsetWidth"); + if (sz === 0) { + sz = parseInt(node.getStyle("marginLeft"), 10) + + parseInt(node.getStyle("paddingLeft"), 10) + + parseInt(node.getStyle("borderLeftWidth"), 10) + + parseInt(node.getStyle("width"), 10) + + parseInt(node.getStyle("borderRightWidth"), 10) + + parseInt(node.getStyle("paddingRight"), 10) + + parseInt(node.getStyle("marginRight"), 10); + } } } @@ -1467,4 +1473,4 @@ Y.Carousel = Y.extend(Carousel, Y.Widget, { }); -}, 'gallery-2011.05.18-19-11' ,{skinnable:true, requires:['widget']}); +}, 'gallery-2012.03.23-18-00' ,{skinnable:true, requires:['widget']}); diff --git a/build/gallery-carousel/gallery-carousel-min.js b/build/gallery-carousel/gallery-carousel-min.js index 7df828664e..1aad3aa2fb 100644 --- a/build/gallery-carousel/gallery-carousel-min.js +++ b/build/gallery-carousel/gallery-carousel-min.js @@ -1,2 +1,2 @@ -YUI.add("gallery-carousel",function(a){function m(){m.superclass.constructor.apply(this,arguments);}var s=a.ClassNameManager.getClassName,f=a.Lang,r=a.Node,i=false,p=true,j="button",e="button-disabled",o="content",d="item",g="nav",l="nav-item",b="afterScroll",h="beforeScroll",q="itemAdded",c="itemRemoved",k="itemSelected",n="navStateChanged";m.NAME="carousel";m.ATTRS={autoPlayInterval:{validator:f.isNumber,value:0},carouselItemEl:{validator:f.isString,value:"li"},hidePagination:{validator:f.isBoolean,value:false},isCircular:{validator:f.isBoolean,value:false},isVertical:{setter:"_setVertical",validator:f.isBoolean,value:false},useMenuForNav:{validator:f.isBoolean,value:false},numItems:{value:0},numVisible:{validator:"_validateNumVisible",value:3},revealAmount:{validator:"_validateRevealAmount",value:0},scrollIncrement:{validator:f.isNumber,value:1},selectedItem:{validator:f.isNumber,value:0},strings:{value:{GOTO_PAGE:"Go to page {page}",NEXT_PAGE:"Next Page",PREV_PAGE:"Previous Page"}}};a.Carousel=a.extend(m,a.Widget,{addItem:function(x,y){var t=this,w,u,v;if(f.isString(x)){v=r.create(a.substitute(t.ITEM_TEMPLATE,{content:x}));}else{if(f.isObject(x)&&x.constructor.NAME==="node"){v=x;}else{return false;}}if(!t._vtbl.item.content){t._vtbl.item.content=v;}if(f.isUndefined(y)){t._vtbl.items.push(v);u=t._vtbl.items.length-1;}else{if(!t._vtbl.items[y]){t._vtbl.items[y]=undefined;}t._vtbl.items.splice(y,0,v);u=y;}w=this.get("numItems");t.set("numItems",w+1);t.fire(q,{item:v,pos:u});return true;},addItems:function(u){var t=this,y=false,w,v,x;if(!f.isArray(u)){return false;}y=true;for(v=0,x=u.length;v0){t.removeItem(0);}},getElementForItem:function(t){return this.getItem(t);},getElementForItems:function(t){return this.getItems(t);},getFirstVisible:function(){var t=this,v=t.get("numVisible"),u=t.get("selectedItem");return t._getFirstVisible(u,v);},getFirstVisibleOnPage:function(t){return(t-1)*this.get("numVisible");},getItem:function(u){var t=this;if(u<0||u>t._vtbl.items.length-1){return null;}return f.isUndefined(t._vtbl.items[u])?null:t._vtbl.items[u];},getItems:function(x){var u=this,t=[],v,w;if(f.isUndefined(x)){for(v=0,w=u._vtbl.items.length;vt._vtbl.items.length-1){return false;}w=t._vtbl.items.splice(u,1);w=f.isUndefined(w[0])?false:w[0];if(!w){return false;}v=t.get("numItems");--v;t.set("numItems",v);this.fire(c,{item:w,pos:u});return true;},scrollBackward:function(){var t=this;t.scrollTo(t.getFirstVisible()-t.get("scrollIncrement"));},scrollForward:function(){var t=this;t.scrollTo(t.getFirstVisible()+t.get("scrollIncrement"));},scrollPageBackward:function(){var t=this;t.scrollTo(t.getFirstVisible()-t.get("numVisible"));},scrollPageForward:function(){var t=this;t.scrollTo(t.getFirstVisible()+t.get("numVisible"));},scrollTo:function(x){var B=this,u=B.get("isCircular"),z=B.get("numItems"),A=B.get("numVisible"),y,t,w,v;x=B._getCorrectedIndex(x);if(isNaN(x)){return;}v=B._getOffsetForIndex(x);t=B.get("contentBox");y=B.get("isVertical")?"top":"left";w=B.getFirstVisible();B.fire(h,{first:w,last:w+A});t.setStyle(y,v);w=B.getFirstVisible();B.fire(b,{first:w,last:w+A});B.set("selectedItem",x);},scrollToPage:function(u){var t=this;t.scrollTo(u*t.get("numVisible"));},bindUI:function(){var t=this,u=t.get("boundingBox");t.after("selectedItemChange",t._afterSelectedItemChange);t.on(q,t._addItemToDom);t.on(c,t._removeItemFromDom);if(!t.get("hidePagination")){u.delegate("click",a.bind(t._onNavItemClick,t),"."+s(m.NAME,l)+" > a");u.delegate("click",a.bind(t._onNavButtonClick,t),"."+s(m.NAME,j));}u.delegate("click",a.bind(t._onItemClick,t),"."+s(m.NAME,d));},initializer:function(){var t=this;t._vtbl={items:[],item:{content:null,hsz:0,vsz:0}};t.get("boundingBox").addClass(s(m.NAME,"loading"));t._parseItems();},renderUI:function(){var t=this;if(t.get("numItems")<1){t.get("boundingBox").addClass(s(m.NAME,"hidden"));return;}if(t._vtbl.item.hsz===0){t._vtbl.item.hsz=t._getNodeSize(t._vtbl.item.content,"width");}if(t._vtbl.item.vsz===0){t._vtbl.item.vsz=t._getNodeSize(t._vtbl.item.content,"height");}t._renderItems();if(!t.get("hidePagination")){t._renderNavigation();}t._renderContainer();},syncUI:function(){var t=this,u=t.get("selectedItem");t._uiSetSelectedItem(u,true);if(!t.get("hidePagination")){t._updateNavigation(u);}},_addItemToDom:function(u){var v=this,t=v.get("contentBox"),y,x,w,z;y=u.item;z=u.pos+1;if(y&&!t.contains(y)){x=v._vtbl.items[z];if(x){t.insertBefore(y,x);}else{t.append(y);}if(v.get("selectedItem")==z){w=v.get("numItems");++z;z=z>w-1?w-1:z;v.set("selectedItem",z);}v._redrawUi();}},_afterSelectedItemChange:function(u){var t=this;t._uiSetSelectedItem(u.prevVal,false);t._uiSetSelectedItem(u.newVal,true);t.fire(k,{pos:u.newVal});if(!t.get("hidePagination")){t._updateNavigation(u.newVal);}},_getCorrectedIndex:function(x){var u=this,t=u.get("isCircular"),y=u.get("numItems"),z=u.get("numVisible"),w=y-1,v=0;if(t){v=u.getPageForItem(w)*z;}if(x<0){if(t){x=v;}else{x=0;}}else{if(x>w){if(t){x=0;}else{x=v;}}}return x;},_getFirstVisible:function(u,t){return u-(u%t);},_getOffsetForIndex:function(u){var t=this,v=t._vtbl.item,w;w=t.get("isVertical")?v.vsz:v.hsz;return -w*u;},_getNodeSize:function(t,v){var u=0;if(t&&t.constructor.NAME==="node"){if(v==="height"){u=parseInt(t.getComputedStyle("marginTop"),10)+parseInt(t.getComputedStyle("paddingTop"),10)+parseInt(t.getComputedStyle("borderTopWidth"),10)+parseInt(t.getComputedStyle("height"),10)+parseInt(t.getComputedStyle("borderBottomWidth"),10)+parseInt(t.getComputedStyle("paddingBottom"),10)+parseInt(t.getComputedStyle("marginBottom"),10); -}else{if(v=="width"){u=parseInt(t.getComputedStyle("marginLeft"),10)+parseInt(t.getComputedStyle("paddingLeft"),10)+parseInt(t.getComputedStyle("borderLeftWidth"),10)+parseInt(t.getComputedStyle("width"),10)+parseInt(t.getComputedStyle("borderRightWidth"),10)+parseInt(t.getComputedStyle("paddingRight"),10)+parseInt(t.getComputedStyle("marginRight"),10);}}}return u;},_onItemClick:function(A){var C=this,z=C.get("boundingBox"),t,u,x,v,B,y,w;x=A&&A.target?A.target:null;if(!x){return;}A.preventDefault();t=z.one("."+s(m.NAME,o));u=x;B=s(m.NAME,d);while(u&&u!=t){if(u.hasClass(B)){break;}u=u.get("parentNode");}if(u){y=C._vtbl.items;for(v=0,w=y.length;v0){y+=(y*u/100);}t._uiSetHeight(y+v);t._uiSetWidth(t._vtbl.item.vsz);t._uiSetWidthCB(t._vtbl.item.vsz);}else{y=t._vtbl.item.hsz*x;if(u>0){y+=(y*u/100);}t._uiSetWidth(y);t._uiSetHeight(t._vtbl.item.hsz+v);t._uiSetHeightCB(t._vtbl.item.hsz);}},_renderItems:function(){var v=this,u=v.get("contentBox"),t,x,z,A,y,w;if(v.get("isVertical")){t="top";w=v._vtbl.item.vsz;}else{t="left";w=v._vtbl.item.hsz;}z=s(m.NAME,d);for(x=0,A=v._vtbl.items.length;x ul");G=F.get("strings.GOTO_PAGE");for(A=1;A<=x;++A){E=a.substitute(G,{page:A});w.append(a.substitute(F.DEF_NAV_ITEM_TEMPLATE,{pagenum:A,label:E}));}v=C.all("."+s(m.NAME,l));D=F.getPageForItem(y);F._uiSetNavItem(v.item(D));B=F.getPageForItem(F.get("numItems")-1);if(B<0){u=C.one("."+s(m.NAME,"first",j));if(u){u.addClass(s(m.NAME,"first",e));i=false;}u=C.one("."+s(m.NAME,"next",j));if(u){u.addClass(s(m.NAME,e));p=false;}}else{if(D===0&&D!=B){u=C.one("."+s(m.NAME,"next",j));if(u){u.removeClass(s(m.NAME,e));p=true;}if(!z){u=C.one("."+s(m.NAME,"first",j));if(u){u.addClass(s(m.NAME,"first",e));i=false;}}}else{if(D!==0&&D==B){u=C.one("."+s(m.NAME,"first",j));if(u){u.removeClass(s(m.NAME,"first",e));i=true;}if(!z){u=C.one("."+s(m.NAME,"next",j));if(u){u.addClass(s(m.NAME,e));p=false;}}}else{if(B>0){u=C.one("."+s(m.NAME,"first",j));if(u){u.removeClass(s(m.NAME,"first",e));i=true;}u=C.one("."+s(m.NAME,"next",j));if(u){u.removeClass(s(m.NAME,e));p=true;}}}}}},_validateNumVisible:function(t,u){return t>0;},_validateRevealAmount:function(t,u){return t>=0&&t<=100;},ITEM_TEMPLATE:'',DEF_NAV_TEMPLATE:'",DEF_NAV_ITEM_TEMPLATE:'',_navBtns:null,_vtbl:null});},"gallery-2011.05.18-19-11",{skinnable:true,requires:["widget"]}); +YUI.add("gallery-carousel",function(a){function m(){m.superclass.constructor.apply(this,arguments);}var s=a.ClassNameManager.getClassName,f=a.Lang,r=a.Node,i=false,p=true,j="button",e="button-disabled",o="content",d="item",g="nav",l="nav-item",b="afterScroll",h="beforeScroll",q="itemAdded",c="itemRemoved",k="itemSelected",n="navStateChanged";m.NAME="carousel";m.ATTRS={autoPlayInterval:{validator:f.isNumber,value:0},carouselItemEl:{validator:f.isString,value:"li"},hidePagination:{validator:f.isBoolean,value:false},isCircular:{validator:f.isBoolean,value:false},isVertical:{setter:"_setVertical",validator:f.isBoolean,value:false},useMenuForNav:{validator:f.isBoolean,value:false},numItems:{value:0},numVisible:{validator:"_validateNumVisible",value:3},revealAmount:{validator:"_validateRevealAmount",value:0},scrollIncrement:{validator:f.isNumber,value:1},selectedItem:{validator:f.isNumber,value:0},strings:{value:{GOTO_PAGE:"Go to page {page}",NEXT_PAGE:"Next Page",PREV_PAGE:"Previous Page"}}};a.Carousel=a.extend(m,a.Widget,{addItem:function(x,y){var t=this,w,u,v;if(f.isString(x)){v=r.create(a.substitute(t.ITEM_TEMPLATE,{content:x}));}else{if(f.isObject(x)&&x.constructor.NAME==="node"){v=x;}else{return false;}}if(!t._vtbl.item.content){t._vtbl.item.content=v;}if(f.isUndefined(y)){t._vtbl.items.push(v);u=t._vtbl.items.length-1;}else{if(!t._vtbl.items[y]){t._vtbl.items[y]=undefined;}t._vtbl.items.splice(y,0,v);u=y;}w=this.get("numItems");t.set("numItems",w+1);t.fire(q,{item:v,pos:u});return true;},addItems:function(u){var t=this,y=false,w,v,x;if(!f.isArray(u)){return false;}y=true;for(v=0,x=u.length;v0){t.removeItem(0);}},getElementForItem:function(t){return this.getItem(t);},getElementForItems:function(t){return this.getItems(t);},getFirstVisible:function(){var t=this,v=t.get("numVisible"),u=t.get("selectedItem");return t._getFirstVisible(u,v);},getFirstVisibleOnPage:function(t){return(t-1)*this.get("numVisible");},getItem:function(u){var t=this;if(u<0||u>t._vtbl.items.length-1){return null;}return f.isUndefined(t._vtbl.items[u])?null:t._vtbl.items[u];},getItems:function(x){var u=this,t=[],v,w;if(f.isUndefined(x)){for(v=0,w=u._vtbl.items.length;vt._vtbl.items.length-1){return false;}w=t._vtbl.items.splice(u,1);w=f.isUndefined(w[0])?false:w[0];if(!w){return false;}v=t.get("numItems");--v;t.set("numItems",v);this.fire(c,{item:w,pos:u});return true;},scrollBackward:function(){var t=this;t.scrollTo(t.getFirstVisible()-t.get("scrollIncrement"));},scrollForward:function(){var t=this;t.scrollTo(t.getFirstVisible()+t.get("scrollIncrement"));},scrollPageBackward:function(){var t=this;t.scrollTo(t.getFirstVisible()-t.get("numVisible"));},scrollPageForward:function(){var t=this;t.scrollTo(t.getFirstVisible()+t.get("numVisible"));},scrollTo:function(x){var B=this,u=B.get("isCircular"),z=B.get("numItems"),A=B.get("numVisible"),y,t,w,v;x=B._getCorrectedIndex(x);if(isNaN(x)){return;}v=B._getOffsetForIndex(x);t=B.get("contentBox");y=B.get("isVertical")?"top":"left";w=B.getFirstVisible();B.fire(h,{first:w,last:w+A});t.setStyle(y,v);w=B.getFirstVisible();B.fire(b,{first:w,last:w+A});B.set("selectedItem",x);},scrollToPage:function(u){var t=this;t.scrollTo(u*t.get("numVisible"));},bindUI:function(){var t=this,u=t.get("boundingBox");t.after("selectedItemChange",t._afterSelectedItemChange);t.on(q,t._addItemToDom);t.on(c,t._removeItemFromDom);if(!t.get("hidePagination")){u.delegate("click",a.bind(t._onNavItemClick,t),"."+s(m.NAME,l)+" > a");u.delegate("click",a.bind(t._onNavButtonClick,t),"."+s(m.NAME,j));}u.delegate("click",a.bind(t._onItemClick,t),"."+s(m.NAME,d));},initializer:function(){var t=this;t._vtbl={items:[],item:{content:null,hsz:0,vsz:0}};t.get("boundingBox").addClass(s(m.NAME,"loading"));t._parseItems();},renderUI:function(){var t=this;if(t.get("numItems")<1){t.get("boundingBox").addClass(s(m.NAME,"hidden"));return;}if(t._vtbl.item.hsz===0){t._vtbl.item.hsz=t._getNodeSize(t._vtbl.item.content,"width");}if(t._vtbl.item.vsz===0){t._vtbl.item.vsz=t._getNodeSize(t._vtbl.item.content,"height");}t._renderItems();if(!t.get("hidePagination")){t._renderNavigation();}t._renderContainer();},syncUI:function(){var t=this,u=t.get("selectedItem");t._uiSetSelectedItem(u,true);if(!t.get("hidePagination")){t._updateNavigation(u);}},_addItemToDom:function(u){var v=this,t=v.get("contentBox"),y,x,w,z;y=u.item;z=u.pos+1;if(y&&!t.contains(y)){x=v._vtbl.items[z];if(x){t.insertBefore(y,x);}else{t.append(y);}if(v.get("selectedItem")==z){w=v.get("numItems");++z;z=z>w-1?w-1:z;v.set("selectedItem",z);}v._redrawUi();}},_afterSelectedItemChange:function(u){var t=this;t._uiSetSelectedItem(u.prevVal,false);t._uiSetSelectedItem(u.newVal,true);t.fire(k,{pos:u.newVal});if(!t.get("hidePagination")){t._updateNavigation(u.newVal);}},_getCorrectedIndex:function(x){var u=this,t=u.get("isCircular"),y=u.get("numItems"),z=u.get("numVisible"),w=y-1,v=0;if(t){v=u.getPageForItem(w)*z;}if(x<0){if(t){x=v;}else{x=0;}}else{if(x>w){if(t){x=0;}else{x=v;}}}return x;},_getFirstVisible:function(u,t){return u-(u%t);},_getOffsetForIndex:function(u){var t=this,v=t._vtbl.item,w;w=t.get("isVertical")?v.vsz:v.hsz;return -w*u;},_getNodeSize:function(t,v){var u=0;if(t&&t.constructor.NAME==="node"){if(v==="height"){u=t.get("offsetHeight");if(u===0){u=parseInt(t.getStyle("marginTop"),10)+parseInt(t.getStyle("paddingTop"),10)+parseInt(t.getStyle("borderTopWidth"),10)+parseInt(t.getStyle("height"),10)+parseInt(t.getStyle("borderBottomWidth"),10)+parseInt(t.getStyle("paddingBottom"),10)+parseInt(t.getStyle("marginBottom"),10); +}}else{if(v=="width"){u=t.get("offsetWidth");if(u===0){u=parseInt(t.getStyle("marginLeft"),10)+parseInt(t.getStyle("paddingLeft"),10)+parseInt(t.getStyle("borderLeftWidth"),10)+parseInt(t.getStyle("width"),10)+parseInt(t.getStyle("borderRightWidth"),10)+parseInt(t.getStyle("paddingRight"),10)+parseInt(t.getStyle("marginRight"),10);}}}}return u;},_onItemClick:function(A){var C=this,z=C.get("boundingBox"),t,u,x,v,B,y,w;x=A&&A.target?A.target:null;if(!x){return;}A.preventDefault();t=z.one("."+s(m.NAME,o));u=x;B=s(m.NAME,d);while(u&&u!=t){if(u.hasClass(B)){break;}u=u.get("parentNode");}if(u){y=C._vtbl.items;for(v=0,w=y.length;v0){y+=(y*u/100);}t._uiSetHeight(y+v);t._uiSetWidth(t._vtbl.item.vsz);t._uiSetWidthCB(t._vtbl.item.vsz);}else{y=t._vtbl.item.hsz*x;if(u>0){y+=(y*u/100);}t._uiSetWidth(y);t._uiSetHeight(t._vtbl.item.hsz+v);t._uiSetHeightCB(t._vtbl.item.hsz);}},_renderItems:function(){var v=this,u=v.get("contentBox"),t,x,z,A,y,w;if(v.get("isVertical")){t="top";w=v._vtbl.item.vsz;}else{t="left";w=v._vtbl.item.hsz;}z=s(m.NAME,d);for(x=0,A=v._vtbl.items.length;x ul");G=F.get("strings.GOTO_PAGE");for(A=1;A<=x;++A){E=a.substitute(G,{page:A});w.append(a.substitute(F.DEF_NAV_ITEM_TEMPLATE,{pagenum:A,label:E}));}v=C.all("."+s(m.NAME,l));D=F.getPageForItem(y);F._uiSetNavItem(v.item(D));B=F.getPageForItem(F.get("numItems")-1);if(B<0){u=C.one("."+s(m.NAME,"first",j));if(u){u.addClass(s(m.NAME,"first",e));i=false;}u=C.one("."+s(m.NAME,"next",j));if(u){u.addClass(s(m.NAME,e));p=false;}}else{if(D===0&&D!=B){u=C.one("."+s(m.NAME,"next",j));if(u){u.removeClass(s(m.NAME,e));p=true;}if(!z){u=C.one("."+s(m.NAME,"first",j));if(u){u.addClass(s(m.NAME,"first",e));i=false;}}}else{if(D!==0&&D==B){u=C.one("."+s(m.NAME,"first",j));if(u){u.removeClass(s(m.NAME,"first",e));i=true;}if(!z){u=C.one("."+s(m.NAME,"next",j));if(u){u.addClass(s(m.NAME,e));p=false;}}}else{if(B>0){u=C.one("."+s(m.NAME,"first",j));if(u){u.removeClass(s(m.NAME,"first",e));i=true;}u=C.one("."+s(m.NAME,"next",j));if(u){u.removeClass(s(m.NAME,e));p=true;}}}}}},_validateNumVisible:function(t,u){return t>0;},_validateRevealAmount:function(t,u){return t>=0&&t<=100;},ITEM_TEMPLATE:'',DEF_NAV_TEMPLATE:'",DEF_NAV_ITEM_TEMPLATE:'',_navBtns:null,_vtbl:null});},"gallery-2012.03.23-18-00",{skinnable:true,requires:["widget"]}); diff --git a/build/gallery-carousel/gallery-carousel.js b/build/gallery-carousel/gallery-carousel.js index e917779055..047582317b 100644 --- a/build/gallery-carousel/gallery-carousel.js +++ b/build/gallery-carousel/gallery-carousel.js @@ -858,21 +858,27 @@ Y.Carousel = Y.extend(Carousel, Y.Widget, { if (node && node.constructor.NAME === "node") { if (which === "height") { - sz = parseInt(node.getComputedStyle("marginTop"), 10) + - parseInt(node.getComputedStyle("paddingTop"), 10) + - parseInt(node.getComputedStyle("borderTopWidth"), 10) + - parseInt(node.getComputedStyle("height"), 10) + - parseInt(node.getComputedStyle("borderBottomWidth"), 10) + - parseInt(node.getComputedStyle("paddingBottom"), 10) + - parseInt(node.getComputedStyle("marginBottom"), 10); + sz = node.get("offsetHeight"); + if (sz === 0) { // height hasn't been computed yet + sz = parseInt(node.getStyle("marginTop"), 10) + + parseInt(node.getStyle("paddingTop"), 10) + + parseInt(node.getStyle("borderTopWidth"), 10) + + parseInt(node.getStyle("height"), 10) + + parseInt(node.getStyle("borderBottomWidth"), 10) + + parseInt(node.getStyle("paddingBottom"), 10) + + parseInt(node.getStyle("marginBottom"), 10); + } } else if (which == "width") { - sz = parseInt(node.getComputedStyle("marginLeft"), 10) + - parseInt(node.getComputedStyle("paddingLeft"), 10) + - parseInt(node.getComputedStyle("borderLeftWidth"), 10) + - parseInt(node.getComputedStyle("width"), 10) + - parseInt(node.getComputedStyle("borderRightWidth"), 10) + - parseInt(node.getComputedStyle("paddingRight"), 10) + - parseInt(node.getComputedStyle("marginRight"), 10); + sz = node.get("offsetWidth"); + if (sz === 0) { + sz = parseInt(node.getStyle("marginLeft"), 10) + + parseInt(node.getStyle("paddingLeft"), 10) + + parseInt(node.getStyle("borderLeftWidth"), 10) + + parseInt(node.getStyle("width"), 10) + + parseInt(node.getStyle("borderRightWidth"), 10) + + parseInt(node.getStyle("paddingRight"), 10) + + parseInt(node.getStyle("marginRight"), 10); + } } } @@ -1444,4 +1450,4 @@ Y.Carousel = Y.extend(Carousel, Y.Widget, { }); -}, 'gallery-2011.05.18-19-11' ,{skinnable:true, requires:['widget']}); +}, 'gallery-2012.03.23-18-00' ,{skinnable:true, requires:['widget']}); diff --git a/build/gallery-console-test/assets/gallery-console-test-core.css b/build/gallery-console-test/assets/gallery-console-test-core.css index e69de29bb2..e5aa092a43 100644 --- a/build/gallery-console-test/assets/gallery-console-test-core.css +++ b/build/gallery-console-test/assets/gallery-console-test-core.css @@ -0,0 +1 @@ +/* nothing to see here */ \ No newline at end of file diff --git a/build/gallery-console-test/assets/skins/sam/gallery-console-test.css b/build/gallery-console-test/assets/skins/sam/gallery-console-test.css index c51ae821b4..98c4d5233f 100644 --- a/build/gallery-console-test/assets/skins/sam/gallery-console-test.css +++ b/build/gallery-console-test/assets/skins/sam/gallery-console-test.css @@ -1 +1 @@ -.yui3-skin-sam .yui3-console-test-container{border-top:1px solid black;padding:4px 1ex}.yui3-skin-sam .yui3-console-test-container select.menu{width:50%} +.yui3-skin-sam .yui3-console-test-container{border-top:1px solid black;padding:4px 1ex}.yui3-skin-sam .yui3-console-test-container select.menu{width:50%}#yui3-css-stamp.skin-sam-gallery-console-test{display:none} diff --git a/build/gallery-console-test/gallery-console-test-debug.js b/build/gallery-console-test/gallery-console-test-debug.js index b538a39b1f..205f0b7207 100644 --- a/build/gallery-console-test/gallery-console-test-debug.js +++ b/build/gallery-console-test/gallery-console-test-debug.js @@ -116,4 +116,4 @@ Y.namespace("Plugin"); Y.Plugin.ConsoleTest = ConsoleTest; -}, 'gallery-2011.06.29-23-18' ,{requires:['console','plugin','test'], skinnable:true}); +}, 'gallery-2012.03.23-18-00' ,{requires:['console','plugin','test'], skinnable:true}); diff --git a/build/gallery-console-test/gallery-console-test-min.js b/build/gallery-console-test/gallery-console-test-min.js index 29d7bf6d3f..b16823ab62 100644 --- a/build/gallery-console-test/gallery-console-test-min.js +++ b/build/gallery-console-test/gallery-console-test-min.js @@ -1 +1 @@ -YUI.add("gallery-console-test",function(c){function a(d){a.superclass.constructor.call(this,d);}a.NAME="ConsoleTestPlugin";a.NS="test";function b(e){var d=c.Node.getDOMNode(e);d.length=0;d[0]=new Option("All tests",-1);c.Array.each(c.Test.Runner.masterSuite.items,function(g,f){d[f+1]=new Option(g.name,f);});}c.extend(a,c.Plugin.Base,{initializer:function(d){this.doAfter("renderUI",this.renderUI);if(this.get("host").get("rendered")){this.renderUI();}},destructor:function(){this.container.remove();},renderUI:function(){var g=this.get("host").get("contentBox").one("."+c.Console.CHROME_CLASSES.console_ft_class);if(g){this.container=c.Node.create(c.Lang.sub('
'+''+''+''+"
",{c:c.ClassNameManager.getClassName("console","test","container"),b1:"Run",b2:"Refresh"}));var f=this.container.one("select");b(f);this.container.one("button.run").on("click",function(){var j=f.get("value");if(j>=0){var l=c.Test.Runner.masterSuite.items;c.Test.Runner.clear();c.Test.Runner.add(l[j]);var k=c.Test.Runner.on("complete",function(){k.detach();c.Test.Runner.clear();c.Array.each(l,function(h){c.Test.Runner.add(h);});});}c.Test.Runner.run();});this.container.one("button.refresh").on("click",function(){b(f);});var e=g.one("."+c.Console.CHROME_CLASSES.console_controls_class);var d=e.get("nextSibling");if(d){g.insertBefore(this.container,d);}else{g.appendChild(this.container);}}}});c.namespace("Plugin");c.Plugin.ConsoleTest=a;},"gallery-2011.06.29-23-18",{requires:["console","plugin","test"],skinnable:true}); \ No newline at end of file +YUI.add("gallery-console-test",function(c){function a(d){a.superclass.constructor.call(this,d);}a.NAME="ConsoleTestPlugin";a.NS="test";function b(e){var d=c.Node.getDOMNode(e);d.length=0;d[0]=new Option("All tests",-1);c.Array.each(c.Test.Runner.masterSuite.items,function(g,f){d[f+1]=new Option(g.name,f);});}c.extend(a,c.Plugin.Base,{initializer:function(d){this.doAfter("renderUI",this.renderUI);if(this.get("host").get("rendered")){this.renderUI();}},destructor:function(){this.container.remove();},renderUI:function(){var g=this.get("host").get("contentBox").one("."+c.Console.CHROME_CLASSES.console_ft_class);if(g){this.container=c.Node.create(c.Lang.sub('
'+''+''+''+"
",{c:c.ClassNameManager.getClassName("console","test","container"),b1:"Run",b2:"Refresh"}));var f=this.container.one("select");b(f);this.container.one("button.run").on("click",function(){var j=f.get("value");if(j>=0){var l=c.Test.Runner.masterSuite.items;c.Test.Runner.clear();c.Test.Runner.add(l[j]);var k=c.Test.Runner.on("complete",function(){k.detach();c.Test.Runner.clear();c.Array.each(l,function(h){c.Test.Runner.add(h);});});}c.Test.Runner.run();});this.container.one("button.refresh").on("click",function(){b(f);});var e=g.one("."+c.Console.CHROME_CLASSES.console_controls_class);var d=e.get("nextSibling");if(d){g.insertBefore(this.container,d);}else{g.appendChild(this.container);}}}});c.namespace("Plugin");c.Plugin.ConsoleTest=a;},"gallery-2012.03.23-18-00",{requires:["console","plugin","test"],skinnable:true}); \ No newline at end of file diff --git a/build/gallery-console-test/gallery-console-test.js b/build/gallery-console-test/gallery-console-test.js index b538a39b1f..205f0b7207 100644 --- a/build/gallery-console-test/gallery-console-test.js +++ b/build/gallery-console-test/gallery-console-test.js @@ -116,4 +116,4 @@ Y.namespace("Plugin"); Y.Plugin.ConsoleTest = ConsoleTest; -}, 'gallery-2011.06.29-23-18' ,{requires:['console','plugin','test'], skinnable:true}); +}, 'gallery-2012.03.23-18-00' ,{requires:['console','plugin','test'], skinnable:true}); diff --git a/build/gallery-datatable-350-preview/gallery-datatable-350-preview-debug.js b/build/gallery-datatable-350-preview/gallery-datatable-350-preview-debug.js index 87de60e0d6..c003df89ed 100644 --- a/build/gallery-datatable-350-preview/gallery-datatable-350-preview-debug.js +++ b/build/gallery-datatable-350-preview/gallery-datatable-350-preview-debug.js @@ -290,7 +290,7 @@ Y.mix(Table.prototype, { @type {HTML} @default '' **/ - TABLE_TEMPLATE : '
', + TABLE_TEMPLATE : '', /** HTML template used to create table's `` if configured with a @@ -967,7 +967,7 @@ Y.mix(Table.prototype, { @protected **/ _initRecordType: function () { - var data, columns, recordType, handle; + var data, columns, recordType, handle, columnKeys; if (!this.get('recordType')) { data = this.get('data'); @@ -988,8 +988,12 @@ Y.mix(Table.prototype, { this._createRecordClass(keys(data[0])); // Or if the columns were defined, build a class from the keys - } else if (keys(columns).length) { - recordType = this._createRecordClass(keys(columns)); + } else { + columnKeys = keys(columns); + + if (columnKeys.length) { + recordType = this._createRecordClass(columnKeys); + } } if (recordType) { @@ -1065,11 +1069,12 @@ Y.mix(Table.prototype, { keys to the returned map. There is no limit to the levels of nesting. All columns are assigned a `_yuid` stamp and `_id` property corresponding - to the column's configured `name` or `key` property. If the same `name` or - `key` appears in multiple columns, subsequent appearances will have their - `_id` appended with an incrementing number (e.g. if column "foo" is - included in the `columns` attribute twice, the first will get `_id` of - "foo", and the second an `_id` of "foo1"). + to the column's configured `name` or `key` property with any spaces + replaced with dashes. If the same `name` or `key` appears in multiple + columns, subsequent appearances will have their `_id` appended with an + incrementing number (e.g. if column "foo" is included in the `columns` + attribute twice, the first will get `_id` of "foo", and the second an `_id` + of "foo1"). The result is an object map with column keys as the property name and the corresponding column object as the associated value. @@ -1111,6 +1116,10 @@ Y.mix(Table.prototype, { // added to the end. id = col.name || col.key || col._yuid; + // Sanitize the _id for use in generated CSS classes. + // TODO: is there more to do for other uses of _id? + id = id.replace(/\s+/, '-'); + if (keys[id]) { id += (keys[id]++); } else { @@ -1146,12 +1155,6 @@ Y.mix(Table.prototype, { this._viewConfig.columns = this.get('columns'); this._viewConfig.modelList = this.data; - contentBox.setAttrs({ - 'role' : 'grid', - 'aria-readonly': true // until further notice - }); - - this.fire('renderTable', { headerView : this.get('headerView'), headerConfig: this._headerConfig, @@ -1360,13 +1363,7 @@ Y.mix(Table.prototype, { className: this.getClassName('caption') })); - captionId = Y.stamp(caption); - - caption.set('id', captionId); - table.prepend(this._captionNode); - - table.setAttribute('aria-describedby', captionId); } caption.setContent(htmlContent); @@ -1375,8 +1372,6 @@ Y.mix(Table.prototype, { caption.remove(true); delete this._captionNode; - - table.removeAttribute('aria-describedby'); } }, @@ -1387,7 +1382,11 @@ Y.mix(Table.prototype, { @protected **/ _uiSetSummary: function (summary) { - this._tableNode.setAttribute('summary', summary || ''); + if (summary) { + this._tableNode.setAttribute('summary', summary); + } else { + this._tableNode.removeAttribute('summary'); + } }, /** @@ -1421,7 +1420,7 @@ Y.mix(Table.prototype, { return val === null || (isFunction(val) && val.prototype.render); } }); -}, 'gallery-2012.02.01-21-35', { requires: ['model-list'] }); +}, 'gallery-2012.03.23-18-00', { requires: ['model-list'] }); YUI.add('gallery-datatable-350-preview-head', function (Y) { /** @@ -1466,6 +1465,8 @@ Supported properties of the column objects include: * `key` - If `label` is not specified, the `key` is used for content. * `children` - Array of columns to appear below this column in the next row. + * `headerTemplate` - Overrides the instance's `CELL_TEMPLATE` for cells in this + column only. * `abbr` - The content of the 'abbr' attribute of the `' + @default '' **/ CELL_TEMPLATE : - '', + '', /** The data representation of the header rows to render. This is assigned by @@ -1543,7 +1544,7 @@ Y.namespace('DataTable').HeaderView = Y.Base.create('tableHeader', Y.View, [], { @default '{content}' **/ ROW_TEMPLATE: - '{content}', + '{content}', // -- Public methods ------------------------------------------------------ @@ -1580,9 +1581,9 @@ Y.namespace('DataTable').HeaderView = Y.Base.create('tableHeader', Y.View, [], { var thead = this.get('container'), columns = this.columns, defaults = { - abbr: '', _colspan: 1, - _rowspan: 1 + _rowspan: 1, + abbr: '' }, i, len, j, jlen, col, html, content, values; @@ -1600,11 +1601,14 @@ Y.namespace('DataTable').HeaderView = Y.Base.create('tableHeader', Y.View, [], { col, { className: this.getClassName('header'), content : col.label || col.key || - ("Column " + (j + 1)), - headers : '' + ("Column " + (j + 1)) } ); + if (col.abbr) { + values.abbr = 'abbr="' + col.abbr + '"'; + } + if (col.className) { values.className += ' ' + col.className; } @@ -1614,12 +1618,8 @@ Y.namespace('DataTable').HeaderView = Y.Base.create('tableHeader', Y.View, [], { ' ' + this.getClassName('col', col._id); } - if (col._parent) { - values._headers = - 'headers="' + col._parent._headers.join(' ') + '"'; - } - - content += fromTemplate(this.CELL_TEMPLATE, values); + content += fromTemplate( + col.headerTemplate || this.CELL_TEMPLATE, values); } html += fromTemplate(this.ROW_TEMPLATE, { @@ -1759,6 +1759,8 @@ Y.namespace('DataTable').HeaderView = Y.Base.create('tableHeader', Y.View, [], { * `children` - Array of columns to appear below this column in the next row. * `abbr` - The content of the 'abbr' attribute of the `` structure with arrays for rows and objects for cells. Column objects have the @@ -1887,7 +1889,7 @@ Y.namespace('DataTable').HeaderView = Y.Base.create('tableHeader', Y.View, [], { return columns; } }); -}, 'gallery-2012.02.01-21-35', { requires: ['view', 'gallery-datatable-350-preview-core'] }); +}, 'gallery-2012.03.23-18-00', { requires: ['view', 'gallery-datatable-350-preview-core'] }); YUI.add('gallery-datatable-350-preview-body', function (Y) { /** @@ -1913,6 +1915,8 @@ Supported properties of the column objects include: * `name` - Used for columns that don't relate to an attribute in the Model (`formatter` or `nodeFormatter` only) if the implementer wants a predictable name to refer to in their CSS. + * `cellTemplate` - Overrides the instance's `CELL_TEMPLATE` for cells in this + column only. * `formatter` - Used to customize or override the content value from the Model. These do not have access to the cell or row Nodes and should return string (HTML) content. @@ -1920,8 +1924,8 @@ Supported properties of the column objects include: custom modifications on the cell or row Node that could not be performed by `formatter`s. Should be used sparingly for better performance. * `emptyCellValue` - String (HTML) value to use if the Model data for a - column, or the content generated by a `formatter`, is the empty string or - `undefined`. + column, or the content generated by a `formatter`, is the empty string, + `null`, or `undefined`. * `allowHTML` - Set to `true` if a column value, `formatter`, or `emptyCellValue` can contain HTML. This defaults to `false` to protect against XSS. @@ -1935,7 +1939,7 @@ Column `formatter`s are passed an object (`o`) with the following properties: * `column` - The column configuration object for the current column. * `className` - Initially empty string to allow `formatter`s to add CSS classes to the cell's ``. @@ -1952,7 +1956,7 @@ properties: * `data` - An object map of Model keys to their current values. * `record` - The Model instance. * `column` - The column configuration object for the current column. - * `rowindex` - The zero-based row number. + * `rowIndex` - The zero-based row number. They are expected to inject content into the cell's Node directly, including any "empty" cell content. Each `nodeFormatter` will have access through the @@ -1988,9 +1992,9 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { @property CELL_TEMPLATE @type {HTML} - @default '' + @default '' **/ - CELL_TEMPLATE: '', + CELL_TEMPLATE: '', /** CSS class applied to even rows. This is assigned at instantiation after @@ -2021,12 +2025,9 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { @property ROW_TEMPLATE @type {HTML} - @default '{content}' + @default '{content}' **/ - ROW_TEMPLATE : - '' + - '{content}' + - '', + ROW_TEMPLATE : '{content}', /** The object that serves as the source of truth for column and row data. @@ -2123,6 +2124,8 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { * `name` - Used for columns that don't relate to an attribute in the Model (`formatter` or `nodeFormatter` only) if the implementer wants a predictable name to refer to in their CSS. + * `cellTemplate` - Overrides the instance's `CELL_TEMPLATE` for cells in + this column only. * `formatter` - Used to customize or override the content value from the Model. These do not have access to the cell or row Nodes and should return string (HTML) content. @@ -2131,8 +2134,8 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { performed by `formatter`s. Should be used sparingly for better performance. * `emptyCellValue` - String (HTML) value to use if the Model data for a - column, or the content generated by a `formatter`, is the empty string - or `undefined`. + column, or the content generated by a `formatter`, is the empty string, + `null`, or `undefined`. * `allowHTML` - Set to `true` if a column value, `formatter`, or `emptyCellValue` can contain HTML. This defaults to `false` to protect against XSS. @@ -2149,7 +2152,7 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { * `column` - The column configuration object for the current column. * `className` - Initially empty string to allow `formatter`s to add CSS classes to the cell's ``. @@ -2168,7 +2171,7 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { * `data` - An object map of Model keys to their current values. * `record` - The Model instance. * `column` - The column configuration object for the current column. - * `rowindex` - The zero-based row number. + * `rowIndex` - The zero-based row number. They are expected to inject content into the cell's Node directly, including any "empty" cell content. Each `nodeFormatter` will have access through the @@ -2267,7 +2270,7 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { var formatterData = { data : record.toJSON(), record : record, - rowindex : index + rowIndex : index }, row = tbodyNode.rows[index], i, len, col, key, cell, keep; @@ -2375,9 +2378,9 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { * `clientID` - From Model, used the assign the ``'s 'id' attribute. * `foo` - The value to populate the 'foo' column cell content. This value will be the value stored in the Model's `foo` attribute, or the - result of the column's `formatter` if assigned. If the value is '' or - `undefined`, and the column's `emptyCellValue` is assigned, that value - will be used. + result of the column's `formatter` if assigned. If the value is '', + `null`, or `undefined`, and the column's `emptyCellValue` is assigned, + that value will be used. * `bar` - Same for the 'bar' column cell content. * `foo-className` - String of CSS classes to apply to the ` + **/ + MESSAGE_TEMPLATE: '', + + /** + Hides the message node. + + @method hideMessage + @return {DataTable} + @chainable + **/ + hideMessage: function () { + this.get('boundingBox').removeClass( + this.getClassName('message', 'visible')); + + return this; + }, + + /** + Display the message node and set its content to `message`. If there is a + localized `strings` entry for the value of `message`, that string will be + used. + + @method showMessage + @param {String} message The message name or message itself to display + @return {DataTable} + @chainable + **/ + showMessage: function (message) { + var content = this.getString(message) || message; + + if (!this._messageNode) { + this._initMessageNode(); + } + + if (this.get('showMessages')) { + if (content) { + this._messageNode.one( + '.' + this.getClassName('message', 'content')) + .setContent(content); + + this.get('boundingBox').addClass( + this.getClassName('message','visible')); + } else { + // TODO: is this right? + // If no message provided, remove the message node. + this.hideMessage(); + } + } + + return this; + }, + + //-------------------------------------------------------------------------- + // Protected methods + //-------------------------------------------------------------------------- + /** + Updates the colspan of the `",COLGROUP_TEMPLATE:"",setColumnWidth:function(i,h){var g=this.getColumn(i),f=g&&b(this._displayColumns,g);if(f>-1){if(d(h)){h+="px";}g.width=h;this._setColumnWidth(f,h);}return this;},_createColumnGroup:function(){return e.Node.create(this.COLGROUP_TEMPLATE);},initializer:function(f){this.after("renderTable",function(g){this._uiSetColumns();this.after("columnsChange",this._uiSetColumns);});},_setColumnWidth:function(j,k){var h=this._colgroupNode,i=h&&h.all("col").item(j),l,g,f;if(i){if(k&&d(k)){k+="px";}i.setStyle("width",k);if(k&&e.Features.test("table","badColWidth")){l=this._tbodyNode&&this._tbodyNode.one("tr");g=l&&l.all("td").item(j);if(g){f=function(m){return parseInt(g.getComputedStyle(m),10)|0;};i.setStyle("width",parseInt(k,10)-f("paddingLeft")-f("paddingRight")-f("borderLeftWidth")-f("borderRightWidth")+"px");}}}},_uiSetColumns:function(){var k=this.COL_TEMPLATE,g=this._colgroupNode,j=this._displayColumns,h,f;if(!g){g=this._colgroupNode=this._createColumnGroup();this._tableNode.insertBefore(g,this._tableNode.one("> thead, > tfoot, > tbody"));}else{g.empty();}for(h=0,f=j.length;h
` * `className` - Adds this string of CSS classes to the column header @@ -1505,10 +1506,10 @@ Y.namespace('DataTable').HeaderView = Y.Base.create('tableHeader', Y.View, [], { @property CELL_TEMPLATE @type {HTML} - @default '{content}{content}{content}{content}
` + * `headerTemplate` - Overrides the instance's `CELL_TEMPLATE` for cells + in this column only. The output structure is basically a simulation of the `
`. - * `rowindex` - The zero-based row number. + * `rowIndex` - The zero-based row number. * `rowClass` - Initially empty string to allow `formatter`s to add CSS classes to the cell's containing row `
{content}{content}{content}{content}
`. - * `rowindex` - The zero-based row number. + * `rowIndex` - The zero-based row number. * `rowClass` - Initially empty string to allow `formatter`s to add CSS classes to the cell's containing row `
`. * `bar-className` - Same. @@ -2419,7 +2422,7 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { record : model, className: '', rowClass : '', - rowindex : index + rowIndex : index }; if (typeof col.formatter === 'string') { @@ -2441,7 +2444,7 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { } } - if (value === undefined || value === '') { + if (value === undefined || value === null || value === '') { value = col.emptyCellValue || ''; } @@ -2467,16 +2470,19 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { _createRowTemplate: function (columns) { var html = '', cellTemplate = this.CELL_TEMPLATE, - i, len, col, key, token, tokenValues; + i, len, col, key, token, headers, tokenValues; for (i = 0, len = columns.length; i < len; ++i) { - col = columns[i]; - key = col.key; - token = col._id; + col = columns[i]; + key = col.key; + token = col._id; + // Only include headers if there are more than one + headers = (col._headers || []).length > 1 ? + 'headers="' + col._headers.join(' ') + '"' : ''; tokenValues = { content : '{' + token + '}', - headers : (col._headers || []).join(' '), + headers : headers, className: this.getClassName('col', token) + ' ' + (col.className || '') + ' ' + this.getClassName('cell') + @@ -2488,7 +2494,7 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { tokenValues.content = ''; } - html += fromTemplate(cellTemplate, tokenValues); + html += fromTemplate(col.cellTemplate || cellTemplate, tokenValues); } this._rowTemplate = fromTemplate(this.ROW_TEMPLATE, { @@ -2594,7 +2600,7 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { **/ //_rowTemplate: null }); -}, 'gallery-2012.02.01-21-35', { requires: ['view', 'gallery-datatable-350-preview-core'] }); +}, 'gallery-2012.03.23-18-00', { requires: ['view', 'gallery-datatable-350-preview-core'] }); YUI.add('gallery-datatable-350-preview-base', function (Y) { /** @@ -2857,7 +2863,7 @@ Y.DataTable.Base = Y.Base.create('datatable', Y.Widget, [Y.DataTable.Core], Y.DataTable = Y.mix( Y.Base.create('datatable', Y.DataTable.Base, []), // Create the class Y.DataTable); // Migrate static and namespaced classes -}, 'gallery-2012.02.01-21-35', { requires: ['model-list', 'view', 'base-build', 'widget', 'escape', 'gallery-datatable-350-preview-core'] }); +}, 'gallery-2012.03.23-18-00', { requires: ['model-list', 'view', 'base-build', 'widget', 'escape', 'gallery-datatable-350-preview-core'] }); YUI.add('gallery-datatable-350-preview-mutable', function (Y) { var toArray = Y.Array, @@ -2902,7 +2908,10 @@ those methods to trigger per-operation sync. @default `false` **/ Mutable.ATTRS = { - autoSync: {} + autoSync: { + value: false, + validator: YLang.isBoolean + } }; Y.mix(Mutable.prototype, { @@ -3058,7 +3067,7 @@ Y.mix(Mutable.prototype, { @method addRow @param {Object} data The data or Model instance for the new record - @param {Object} [config]* Configuration to pass along + @param {Object} [config] Configuration to pass along @param {Function} [callback] Callback function for Model's `save()` @param {Error|null} callback.err If an error occurred or validation failed, this parameter will contain the error. If the sync operation @@ -3070,10 +3079,11 @@ Y.mix(Mutable.prototype, { @chainable **/ addRow: function (data, config) { + // Allow autoSync: true + addRow({ data }, { sync: false }) var sync = (config && ('sync' in config)) ? config.sync : this.get('autoSync'), - models, i, len, args; + models, model, i, len, args; if (this.data) { models = this.data.add.apply(this.data, arguments); @@ -3083,7 +3093,11 @@ Y.mix(Mutable.prototype, { args = toArray(arguments, 1, true); for (i = 0, len = models.length; i < len; ++i) { - models[i].save.apply(models[i], args); + model = models[i]; + + if (model.isNew()) { + models[i].save.apply(models[i], args); + } } } } @@ -3111,7 +3125,7 @@ Y.mix(Mutable.prototype, { @method removeRow @param {Object|String|Number} id The Model instance or identifier - @param {Object} [config]* Configuration to pass along + @param {Object} [config] Configuration to pass along @param {Function} [callback] Callback function for Model's `save()` @param {Error|null} callback.err If an error occurred or validation failed, this parameter will contain the error. If the sync operation @@ -3124,6 +3138,7 @@ Y.mix(Mutable.prototype, { **/ removeRow: function (id, config) { var modelList = this.data, + // Allow autoSync: true + addRow({ data }, { sync: false }) sync = (config && ('sync' in config)) ? config.sync : this.get('autoSync'), @@ -3183,7 +3198,7 @@ Y.mix(Mutable.prototype, { @method modifyRow @param {Object|String|Number} id The Model instance or identifier @param {Object} data New data values for the Model - @param {Object} [config]* Configuration to pass along to `setAttrs()` + @param {Object} [config] Configuration to pass along to `setAttrs()` @param {Function} [callback] Callback function for Model's `save()` @param {Error|null} callback.err If an error occurred or validation failed, this parameter will contain the error. If the sync operation @@ -3196,6 +3211,7 @@ Y.mix(Mutable.prototype, { **/ modifyRow: function (id, data, config) { var modelList = this.data, + // Allow autoSync: true + addRow({ data }, { sync: false }) sync = (config && ('sync' in config)) ? config.sync : this.get('autoSync'), @@ -3214,7 +3230,7 @@ Y.mix(Mutable.prototype, { model.setAttrs.apply(model, args); - if (sync) { + if (sync && !model.isNew()) { model.save.apply(model, args); } } @@ -3297,7 +3313,7 @@ Y.mix(Mutable.prototype, { fromCols, fromIndex, toCols, i, len; if (column) { - fromCols = column.parent ? column.parent.children : columns; + fromCols = column._parent ? column._parent.children : columns; fromIndex = arrayIndex(fromCols, column); if (fromIndex > -1) { @@ -3335,7 +3351,7 @@ Y.mix(Mutable.prototype, { cols, index; if (column) { - cols = column.parent ? column.parent.children : columns; + cols = column._parent ? column._parent.children : columns; index = Y.Array.indexOf(cols, column); if (index > -1) { @@ -3389,7 +3405,7 @@ as a callback to each Model's `save()` method. @method addRows @param {Object[]} data The data or Model instances to add -@param {Object} [config]* Configuration to pass along +@param {Object} [config] Configuration to pass along @param {Function} [callback] Callback function for each Model's `save()` @param {Error|null} callback.err If an error occurred or validation failed, this parameter will contain the error. If the sync operation @@ -3442,7 +3458,284 @@ Fired by the `moveColumn` method. @param {Object} index The destination index to move to **/ -}, 'gallery-2012.02.01-21-35', { requires: ['gallery-datatable-350-preview-base'] }); +}, 'gallery-2012.03.23-18-00', { requires: ['gallery-datatable-350-preview-base'] }); + +YUI.add('lang/gallery-datatable-350-preview-message', function (Y) { +Y.Intl.add('datatable-message', '', +{ + emptyMessage: "No data to display", + loadingMessage: "Loading..." +} +);}, 'gallery-2012.03.23-18-00'); + +YUI.add('lang/gallery-datatable-350-preview-message_en', function (Y) { +Y.Intl.add('datatable-message', 'en', +{ + emptyMessage: "No data to display", + loadingMessage: "Loading..." +} +);}, 'gallery-2012.03.23-18-00'); + +YUI.add('gallery-datatable-350-preview-message', function (Y) { +/** +Adds support for a message container to appear in the table. This can be used +to indicate loading progress, lack of records, or any other communication +needed. + +Features added to `Y.DataTable`, and made available for custom classes at +`Y.DataTable.Message`. + +@module datatable-message +@class DataTable.Message +@for DataTable +**/ +var Message; + +Y.namespace('DataTable').Message = Message = function () {}; + +Message.ATTRS = { + /** + Enables the display of messages in the table. Setting this to false will + prevent the message Node from being created and `showMessage` from doing + anything. + + @attribute showMessages + @type {Boolean} + @default true + **/ + showMessages: { + value: true, + validator: Y.Lang.isBoolean + } +}; + +Y.mix(Message.prototype, { + /** + Template used to generate the node that will be used to report messages. + + @property MESSAGE_TEMPLATE + @type {HTML} + @default
` used to display the messages. + + @method _afterMessageColumnsChange + @param {EventFacade} e The columnsChange event + @protected + **/ + _afterMessageColumnsChange: function (e) { + var contentNode; + + if (this._messageNode) { + contentNode = this._messageNode.one( + '.' + this.getClassName('message', 'content')); + + if (contentNode) { + contentNode.set('colSpan', this._displayColumns.length); + } + } + }, + + /** + Relays to `_uiSetMessage` to hide or show the message node. + + @method _afterMessageDataChange + @param {EventFacade} e The dataChange event + @protected + **/ + _afterMessageDataChange: function (e) { + this._uiSetMessage(); + }, + + /** + Removes the message node if `showMessages` is `false`, or relays to + `_uiSetMessage` if `true`. + + @method _afterShowMessagesChange + @param {EventFacade} e The showMessagesChange event + @protected + **/ + _afterShowMessagesChange: function (e) { + if (e.newVal) { + this._uiSetMessage(e); + } else if (this._messageNode) { + this.get('boundingBox').removeClass( + this.getClassName('message', 'visible')); + + this._messageNode.remove().destroy(true); + this._messageNode = null; + } + }, + + /** + Binds the events necessary to keep the message node in sync with the current + table and configuration state. + + @method _bindMessageUI + @protected + **/ + _bindMessageUI: function () { + this.after(['dataChange', '*:add', '*:remove', '*:reset'], + Y.bind('_afterMessageDataChange', this)); + + this.after('columnsChange', Y.bind('_afterMessageColumnsChange', this)); + + this.after('showMessagesChange', + Y.bind('_afterShowMessagesChange', this)); + }, + + /** + Merges in the message related strings and hooks into the rendering cycle to + also render and bind the message node. + + @method initializer + @protected + **/ + initializer: function () { + this._initMessageStrings(); + + if (this.get('showMessages')) { + this.after('renderBody', Y.bind('_initMessageNode', this)); + } + + this.after(Y.bind('_bindMessageUI', this), this, 'bindUI'); + this.after(Y.bind('_syncMessageUI', this), this, 'syncUI'); + }, + + /** + Creates the `_messageNode` property from the configured `MESSAGE_TEMPLATE` + and inserts it before the ``'s `` node. + + @method _initMessageNode + @protected + **/ + _initMessageNode: function () { + if (!this._messageNode) { + this._messageNode = Y.Node.create( + Y.Lang.sub(this.MESSAGE_TEMPLATE, { + className: this.getClassName('message'), + contentClass: this.getClassName('message', 'content'), + colspan: this._displayColumns.length || 1 + })); + + this._tableNode.insertBefore(this._messageNode, this._tbodyNode); + } + }, + + /** + Add the messaging related strings to the `strings` map. + + @method _initMessageStrings + @protected + **/ + _initMessageStrings: function () { + // Not a valueFn because other class extensions will want to add to it + this.set('strings', Y.mix((this.get('strings') || {}), + Y.Intl.get('datatable-message'))); + }, + + /** + Node used to display messages from `showMessage`. + + @property _messageNode + @type {Node} + @value `undefined` (not initially set) + **/ + //_messageNode: null, + + /** + Synchronizes the message UI with the table state. + + @method _syncMessageUI + @protected + **/ + _syncMessageUI: function () { + this._uiSetMessage(); + }, + + /** + Calls `hideMessage` or `showMessage` as appropriate based on the presence of + records in the `data` ModelList. + + This is called when `data` is reset or records are added or removed. Also, + if the `showMessages` attribute is updated. In either case, if the + triggering event has a `message` property on the EventFacade, it will be + passed to `showMessage` (if appropriate). If no such property is on the + facade, the `emptyMessage` will be used (see the strings). + + @method _uiSetMessage + @param {EventFacade} e The columnsChange event + @protected + **/ + _uiSetMessage: function (e) { + if (!this.data.size()) { + this.showMessage((e && e.message) || 'emptyMessage'); + } else { + this.hideMessage(); + } + } +}); + + +if (Y.Lang.isFunction(Y.DataTable)) { + Y.Base.mix(Y.DataTable, [ Message ]); +} +}, 'gallery-2012.03.23-18-00', { requires: ['gallery-datatable-350-preview-base'] }); YUI.add('gallery-datatable-350-preview-column-widths', function (Y) { /** @@ -3717,7 +4010,7 @@ Y.mix(ColumnWidths.prototype, { Y.DataTable.ColumnWidths = ColumnWidths; Y.Base.mix(Y.DataTable, [ColumnWidths]); -}, 'gallery-2012.02.01-21-35', { requires: ['gallery-datatable-350-preview-base'] }); +}, 'gallery-2012.03.23-18-00', { requires: ['gallery-datatable-350-preview-base'] }); YUI.add('gallery-datatable-350-preview-scroll', function (Y) { /** @@ -3744,7 +4037,7 @@ the following values: * 'y' - Activate vertical scrolling only. Requires the `height` attribute is also set. - @module @datatable-scroll + @module datatable-scroll @class DataTable.Scrollable @for DataTable **/ @@ -4478,11 +4771,40 @@ Y.mix(Scrollable.prototype, { **/ //_yScrollNode - // TODO: Add _xScrollNode + /** + Overflow Node used to contain the table headers and data in a horizontally + scrolling table. + + @property _xScrollNode + @type {Node} + @default undefined (not initially set) + @protected + **/ + //_xScrollNode }, true); Y.Base.mix(Y.DataTable, [Scrollable]); -}, 'gallery-2012.02.01-21-35', { requires: ['gallery-datatable-350-preview-base', 'gallery-datatable-350-preview-column-widths', 'dom-screen'] }); +}, 'gallery-2012.03.23-18-00', { requires: ['gallery-datatable-350-preview-base', 'gallery-datatable-350-preview-column-widths', 'dom-screen'] }); + +YUI.add('lang/gallery-datatable-350-preview-sort', function (Y) { +Y.Intl.add('datatable-sort', '', +{ + asc: "Ascending", + desc: "Descending", + sortBy: "Sort by {column}", + reverseSortBy: "Reverse sort by {column}" +} +);}, 'gallery-2012.03.23-18-00'); + +YUI.add('lang/gallery-datatable-350-preview-sort_en', function (Y) { +Y.Intl.add('datatable-sort', 'en', +{ + asc: "Ascending", + desc: "Descending", + sortBy: "Sort by {column}", + reverseSortBy: "Reverse sort by {column}" +} +);}, 'gallery-2012.03.23-18-00'); YUI.add('gallery-datatable-350-preview-sort', function (Y) { /** @@ -4572,7 +4894,8 @@ var YLang = Y.Lang, isArray = YLang.isArray, isObject = YLang.isObject, - toArray = Y.Array, + toArray = Y.Array, + sub = YLang.sub, dirMap = { asc : 1, @@ -4641,11 +4964,7 @@ Sortable.ATTRS = { @type {Object} @default (strings for current lang configured in the YUI instance config) **/ - strings: { - valueFn: function () { - return Y.Intl.get('datatable-sort'); - } - } + strings: {} }; Y.mix(Sortable.prototype, { @@ -4682,6 +5001,16 @@ Y.mix(Sortable.prototype, { })); }, + /** + Template for the node that will wrap the header content for sortable + columns. + + @property SORTABLE_HEADER_TEMPLATE + @type {HTML} + @value '
' + **/ + SORTABLE_HEADER_TEMPLATE: '
', + /** Reverse the current sort direction of one or more fields currently being sorted by. @@ -4740,15 +5069,39 @@ Y.mix(Sortable.prototype, { //-------------------------------------------------------------------------- // Protected properties and methods //-------------------------------------------------------------------------- + /** + Sorts the `data` ModelList based on the new `sortBy` configuration. + + @method _afterSortByChange + @param {EventFacade} e The `sortByChange` event + @protected + **/ + _afterSortByChange: function (e) { + // Can't use a setter because it's a chicken and egg problem. The + // columns need to be set up to translate, but columns are initialized + // from Core's initializer. So construction-time assignment would + // fail. + this._setSortBy(); + + // Don't sort unless sortBy has been set + if (this._sortBy.length) { + if (!this.data.comparator) { + this.data.comparator = this._sortComparator; + } + + this.data.sort(); + } + }, + /** Applies the sorting logic to the new ModelList if the `newVal` is a new ModelList. - @method _afterDataChange + @method _afterSortDataChange @param {EventFacade} e the `dataChange` event @protected **/ - _afterDataChange: function (e) { + _afterSortDataChange: function (e) { // object values always trigger a change event, but we only want to // call _initSortFn if the value passed to the `data` attribute was a // new ModelList, not a set of new data as an array, or even the same @@ -4759,26 +5112,21 @@ Y.mix(Sortable.prototype, { }, /** - Sorts the `data` ModelList based on the new `sortBy` configuration. + Checks if any of the fields in the modified record are fields that are + currently being sorted by, and if so, resorts the `data` ModelList. - @method _afterSortByChange - @param {EventFacade} e The `sortByChange` event + @method _afterSortRecordChange + @param {EventFacade} e The Model's `change` event @protected **/ - _afterSortByChange: function (e) { - // Can't use a setter because it's a chicken and egg problem. The - // columns need to be set up to translate, but columns are initialized - // from Core's initializer. So construction-time assignment would - // fail. - this._setSortBy(); + _afterSortRecordChange: function (e) { + var i, len; - // Don't sort unless sortBy has been set - if (this._sortBy.length) { - if (!this.data.comparator) { - this.data.comparator = this._sortComparator; + for (i = 0, len = this._sortBy.length; i < len; ++i) { + if (e.changed[this._sortBy[i].key]) { + this.data.sort(); + break; } - - this.data.sort(); } }, @@ -4892,12 +5240,15 @@ Y.mix(Sortable.prototype, { this._initSortFn(); + this._initSortStrings(); + this.after({ renderHeader : Y.bind('_renderSortable', this), - dataChange : Y.bind('_afterDataChange', this), + dataChange : Y.bind('_afterSortDataChange', this), sortByChange : Y.bind('_afterSortByChange', this), sortableChange: boundParseSortable, - columnsChange : boundParseSortable + columnsChange : boundParseSortable, + "*:change" : Y.bind('_afterSortRecordChange', this) }); this.publish('sort', { @@ -4955,6 +5306,18 @@ Y.mix(Sortable.prototype, { } }, + /** + Add the sort related strings to the `strings` map. + + @method _initSortStrings + @protected + **/ + _initSortStrings: function () { + // Not a valueFn because other class extensions will want to add to it + this.set('strings', Y.mix((this.get('strings') || {}), + Y.Intl.get('datatable-sort'))); + }, + /** Fires the `sort` event in response to user clicks on sortable column headers. @@ -5163,7 +5526,7 @@ Y.mix(Sortable.prototype, { ascClass = this.getClassName('sorted'), descClass = this.getClassName('sorted', 'desc'), linerClass = this.getClassName('sort', 'liner'), - i, len, col, node, content; + i, len, col, node, content, title; this.get('boundingBox').toggleClass( this.getClassName('sortable'), @@ -5196,7 +5559,16 @@ Y.mix(Sortable.prototype, { } } - Y.Node.create('
') + title = sub(this.getString( + (col.sortDir === 1) ? 'reverseSortBy' : 'sortBy'), { + column: col.abbr || col.label || + col.key || ('column ' + i) + }); + + Y.Node.create(Y.Lang.sub(this.SORTABLE_HEADER_TEMPLATE, { + className: linerClass, + title : title + })) .append(node.get('childNodes').toFrag()) .appendTo(node); } @@ -5235,16 +5607,21 @@ Y.mix(Sortable.prototype, { Y.DataTable.Sortable = Sortable; Y.Base.mix(Y.DataTable, [Sortable]); -}, 'gallery-2012.02.01-21-35', { requires: ['gallery-datatable-350-preview-base'] }); +}, 'gallery-2012.03.23-18-00', { requires: ['gallery-datatable-350-preview-base'] }); Y.use('gallery-datatable-350-preview-core', 'gallery-datatable-350-preview-head', 'gallery-datatable-350-preview-body', 'gallery-datatable-350-preview-base', 'gallery-datatable-350-preview-mutable', + 'lang/gallery-datatable-350-preview-message', + 'lang/gallery-datatable-350-preview-message_en', + 'gallery-datatable-350-preview-message', 'gallery-datatable-350-preview-column-widths', 'gallery-datatable-350-preview-scroll', - 'gallery-datatable-350-preview-sort'); + 'gallery-datatable-350-preview-sort', + 'lang/gallery-datatable-350-preview-sort', + 'lang/gallery-datatable-350-preview-sort_en'); -}, 'gallery-2012.02.01-21-35' ,{requires:['base-build', 'widget', 'model-list', 'view', 'escape', 'dom-screen']}); +}, 'gallery-2012.03.23-18-00' ,{requires:['base-build', 'widget', 'model-list', 'view', 'escape', 'dom-screen', 'intl']}); diff --git a/build/gallery-datatable-350-preview/gallery-datatable-350-preview-min.js b/build/gallery-datatable-350-preview/gallery-datatable-350-preview-min.js index f2341770ba..086d2b5895 100644 --- a/build/gallery-datatable-350-preview/gallery-datatable-350-preview-min.js +++ b/build/gallery-datatable-350-preview/gallery-datatable-350-preview-min.js @@ -1,6 +1,7 @@ -YUI.add("gallery-datatable-350-preview",function(a){YUI.add("gallery-datatable-350-preview-core",function(c){var f=c.Attribute.INVALID_VALUE,h=c.Lang,e=h.isFunction,n=h.isObject,i=h.isArray,d=h.isString,m=h.isNumber,k=h.sub,g=c.Array,l=c.Object.keys,j;function b(q){var r={},p;for(p in q){r[p]=q[p];}return r;}j=c.namespace("DataTable").Core=function(){};j.ATTRS={columns:{validator:i,getter:"_getColumns"},recordType:{setter:"_setRecordType",writeOnce:true},data:{value:[],setter:"_setData",getter:"_getData"},headerView:{validator:"_validateView",writeOnce:true},footerView:{validator:"_validateView",writeOnce:true},bodyView:{validator:"_validateView",writeOnce:true},summary:{value:"",setter:c.Escape.html},caption:{value:""},recordset:{setter:"_setRecordset",getter:"_getRecordset"},columnset:{setter:"_setColumnset",getter:"_getColumnset"}};c.mix(j.prototype,{CAPTION_TEMPLATE:'",COLGROUP_TEMPLATE:"",setColumnWidth:function(i,h){var g=this.getColumn(i),f=g&&b(this._displayColumns,g);if(f>-1){if(d(h)){h+="px";}g.width=h;this._setColumnWidth(f,h);}return this;},_createColumnGroup:function(){return e.Node.create(this.COLGROUP_TEMPLATE);},initializer:function(f){this.after("renderTable",function(g){this._uiSetColumns();this.after("columnsChange",this._uiSetColumns);});},_setColumnWidth:function(j,k){var h=this._colgroupNode,i=h&&h.all("col").item(j),l,g,f;if(i){if(k&&d(k)){k+="px";}i.setStyle("width",k);if(k&&e.Features.test("table","badColWidth")){l=this._tbodyNode&&this._tbodyNode.one("tr");g=l&&l.all("td").item(j);if(g){f=function(m){return parseInt(g.getComputedStyle(m),10)|0;};i.setStyle("width",parseInt(k,10)-f("paddingLeft")-f("paddingRight")-f("borderLeftWidth")-f("borderRightWidth")+"px");}}}},_uiSetColumns:function(){var k=this.COL_TEMPLATE,g=this._colgroupNode,j=this._displayColumns,h,f;if(!g){g=this._colgroupNode=this._createColumnGroup();this._tableNode.insertBefore(g,this._tableNode.one("> thead, > tfoot, > tbody"));}else{g.empty();}for(h=0,f=j.length;h
',TABLE_TEMPLATE:'',TBODY_TEMPLATE:'',TFOOT_TEMPLATE:'',THEAD_TEMPLATE:'',delegate:function(){var o=this.get("contentBox");return o.delegate.apply(o,arguments);},getCell:function(p,o){return this.body&&this.body.getCell&&this.body.getCell(p,o);},getColumn:function(q){var p,s,r,o,t;if(n(q)&&!i(q)){p=q;}else{p=this.get("columns."+q);}if(p){return p;}s=this.get("columns");if(m(q)||i(q)){q=g(q);t=s;for(r=0,o=q.length-1;t&&r tbody"));},_defRenderHeaderFn:function(o){o.view.render();this.head=o.view;this._theadNode=o.view.get("container");this._tableNode.insertBefore(this._theadNode,this._tableNode.one("> tfoot, > tbody"));},_defRenderTableFn:function(q){var o,p;this._tableNode=this._createTable();if(q.headerView){p=b(q.headerConfig||{});p.container=this._createTHead();o=new q.headerView(p);o.addTarget(this);this.fire("renderHeader",{view:o});}if(q.footerView){p=b(q.footerConfig||{});p.container=this._createTFoot();o=new q.footerView(p);o.addTarget(this);this.fire("renderFooter",{view:o});}if(q.bodyView){p=b(q.bodyConfig||{});p.container=this._createTBody();o=new q.bodyView(p);o.addTarget(this);this.fire("renderBody",{view:o});}},_getColumns:function(p,o){return o.length>8?this._columnMap:p;},_getColumnset:function(p,o){return this.get(o.replace(/^columnset/,"columns"));},_getData:function(o){return this.data||o;},_initColumns:function(){var o=this.get("columns"),p=this.get("recordType");if(!o){o=(p&&p.ATTRS)?l(p.ATTRS):[];this.set("columns",o,{silent:true});}this._setColumnMap(o);this._setDisplayColumns(o);},_initData:function(){var p=this.get("data"),q,o;if(i(p)){q=this.get("recordType");o=p;p=new c.ModelList();if(q){p.model=q;p.reset(o,{silent:true});}this.set("data",p,{silent:true});}this.data=p;this.data.addTarget(this);},_initEvents:function(){this.publish({renderTable:{fireOnce:true,defaultFn:c.bind("_defRenderTableFn",this)},renderHeader:{fireOnce:true,defaultFn:c.bind("_defRenderHeaderFn",this)},renderBody:{fireOnce:true,defaultFn:c.bind("_defRenderBodyFn",this)},renderFooter:{fireOnce:true,defaultFn:c.bind("_defRenderFooterFn",this)}});},initializer:function(){this._initColumns();this._initRecordType();this._initData();this._initViewConfig();this._initEvents();this.after("columnsChange",this._afterColumnsChange);this._UI_ATTRS={BIND:this._UI_ATTRS.BIND.concat(["caption","summary"]),SYNC:this._UI_ATTRS.SYNC.concat(["caption","summary"])};},_initRecordType:function(){var q,o,r,p;if(!this.get("recordType")){q=this.get("data");o=this._columnMap;if(q.model){r=q.model;}else{if(q.size&&q.size()){r=q.model=q.item(0).constructor;}else{if(i(q)&&q.length){r=(q[0].constructor.ATTRS)?q[0].constructor:this._createRecordClass(l(q[0]));}else{if(l(o).length){r=this._createRecordClass(l(o));}}}}if(r){this.set("recordType",r,{silent:true});if(!o||!o.length){this._initColumns();}}else{p=this.after(["columnsChange","recordTypeChange","dataChange"],function(s){p.detach();if(!this.data.model){this._initRecordType();this.data.model=this.get("recordType");}});}}},_initViewConfig:function(){this._viewConfig={source:this,cssPrefix:this._cssPrefix};this._headerConfig=c.Object(this._viewConfig);this._bodyConfig=c.Object(this._viewConfig);this._footerConfig=c.Object(this._viewConfig);},_parseColumns:function(o){var r={},p={};function q(x){var w,s,t,v,u,y;for(w=0,s=x.length;w{content}',ROW_TEMPLATE:'{content}',getClassName:function(){var i=c(arguments);i.unshift(this._cssPrefix);i.push(true);return d.apply(f,i);},render:function(){var s=this.get("container"),l=this.columns,m={abbr:"",_colspan:1,_rowspan:1},o,q,n,u,k,p,r,t;if(s&&l){p="";if(l.length){for(o=0,q=l.length;o=q){if(s.length>1){u=s[s.length-2];t=u[0][u[1]];t._colspan=0;for(o=0,q=v.length;o=0;--n){t=s[n][0][s[n][1]];l._headers.unshift(t._yuid);}if(k&&k.length){s.push([k,-1]);break;}else{l._rowspan=r-s.length+1;}}if(o>=q){s.pop();}}}return m;}});},"gallery-2012.02.01-21-35",{requires:["view","gallery-datatable-350-preview-core"]});YUI.add("gallery-datatable-350-preview-body",function(b){var f=b.Lang,g=f.isArray,k=f.sub,j=b.Escape.html,d=b.Array,h=b.bind,i=b.Object,c=b.ClassNameManager,e=c.getClassName;b.namespace("DataTable").BodyView=b.Base.create("tableBody",b.View,[],{CELL_TEMPLATE:'',ROW_TEMPLATE:''+"{content}"+"",getCell:function(o,m){var l=this.get("container"),n;if(l){n=l.getDOMNode().rows[+o];n&&(n=n.cells[+m]);}return b.one(n);},getClassName:function(){var l=d(arguments);l.unshift(this._cssPrefix);l.push(true);return e.apply(c,l);},getRow:function(m){var l=this.get("container");return b.one(l&&l.getDOMNode().rows[+m]);},render:function(){var l=this.get("container"),n=this.get("modelList"),m=this.columns;this._createRowTemplate(m);if(l&&n){l.setContent(this._createDataHTML(m));this._applyNodeFormatters(l,m);}this.bindUI();return this;},_afterColumnsChange:function(l){this.columns=this._parseColumns(l.newVal);this.render();},_afterDataChange:function(l){this.render();},_applyNodeFormatters:function(r,m){var l=this.source,p=this.get("modelList"),o=[],t=r.getDOMNode(),n="."+this.getClassName("liner"),q,s;for(q=0,s=m.length;q-1){s=m;for(o=0,p=n.length-1;s&&o-1){n.splice(k,1);this.set("columns",l,{originEvent:o});}}},initializer:function(){this.publish({addColumn:{defaultFn:b.bind("_defAddColumnFn",this)},removeColumn:{defaultFn:b.bind("_defRemoveColumnFn",this)},moveColumn:{defaultFn:b.bind("_defMoveColumnFn",this)},modifyColumn:{defaultFn:b.bind("_defModifyColumnFn",this)}});}});f.prototype.addRows=f.prototype.addRow;if(g.isFunction(b.DataTable)){b.Base.mix(b.DataTable,[f]);}},"gallery-2012.02.01-21-35",{requires:["gallery-datatable-350-preview-base"]});YUI.add("gallery-datatable-350-preview-column-widths",function(e){var d=e.Lang.isNumber,b=e.Array.indexOf;e.Features.add("table","badColWidth",{test:function(){var f=e.one("body"),h,g;if(f){h=f.insertBefore(''+''+""+'"+"
'+"."+"
",f.get("firstChild")); -g=h.one("td").getComputedStyle("width")!=="1px";h.remove(true);}return g;}});function c(){}e.mix(c.prototype,{COL_TEMPLATE:"
',_SCROLLBAR_TEMPLATE:'
',_X_SCROLLER_TEMPLATE:'
',_Y_SCROLLER_TEMPLATE:'
',_addVirtualScrollbar:function(){var h=this._yScrollNode,i=g.DOM.getScrollbarWidth()+"px",j=g.Node.create(g.Lang.sub(this._SCROLLBAR_TEMPLATE,{className:this.getClassName("virtual","scrollbar")}));this._scrollbarNode=j;j.setStyles({height:h.get("clientHeight")+"px",width:i,bottom:i});j.one("div").setStyle("height",h.get("scrollHeight")+"px");this._virtualScrollHandle=new g.EventHandle([j.on("scroll",g.rbind("_syncVirtualScroll",this)),h.on("scroll",g.rbind("_syncVirtualScroll",this))]);this.get("contentBox").appendChild(j);},_afterContentChange:function(h){this._mergeXScrollContent();this._mergeYScrollContent();this._uiSetWidth(this.get("width"));this._syncScrollUI();},_afterScrollableChange:function(h){this._uiSetScrollable();this._syncScrollUI();},_afterScrollHeightChange:function(h){this._yScroll&&this._syncScrollUI();},_bindScrollUI:function(){this.after(["dataChange","columnsChange","captionChange","heightChange"],g.bind("_afterContentChange",this));this.data.after(["add","remove","reset","*:change"],g.bind("_afterContentChange",this));},_calcScrollHeight:function(){var h=this._yScrollNode;return this.get("contentBox").get("clientHeight")-h.get("offsetTop")-h.get("offsetHeight")+h.get("clientHeight");},_createXScrollNode:function(){if(!this._xScrollNode){this._xScrollNode=g.Node.create(g.Lang.sub(this._X_SCROLLER_TEMPLATE,{className:this.getClassName("x","scroller")}));}},_createYScrollNode:function(){if(!this._yScrollNode){this._yScrollNode=g.Node.create(g.Lang.sub(this._Y_SCROLLER_TEMPLATE,{className:this.getClassName("y","scroller"),tableClassName:this.getClassName("y","scroll","table")}));}},_fixColumnWidths:function(){var l=this._tbodyNode,o=l.get("parentNode"),j=l.one("tr"),p=j&&j.all("td"),q=g.DOM.getScrollbarWidth(),h=[],k,m,n;if(p){this._tableNode.appendChild(this._tbodyNode);k=p.size()-1;n=p.item(k);this._setColumnWidth(k,(n.get("offsetWidth")+q)+"px");p.pop();h=p.get("offsetWidth");for(k=0,m=h.length;k tr").getDOMNodes(),j,k,h;for(k=0,h=l.length;k tr").getDOMNodes(),m,j,k,h;j=g.one(l[0].cells[l[0].cells.length-1]);m=(g.DOM.getScrollbarWidth()+parseInt(j.getComputedStyle("paddingRight"),10))+"px";for(k=0,h=l.length;k-1;this._yScroll=h&&j.indexOf("y")>-1;},_setYScrollColWidths:function(){var k=this._yScrollNode,j=k&&k.one("> table"),i,h;if(j){k.all("colgroup,col").remove();i=this._colgroupNode.cloneNode(true);i.set("id",g.stamp(i));if(!g.Features.test("table","badColWidth")){h=i.all("col").pop();h.setStyle("width",(parseInt(h.getStyle("width"),10)-1-g.DOM.getScrollbarWidth())+"px");}j.insertBefore(i,j.one("> thead, > tfoot, > tbody"));}},_splitXScrollContent:function(){var h;this._createXScrollNode();this._tableNode.wrap(this._xScrollNode);if(this._yScrollNode){this._xScrollNode.append(this._yScrollNode);}if(this._captionNode){h=g.Node.create(g.Lang.sub(this._CAPTION_TABLE_TEMPLATE,{className:this.getClassName("caption","table")}));h.setStyle("width",this.get("width"));h.insertBefore(this._captionNode,h.get("firstChild"));this.get("contentBox").insertBefore(h,this._xScrollNode);}},_splitYScrollContent:function(){var j=this._tableNode,k=this._yScrollTable,h,i;this.get("boundingBox").addClass(this.getClassName("scrollable","y"));if(!k){this._fixColumnWidths();this._setHeaderScrollPadding();i=parseInt(j.getComputedStyle("width"),10);j.setStyle("width",i+"px");this._createYScrollNode();k=this._yScrollNode;h=k.one("table");h.append(this._tbodyNode);j.insert(k,"after");k.setStyles({height:this._calcScrollHeight()+"px",width:(i-2)+"px"});h.setStyle("width",k.get("clientWidth")+"px");}this._setYScrollColWidths();},_syncScrollUI:function(){var h=this.get("contentBox"),j=this._yScrollNode||h,i=j.one("table");this._uiSetDim("width","");this._tableNode.setStyle("width","");this._uiSetScrollable();if(this._yScroll){if(i.get("scrollHeight")>j.get("clientHeight")){this._splitYScrollContent();}else{this._mergeYScrollContent();}}else{this._mergeYScrollContent();}if(this._xScroll){if(i.get("scrollWidth")>parseInt(this.get("width"),10)){this._splitXScrollContent();if(this._yScrollNode){this._yScrollNode.setStyle("height",(this._yScrollNode.get("offsetHeight")-g.DOM.getScrollbarWidth())+"px");if(g.DOM.getScrollbarWidth()){this._addVirtualScrollbar();}}}else{this._mergeXScrollContent();}}else{this._mergeXScrollContent();}this._uiSetDim("width",this.get("width"));},_syncVirtualScroll:function(i){var h=(i.currentTarget===this._scrollbarNode)?this._yScrollNode:this._scrollbarNode;h.set("scrollTop",i.currentTarget.get("scrollTop"));},_uiSetWidth:function(h){var i=this._xScrollNode||this._yScrollNode;if(f(h)){h+=this.DEF_UNIT;}if(i){this._mergeXScrollContent();this._mergeYScrollContent();this._syncScrollUI();}else{this._uiSetDim("width",h);this._tableNode.setStyle("width",h);}},_uiSetScrollable:function(){this.get("boundingBox").toggleClass(this.getClassName("scrollable","x"),this._xScroll).toggleClass(this.getClassName("scrollable","y"),this._yScroll);}},true);g.Base.mix(g.DataTable,[c]);},"gallery-2012.02.01-21-35",{requires:["gallery-datatable-350-preview-base","gallery-datatable-350-preview-column-widths","dom-screen"]});YUI.add("gallery-datatable-350-preview-sort",function(b){var h=b.Lang,e=h.isBoolean,c=h.isString,g=h.isArray,j=h.isObject,f=b.Array,i={asc:1,desc:-1,"1":1,"-1":-1};function d(){}d.ATTRS={sortable:{value:"auto",validator:"_validateSortable"},sortBy:{validator:"_validateSortBy",getter:"_getSortBy"},strings:{valueFn:function(){return b.Intl.get("datatable-sort");}}};b.mix(d.prototype,{sort:function(k,l){return this.fire("sort",b.merge((l||{}),{sortBy:k||this.get("sortBy")}));},toggleSort:function(l,r){var q=this._sortBy,s=[],n,p,m,k,o;for(n=0,p=q.length;n=0;--n){if(s[m][k]){s[m][k]*=-1;break;}}}}else{for(n=0,p=s.length;nq)?n:((l=0;--m){if(!n[m].sortable){n.splice(m,1);}}}}}this._sortable=n;},_renderSortable:function(){this._uiSetSortable();this._bindSortUI();},_setSortBy:function(){var n=this._displayColumns,s=this.get("sortBy")||[],p=" "+this.getClassName("sorted"),o,q,k,l,r,m;this._sortBy=[];for(o=0,q=n.length;o').append(l.get("childNodes").toFrag()).appendTo(l);}}},_validateSortable:function(k){return k==="auto"||e(k)||g(k);},_validateSortBy:function(k){return k===null||c(k)||j(k,true)||(g(k)&&(c(k[0])||j(k,true)));}},true);b.DataTable.Sortable=d;b.Base.mix(b.DataTable,[d]);},"gallery-2012.02.01-21-35",{requires:["gallery-datatable-350-preview-base"]});a.use("gallery-datatable-350-preview-core","gallery-datatable-350-preview-head","gallery-datatable-350-preview-body","gallery-datatable-350-preview-base","gallery-datatable-350-preview-mutable","gallery-datatable-350-preview-column-widths","gallery-datatable-350-preview-scroll","gallery-datatable-350-preview-sort");},"gallery-2012.02.01-21-35",{requires:["base-build","widget","model-list","view","escape","dom-screen"]}); \ No newline at end of file +YUI.add("gallery-datatable-350-preview",function(a){YUI.add("gallery-datatable-350-preview-core",function(c){var f=c.Attribute.INVALID_VALUE,h=c.Lang,e=h.isFunction,n=h.isObject,i=h.isArray,d=h.isString,m=h.isNumber,k=h.sub,g=c.Array,l=c.Object.keys,j;function b(q){var r={},p;for(p in q){r[p]=q[p];}return r;}j=c.namespace("DataTable").Core=function(){};j.ATTRS={columns:{validator:i,getter:"_getColumns"},recordType:{setter:"_setRecordType",writeOnce:true},data:{value:[],setter:"_setData",getter:"_getData"},headerView:{validator:"_validateView",writeOnce:true},footerView:{validator:"_validateView",writeOnce:true},bodyView:{validator:"_validateView",writeOnce:true},summary:{value:"",setter:c.Escape.html},caption:{value:""},recordset:{setter:"_setRecordset",getter:"_getRecordset"},columnset:{setter:"_setColumnset",getter:"_getColumnset"}};c.mix(j.prototype,{CAPTION_TEMPLATE:'
',TABLE_TEMPLATE:'',TBODY_TEMPLATE:'',TFOOT_TEMPLATE:'',THEAD_TEMPLATE:'',delegate:function(){var o=this.get("contentBox");return o.delegate.apply(o,arguments);},getCell:function(p,o){return this.body&&this.body.getCell&&this.body.getCell(p,o);},getColumn:function(q){var p,s,r,o,t;if(n(q)&&!i(q)){p=q;}else{p=this.get("columns."+q);}if(p){return p;}s=this.get("columns");if(m(q)||i(q)){q=g(q);t=s;for(r=0,o=q.length-1;t&&r tbody"));},_defRenderHeaderFn:function(o){o.view.render();this.head=o.view;this._theadNode=o.view.get("container");this._tableNode.insertBefore(this._theadNode,this._tableNode.one("> tfoot, > tbody"));},_defRenderTableFn:function(q){var o,p;this._tableNode=this._createTable();if(q.headerView){p=b(q.headerConfig||{});p.container=this._createTHead();o=new q.headerView(p);o.addTarget(this);this.fire("renderHeader",{view:o});}if(q.footerView){p=b(q.footerConfig||{});p.container=this._createTFoot();o=new q.footerView(p);o.addTarget(this);this.fire("renderFooter",{view:o});}if(q.bodyView){p=b(q.bodyConfig||{});p.container=this._createTBody();o=new q.bodyView(p);o.addTarget(this);this.fire("renderBody",{view:o});}},_getColumns:function(p,o){return o.length>8?this._columnMap:p;},_getColumnset:function(p,o){return this.get(o.replace(/^columnset/,"columns"));},_getData:function(o){return this.data||o;},_initColumns:function(){var o=this.get("columns"),p=this.get("recordType");if(!o){o=(p&&p.ATTRS)?l(p.ATTRS):[];this.set("columns",o,{silent:true});}this._setColumnMap(o);this._setDisplayColumns(o);},_initData:function(){var p=this.get("data"),q,o;if(i(p)){q=this.get("recordType");o=p;p=new c.ModelList();if(q){p.model=q;p.reset(o,{silent:true});}this.set("data",p,{silent:true});}this.data=p;this.data.addTarget(this);},_initEvents:function(){this.publish({renderTable:{fireOnce:true,defaultFn:c.bind("_defRenderTableFn",this)},renderHeader:{fireOnce:true,defaultFn:c.bind("_defRenderHeaderFn",this)},renderBody:{fireOnce:true,defaultFn:c.bind("_defRenderBodyFn",this)},renderFooter:{fireOnce:true,defaultFn:c.bind("_defRenderFooterFn",this)}});},initializer:function(){this._initColumns();this._initRecordType();this._initData();this._initViewConfig();this._initEvents();this.after("columnsChange",this._afterColumnsChange);this._UI_ATTRS={BIND:this._UI_ATTRS.BIND.concat(["caption","summary"]),SYNC:this._UI_ATTRS.SYNC.concat(["caption","summary"])};},_initRecordType:function(){var r,o,s,q,p;if(!this.get("recordType")){r=this.get("data");o=this._columnMap;if(r.model){s=r.model;}else{if(r.size&&r.size()){s=r.model=r.item(0).constructor;}else{if(i(r)&&r.length){s=(r[0].constructor.ATTRS)?r[0].constructor:this._createRecordClass(l(r[0]));}else{p=l(o);if(p.length){s=this._createRecordClass(p);}}}}if(s){this.set("recordType",s,{silent:true});if(!o||!o.length){this._initColumns();}}else{q=this.after(["columnsChange","recordTypeChange","dataChange"],function(t){q.detach();if(!this.data.model){this._initRecordType();this.data.model=this.get("recordType");}});}}},_initViewConfig:function(){this._viewConfig={source:this,cssPrefix:this._cssPrefix};this._headerConfig=c.Object(this._viewConfig);this._bodyConfig=c.Object(this._viewConfig);this._footerConfig=c.Object(this._viewConfig);},_parseColumns:function(o){var r={},p={};function q(x){var w,s,t,v,u,y;for(w=0,s=x.length;w{content}',ROW_TEMPLATE:"{content}",getClassName:function(){var i=c(arguments);i.unshift(this._cssPrefix);i.push(true);return d.apply(f,i);},render:function(){var s=this.get("container"),l=this.columns,m={_colspan:1,_rowspan:1,abbr:""},o,q,n,u,k,p,r,t;if(s&&l){p="";if(l.length){for(o=0,q=l.length;o=q){if(s.length>1){u=s[s.length-2];t=u[0][u[1]];t._colspan=0;for(o=0,q=v.length;o=0;--n){t=s[n][0][s[n][1]];l._headers.unshift(t._yuid);}if(k&&k.length){s.push([k,-1]);break;}else{l._rowspan=r-s.length+1;}}if(o>=q){s.pop();}}}return m;}});},"gallery-2012.03.23-18-00",{requires:["view","gallery-datatable-350-preview-core"]});YUI.add("gallery-datatable-350-preview-body",function(b){var f=b.Lang,g=f.isArray,k=f.sub,j=b.Escape.html,d=b.Array,h=b.bind,i=b.Object,c=b.ClassNameManager,e=c.getClassName;b.namespace("DataTable").BodyView=b.Base.create("tableBody",b.View,[],{CELL_TEMPLATE:'',ROW_TEMPLATE:'{content}',getCell:function(o,m){var l=this.get("container"),n;if(l){n=l.getDOMNode().rows[+o];n&&(n=n.cells[+m]);}return b.one(n);},getClassName:function(){var l=d(arguments);l.unshift(this._cssPrefix);l.push(true);return e.apply(c,l);},getRow:function(m){var l=this.get("container");return b.one(l&&l.getDOMNode().rows[+m]);},render:function(){var l=this.get("container"),n=this.get("modelList"),m=this.columns;this._createRowTemplate(m);if(l&&n){l.setContent(this._createDataHTML(m));this._applyNodeFormatters(l,m);}this.bindUI();return this;},_afterColumnsChange:function(l){this.columns=this._parseColumns(l.newVal);this.render();},_afterDataChange:function(l){this.render();},_applyNodeFormatters:function(r,m){var l=this.source,p=this.get("modelList"),o=[],t=r.getDOMNode(),n="."+this.getClassName("liner"),q,s;for(q=0,s=m.length;q1?'headers="'+n._headers.join(" ")+'"':"";l={content:"{"+p+"}",headers:m,className:this.getClassName("col",p)+" "+(n.className||"")+" "+this.getClassName("cell")+" {"+p+"-className}"};if(n.nodeFormatter){l.content="";}r+=k(n.cellTemplate||u,l);}this._rowTemplate=k(this.ROW_TEMPLATE,{content:r});},destructor:function(){(new b.EventHandle(i.values(this._eventHandles))).detach();},initializer:function(l){var m=l.cssPrefix||(l.source||{}).cssPrefix;this.source=l.source;this.columns=this._parseColumns(l.columns);this._eventHandles={};if(m){this._cssPrefix=m;}this.CLASS_ODD=this.getClassName("odd");this.CLASS_EVEN=this.getClassName("even");},_parseColumns:function(p,o){var m,n,l;o||(o=[]);if(g(p)&&p.length){for(n=0,l=p.length;n-1){s=m;for(o=0,p=n.length-1;s&&o-1){n.splice(k,1);this.set("columns",l,{originEvent:o});}}},initializer:function(){this.publish({addColumn:{defaultFn:b.bind("_defAddColumnFn",this)},removeColumn:{defaultFn:b.bind("_defRemoveColumnFn",this)},moveColumn:{defaultFn:b.bind("_defMoveColumnFn",this)},modifyColumn:{defaultFn:b.bind("_defModifyColumnFn",this)}});}});f.prototype.addRows=f.prototype.addRow;if(g.isFunction(b.DataTable)){b.Base.mix(b.DataTable,[f]);}},"gallery-2012.03.23-18-00",{requires:["gallery-datatable-350-preview-base"]});YUI.add("lang/gallery-datatable-350-preview-message",function(b){b.Intl.add("datatable-message","",{emptyMessage:"No data to display",loadingMessage:"Loading..."});},"gallery-2012.03.23-18-00");YUI.add("lang/gallery-datatable-350-preview-message_en",function(b){b.Intl.add("datatable-message","en",{emptyMessage:"No data to display",loadingMessage:"Loading..."});},"gallery-2012.03.23-18-00");YUI.add("gallery-datatable-350-preview-message",function(c){var b; +c.namespace("DataTable").Message=b=function(){};b.ATTRS={showMessages:{value:true,validator:c.Lang.isBoolean}};c.mix(b.prototype,{MESSAGE_TEMPLATE:'',hideMessage:function(){this.get("boundingBox").removeClass(this.getClassName("message","visible"));return this;},showMessage:function(e){var d=this.getString(e)||e;if(!this._messageNode){this._initMessageNode();}if(this.get("showMessages")){if(d){this._messageNode.one("."+this.getClassName("message","content")).setContent(d);this.get("boundingBox").addClass(this.getClassName("message","visible"));}else{this.hideMessage();}}return this;},_afterMessageColumnsChange:function(f){var d;if(this._messageNode){d=this._messageNode.one("."+this.getClassName("message","content"));if(d){d.set("colSpan",this._displayColumns.length);}}},_afterMessageDataChange:function(d){this._uiSetMessage();},_afterShowMessagesChange:function(d){if(d.newVal){this._uiSetMessage(d);}else{if(this._messageNode){this.get("boundingBox").removeClass(this.getClassName("message","visible"));this._messageNode.remove().destroy(true);this._messageNode=null;}}},_bindMessageUI:function(){this.after(["dataChange","*:add","*:remove","*:reset"],c.bind("_afterMessageDataChange",this));this.after("columnsChange",c.bind("_afterMessageColumnsChange",this));this.after("showMessagesChange",c.bind("_afterShowMessagesChange",this));},initializer:function(){this._initMessageStrings();if(this.get("showMessages")){this.after("renderBody",c.bind("_initMessageNode",this));}this.after(c.bind("_bindMessageUI",this),this,"bindUI");this.after(c.bind("_syncMessageUI",this),this,"syncUI");},_initMessageNode:function(){if(!this._messageNode){this._messageNode=c.Node.create(c.Lang.sub(this.MESSAGE_TEMPLATE,{className:this.getClassName("message"),contentClass:this.getClassName("message","content"),colspan:this._displayColumns.length||1}));this._tableNode.insertBefore(this._messageNode,this._tbodyNode);}},_initMessageStrings:function(){this.set("strings",c.mix((this.get("strings")||{}),c.Intl.get("datatable-message")));},_syncMessageUI:function(){this._uiSetMessage();},_uiSetMessage:function(d){if(!this.data.size()){this.showMessage((d&&d.message)||"emptyMessage");}else{this.hideMessage();}}});if(c.Lang.isFunction(c.DataTable)){c.Base.mix(c.DataTable,[b]);}},"gallery-2012.03.23-18-00",{requires:["gallery-datatable-350-preview-base"]});YUI.add("gallery-datatable-350-preview-column-widths",function(e){var d=e.Lang.isNumber,b=e.Array.indexOf;e.Features.add("table","badColWidth",{test:function(){var f=e.one("body"),h,g;if(f){h=f.insertBefore('
{content}
'+''+""+'"+"
'+"."+"
",f.get("firstChild"));g=h.one("td").getComputedStyle("width")!=="1px";h.remove(true);}return g;}});function c(){}e.mix(c.prototype,{COL_TEMPLATE:"
',_SCROLLBAR_TEMPLATE:'
',_X_SCROLLER_TEMPLATE:'
',_Y_SCROLLER_TEMPLATE:'
',_addVirtualScrollbar:function(){var h=this._yScrollNode,i=g.DOM.getScrollbarWidth()+"px",j=g.Node.create(g.Lang.sub(this._SCROLLBAR_TEMPLATE,{className:this.getClassName("virtual","scrollbar")}));this._scrollbarNode=j;j.setStyles({height:h.get("clientHeight")+"px",width:i,bottom:i});j.one("div").setStyle("height",h.get("scrollHeight")+"px");this._virtualScrollHandle=new g.EventHandle([j.on("scroll",g.rbind("_syncVirtualScroll",this)),h.on("scroll",g.rbind("_syncVirtualScroll",this))]);this.get("contentBox").appendChild(j);},_afterContentChange:function(h){this._mergeXScrollContent();this._mergeYScrollContent();this._uiSetWidth(this.get("width"));this._syncScrollUI();},_afterScrollableChange:function(h){this._uiSetScrollable();this._syncScrollUI();},_afterScrollHeightChange:function(h){this._yScroll&&this._syncScrollUI(); +},_bindScrollUI:function(){this.after(["dataChange","columnsChange","captionChange","heightChange"],g.bind("_afterContentChange",this));this.data.after(["add","remove","reset","*:change"],g.bind("_afterContentChange",this));},_calcScrollHeight:function(){var h=this._yScrollNode;return this.get("contentBox").get("clientHeight")-h.get("offsetTop")-h.get("offsetHeight")+h.get("clientHeight");},_createXScrollNode:function(){if(!this._xScrollNode){this._xScrollNode=g.Node.create(g.Lang.sub(this._X_SCROLLER_TEMPLATE,{className:this.getClassName("x","scroller")}));}},_createYScrollNode:function(){if(!this._yScrollNode){this._yScrollNode=g.Node.create(g.Lang.sub(this._Y_SCROLLER_TEMPLATE,{className:this.getClassName("y","scroller"),tableClassName:this.getClassName("y","scroll","table")}));}},_fixColumnWidths:function(){var l=this._tbodyNode,o=l.get("parentNode"),j=l.one("tr"),p=j&&j.all("td"),q=g.DOM.getScrollbarWidth(),h=[],k,m,n;if(p){this._tableNode.appendChild(this._tbodyNode);k=p.size()-1;n=p.item(k);this._setColumnWidth(k,(n.get("offsetWidth")+q)+"px");p.pop();h=p.get("offsetWidth");for(k=0,m=h.length;k tr").getDOMNodes(),j,k,h;for(k=0,h=l.length;k tr").getDOMNodes(),m,j,k,h;j=g.one(l[0].cells[l[0].cells.length-1]);m=(g.DOM.getScrollbarWidth()+parseInt(j.getComputedStyle("paddingRight"),10))+"px";for(k=0,h=l.length;k-1;this._yScroll=h&&j.indexOf("y")>-1;},_setYScrollColWidths:function(){var k=this._yScrollNode,j=k&&k.one("> table"),i,h;if(j){k.all("colgroup,col").remove();i=this._colgroupNode.cloneNode(true);i.set("id",g.stamp(i));if(!g.Features.test("table","badColWidth")){h=i.all("col").pop();h.setStyle("width",(parseInt(h.getStyle("width"),10)-1-g.DOM.getScrollbarWidth())+"px");}j.insertBefore(i,j.one("> thead, > tfoot, > tbody"));}},_splitXScrollContent:function(){var h;this._createXScrollNode();this._tableNode.wrap(this._xScrollNode);if(this._yScrollNode){this._xScrollNode.append(this._yScrollNode);}if(this._captionNode){h=g.Node.create(g.Lang.sub(this._CAPTION_TABLE_TEMPLATE,{className:this.getClassName("caption","table")}));h.setStyle("width",this.get("width"));h.insertBefore(this._captionNode,h.get("firstChild"));this.get("contentBox").insertBefore(h,this._xScrollNode);}},_splitYScrollContent:function(){var j=this._tableNode,k=this._yScrollTable,h,i;this.get("boundingBox").addClass(this.getClassName("scrollable","y"));if(!k){this._fixColumnWidths();this._setHeaderScrollPadding();i=parseInt(j.getComputedStyle("width"),10);j.setStyle("width",i+"px");this._createYScrollNode();k=this._yScrollNode;h=k.one("table");h.append(this._tbodyNode);j.insert(k,"after");k.setStyles({height:this._calcScrollHeight()+"px",width:(i-2)+"px"});h.setStyle("width",k.get("clientWidth")+"px");}this._setYScrollColWidths();},_syncScrollUI:function(){var h=this.get("contentBox"),j=this._yScrollNode||h,i=j.one("table");this._uiSetDim("width","");this._tableNode.setStyle("width","");this._uiSetScrollable();if(this._yScroll){if(i.get("scrollHeight")>j.get("clientHeight")){this._splitYScrollContent();}else{this._mergeYScrollContent();}}else{this._mergeYScrollContent();}if(this._xScroll){if(i.get("scrollWidth")>parseInt(this.get("width"),10)){this._splitXScrollContent();if(this._yScrollNode){this._yScrollNode.setStyle("height",(this._yScrollNode.get("offsetHeight")-g.DOM.getScrollbarWidth())+"px");if(g.DOM.getScrollbarWidth()){this._addVirtualScrollbar();}}}else{this._mergeXScrollContent();}}else{this._mergeXScrollContent();}this._uiSetDim("width",this.get("width"));},_syncVirtualScroll:function(i){var h=(i.currentTarget===this._scrollbarNode)?this._yScrollNode:this._scrollbarNode;h.set("scrollTop",i.currentTarget.get("scrollTop"));},_uiSetWidth:function(h){var i=this._xScrollNode||this._yScrollNode;if(f(h)){h+=this.DEF_UNIT;}if(i){this._mergeXScrollContent();this._mergeYScrollContent();this._syncScrollUI();}else{this._uiSetDim("width",h);this._tableNode.setStyle("width",h);}},_uiSetScrollable:function(){this.get("boundingBox").toggleClass(this.getClassName("scrollable","x"),this._xScroll).toggleClass(this.getClassName("scrollable","y"),this._yScroll);}},true);g.Base.mix(g.DataTable,[c]);},"gallery-2012.03.23-18-00",{requires:["gallery-datatable-350-preview-base","gallery-datatable-350-preview-column-widths","dom-screen"]}); +YUI.add("lang/gallery-datatable-350-preview-sort",function(b){b.Intl.add("datatable-sort","",{asc:"Ascending",desc:"Descending",sortBy:"Sort by {column}",reverseSortBy:"Reverse sort by {column}"});},"gallery-2012.03.23-18-00");YUI.add("lang/gallery-datatable-350-preview-sort_en",function(b){b.Intl.add("datatable-sort","en",{asc:"Ascending",desc:"Descending",sortBy:"Sort by {column}",reverseSortBy:"Reverse sort by {column}"});},"gallery-2012.03.23-18-00");YUI.add("gallery-datatable-350-preview-sort",function(c){var i=c.Lang,f=i.isBoolean,d=i.isString,h=i.isArray,k=i.isObject,g=c.Array,b=i.sub,j={asc:1,desc:-1,"1":1,"-1":-1};function e(){}e.ATTRS={sortable:{value:"auto",validator:"_validateSortable"},sortBy:{validator:"_validateSortBy",getter:"_getSortBy"},strings:{}};c.mix(e.prototype,{sort:function(l,m){return this.fire("sort",c.merge((m||{}),{sortBy:l||this.get("sortBy")}));},SORTABLE_HEADER_TEMPLATE:'
',toggleSort:function(m,s){var r=this._sortBy,t=[],o,q,n,l,p;for(o=0,q=r.length;o=0;--o){if(t[n][l]){t[n][l]*=-1;break;}}}}else{for(o=0,q=t.length;or)?o:((m=0;--n){if(!o[n].sortable){o.splice(n,1);}}}}}this._sortable=o;},_renderSortable:function(){this._uiSetSortable();this._bindSortUI();},_setSortBy:function(){var o=this._displayColumns,t=this.get("sortBy")||[],q=" "+this.getClassName("sorted"),p,r,l,m,s,n;this._sortBy=[];for(p=0,r=o.length;p' **/ - TABLE_TEMPLATE : '', + TABLE_TEMPLATE : '', /** HTML template used to create table's `` if configured with a @@ -967,7 +967,7 @@ Y.mix(Table.prototype, { @protected **/ _initRecordType: function () { - var data, columns, recordType, handle; + var data, columns, recordType, handle, columnKeys; if (!this.get('recordType')) { data = this.get('data'); @@ -988,8 +988,12 @@ Y.mix(Table.prototype, { this._createRecordClass(keys(data[0])); // Or if the columns were defined, build a class from the keys - } else if (keys(columns).length) { - recordType = this._createRecordClass(keys(columns)); + } else { + columnKeys = keys(columns); + + if (columnKeys.length) { + recordType = this._createRecordClass(columnKeys); + } } if (recordType) { @@ -1065,11 +1069,12 @@ Y.mix(Table.prototype, { keys to the returned map. There is no limit to the levels of nesting. All columns are assigned a `_yuid` stamp and `_id` property corresponding - to the column's configured `name` or `key` property. If the same `name` or - `key` appears in multiple columns, subsequent appearances will have their - `_id` appended with an incrementing number (e.g. if column "foo" is - included in the `columns` attribute twice, the first will get `_id` of - "foo", and the second an `_id` of "foo1"). + to the column's configured `name` or `key` property with any spaces + replaced with dashes. If the same `name` or `key` appears in multiple + columns, subsequent appearances will have their `_id` appended with an + incrementing number (e.g. if column "foo" is included in the `columns` + attribute twice, the first will get `_id` of "foo", and the second an `_id` + of "foo1"). The result is an object map with column keys as the property name and the corresponding column object as the associated value. @@ -1111,6 +1116,10 @@ Y.mix(Table.prototype, { // added to the end. id = col.name || col.key || col._yuid; + // Sanitize the _id for use in generated CSS classes. + // TODO: is there more to do for other uses of _id? + id = id.replace(/\s+/, '-'); + if (keys[id]) { id += (keys[id]++); } else { @@ -1146,12 +1155,6 @@ Y.mix(Table.prototype, { this._viewConfig.columns = this.get('columns'); this._viewConfig.modelList = this.data; - contentBox.setAttrs({ - 'role' : 'grid', - 'aria-readonly': true // until further notice - }); - - this.fire('renderTable', { headerView : this.get('headerView'), headerConfig: this._headerConfig, @@ -1358,13 +1361,7 @@ Y.mix(Table.prototype, { className: this.getClassName('caption') })); - captionId = Y.stamp(caption); - - caption.set('id', captionId); - table.prepend(this._captionNode); - - table.setAttribute('aria-describedby', captionId); } caption.setContent(htmlContent); @@ -1373,8 +1370,6 @@ Y.mix(Table.prototype, { caption.remove(true); delete this._captionNode; - - table.removeAttribute('aria-describedby'); } }, @@ -1385,7 +1380,11 @@ Y.mix(Table.prototype, { @protected **/ _uiSetSummary: function (summary) { - this._tableNode.setAttribute('summary', summary || ''); + if (summary) { + this._tableNode.setAttribute('summary', summary); + } else { + this._tableNode.removeAttribute('summary'); + } }, /** @@ -1419,7 +1418,7 @@ Y.mix(Table.prototype, { return val === null || (isFunction(val) && val.prototype.render); } }); -}, 'gallery-2012.02.01-21-35', { requires: ['model-list'] }); +}, 'gallery-2012.03.23-18-00', { requires: ['model-list'] }); YUI.add('gallery-datatable-350-preview-head', function (Y) { /** @@ -1464,6 +1463,8 @@ Supported properties of the column objects include: * `key` - If `label` is not specified, the `key` is used for content. * `children` - Array of columns to appear below this column in the next row. + * `headerTemplate` - Overrides the instance's `CELL_TEMPLATE` for cells in this + column only. * `abbr` - The content of the 'abbr' attribute of the `' + @default '' **/ CELL_TEMPLATE : - '', + '', /** The data representation of the header rows to render. This is assigned by @@ -1541,7 +1542,7 @@ Y.namespace('DataTable').HeaderView = Y.Base.create('tableHeader', Y.View, [], { @default '{content}' **/ ROW_TEMPLATE: - '{content}', + '{content}', // -- Public methods ------------------------------------------------------ @@ -1578,9 +1579,9 @@ Y.namespace('DataTable').HeaderView = Y.Base.create('tableHeader', Y.View, [], { var thead = this.get('container'), columns = this.columns, defaults = { - abbr: '', _colspan: 1, - _rowspan: 1 + _rowspan: 1, + abbr: '' }, i, len, j, jlen, col, html, content, values; @@ -1598,11 +1599,14 @@ Y.namespace('DataTable').HeaderView = Y.Base.create('tableHeader', Y.View, [], { col, { className: this.getClassName('header'), content : col.label || col.key || - ("Column " + (j + 1)), - headers : '' + ("Column " + (j + 1)) } ); + if (col.abbr) { + values.abbr = 'abbr="' + col.abbr + '"'; + } + if (col.className) { values.className += ' ' + col.className; } @@ -1612,12 +1616,8 @@ Y.namespace('DataTable').HeaderView = Y.Base.create('tableHeader', Y.View, [], { ' ' + this.getClassName('col', col._id); } - if (col._parent) { - values._headers = - 'headers="' + col._parent._headers.join(' ') + '"'; - } - - content += fromTemplate(this.CELL_TEMPLATE, values); + content += fromTemplate( + col.headerTemplate || this.CELL_TEMPLATE, values); } html += fromTemplate(this.ROW_TEMPLATE, { @@ -1757,6 +1757,8 @@ Y.namespace('DataTable').HeaderView = Y.Base.create('tableHeader', Y.View, [], { * `children` - Array of columns to appear below this column in the next row. * `abbr` - The content of the 'abbr' attribute of the `` structure with arrays for rows and objects for cells. Column objects have the @@ -1885,7 +1887,7 @@ Y.namespace('DataTable').HeaderView = Y.Base.create('tableHeader', Y.View, [], { return columns; } }); -}, 'gallery-2012.02.01-21-35', { requires: ['view', 'gallery-datatable-350-preview-core'] }); +}, 'gallery-2012.03.23-18-00', { requires: ['view', 'gallery-datatable-350-preview-core'] }); YUI.add('gallery-datatable-350-preview-body', function (Y) { /** @@ -1911,6 +1913,8 @@ Supported properties of the column objects include: * `name` - Used for columns that don't relate to an attribute in the Model (`formatter` or `nodeFormatter` only) if the implementer wants a predictable name to refer to in their CSS. + * `cellTemplate` - Overrides the instance's `CELL_TEMPLATE` for cells in this + column only. * `formatter` - Used to customize or override the content value from the Model. These do not have access to the cell or row Nodes and should return string (HTML) content. @@ -1918,8 +1922,8 @@ Supported properties of the column objects include: custom modifications on the cell or row Node that could not be performed by `formatter`s. Should be used sparingly for better performance. * `emptyCellValue` - String (HTML) value to use if the Model data for a - column, or the content generated by a `formatter`, is the empty string or - `undefined`. + column, or the content generated by a `formatter`, is the empty string, + `null`, or `undefined`. * `allowHTML` - Set to `true` if a column value, `formatter`, or `emptyCellValue` can contain HTML. This defaults to `false` to protect against XSS. @@ -1933,7 +1937,7 @@ Column `formatter`s are passed an object (`o`) with the following properties: * `column` - The column configuration object for the current column. * `className` - Initially empty string to allow `formatter`s to add CSS classes to the cell's ``. @@ -1950,7 +1954,7 @@ properties: * `data` - An object map of Model keys to their current values. * `record` - The Model instance. * `column` - The column configuration object for the current column. - * `rowindex` - The zero-based row number. + * `rowIndex` - The zero-based row number. They are expected to inject content into the cell's Node directly, including any "empty" cell content. Each `nodeFormatter` will have access through the @@ -1986,9 +1990,9 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { @property CELL_TEMPLATE @type {HTML} - @default '' + @default '' **/ - CELL_TEMPLATE: '', + CELL_TEMPLATE: '', /** CSS class applied to even rows. This is assigned at instantiation after @@ -2019,12 +2023,9 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { @property ROW_TEMPLATE @type {HTML} - @default '{content}' + @default '{content}' **/ - ROW_TEMPLATE : - '' + - '{content}' + - '', + ROW_TEMPLATE : '{content}', /** The object that serves as the source of truth for column and row data. @@ -2121,6 +2122,8 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { * `name` - Used for columns that don't relate to an attribute in the Model (`formatter` or `nodeFormatter` only) if the implementer wants a predictable name to refer to in their CSS. + * `cellTemplate` - Overrides the instance's `CELL_TEMPLATE` for cells in + this column only. * `formatter` - Used to customize or override the content value from the Model. These do not have access to the cell or row Nodes and should return string (HTML) content. @@ -2129,8 +2132,8 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { performed by `formatter`s. Should be used sparingly for better performance. * `emptyCellValue` - String (HTML) value to use if the Model data for a - column, or the content generated by a `formatter`, is the empty string - or `undefined`. + column, or the content generated by a `formatter`, is the empty string, + `null`, or `undefined`. * `allowHTML` - Set to `true` if a column value, `formatter`, or `emptyCellValue` can contain HTML. This defaults to `false` to protect against XSS. @@ -2147,7 +2150,7 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { * `column` - The column configuration object for the current column. * `className` - Initially empty string to allow `formatter`s to add CSS classes to the cell's ``. @@ -2166,7 +2169,7 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { * `data` - An object map of Model keys to their current values. * `record` - The Model instance. * `column` - The column configuration object for the current column. - * `rowindex` - The zero-based row number. + * `rowIndex` - The zero-based row number. They are expected to inject content into the cell's Node directly, including any "empty" cell content. Each `nodeFormatter` will have access through the @@ -2265,7 +2268,7 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { var formatterData = { data : record.toJSON(), record : record, - rowindex : index + rowIndex : index }, row = tbodyNode.rows[index], i, len, col, key, cell, keep; @@ -2373,9 +2376,9 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { * `clientID` - From Model, used the assign the ``'s 'id' attribute. * `foo` - The value to populate the 'foo' column cell content. This value will be the value stored in the Model's `foo` attribute, or the - result of the column's `formatter` if assigned. If the value is '' or - `undefined`, and the column's `emptyCellValue` is assigned, that value - will be used. + result of the column's `formatter` if assigned. If the value is '', + `null`, or `undefined`, and the column's `emptyCellValue` is assigned, + that value will be used. * `bar` - Same for the 'bar' column cell content. * `foo-className` - String of CSS classes to apply to the ` + **/ + MESSAGE_TEMPLATE: '', + + /** + Hides the message node. + + @method hideMessage + @return {DataTable} + @chainable + **/ + hideMessage: function () { + this.get('boundingBox').removeClass( + this.getClassName('message', 'visible')); + + return this; + }, + + /** + Display the message node and set its content to `message`. If there is a + localized `strings` entry for the value of `message`, that string will be + used. + + @method showMessage + @param {String} message The message name or message itself to display + @return {DataTable} + @chainable + **/ + showMessage: function (message) { + var content = this.getString(message) || message; + + if (!this._messageNode) { + this._initMessageNode(); + } + + if (this.get('showMessages')) { + if (content) { + this._messageNode.one( + '.' + this.getClassName('message', 'content')) + .setContent(content); + + this.get('boundingBox').addClass( + this.getClassName('message','visible')); + } else { + // TODO: is this right? + // If no message provided, remove the message node. + this.hideMessage(); + } + } + + return this; + }, + + //-------------------------------------------------------------------------- + // Protected methods + //-------------------------------------------------------------------------- + /** + Updates the colspan of the `");k.set("className",a.FormManager.row_marker_class);var t=a.Node.create("");t.set("className",this.getClassName("error"));k.appendChild(t);var l=this._createContainer();l.set("colSpan",1+this.plugin_column_count);l.set("innerHTML",'

');t.appendChild(l);t.appendChild(this._createContainer());var q=a.Node.create("");q.set("className",this.getClassName("criterion"));k.appendChild(q);var j=this._createContainer();j.set("className",this.getClassName("variable"));q.appendChild(j);j.set("innerHTML",this._variablesMenu(this.variableName(u)));var o=j.one("select");o.on("change",c,this,q);var x=a.Node.getDOMNode(o).options;for(var r=0;r2){var m=r.get("children").item(0).next();m.remove(true);}var n=this.row_list[u].var_menu;var l=this.var_list[n.get("selectedIndex")];var w=[];if(l.type=="none"){r.addClass(this.getClassName("empty"));}else{r.removeClass(this.getClassName("empty"));this.row_list[u].plugin=new b.plugin_mapping[l.type](this,this.get("pluginConfig"));w=this.row_list[u].plugin.create(u,l,this.op_list[l.type],v);}while(w.lengththis.plugin_column_count){var k=1+w.length;for(var q=0;q");},_notifyChanged:function(){this.fire("queryChanged");},variableName:function(j){return a.Lang.sub(this.var_menu_name_pattern,{i:j});},_variablesMenu:function(k){var j='';return a.Lang.sub(j,{n:k,f:a.FormManager.field_marker_class,c:this.getClassName("field")});},_rowControls:function(){var j=''+'';if(!this._controls_markup){this._controls_markup=a.Lang.sub(j,{ci:this.getClassName("insert"),cr:this.getClassName("remove")});}return this._controls_markup;}});a.QueryBuilder=b;a.QueryBuilder.Env={has_bubble_problem:d};b.String=function(k,j){this.qb=k;this.op_menu_name_pattern=j.field_prefix+"query_op_{i}";this.val_input_name_pattern=j.field_prefix+"query_val_{i}";};b.String.prototype={create:function(o,n,j,p){var m=this.qb._createContainer();m.set("className",this.qb.getClassName("operator"));m.set("innerHTML",this._operationsMenu(this.operationName(o)));this.op_menu=m.one("select");var k=a.Node.getDOMNode(this.op_menu).options;for(var l=0;lThe QuickEdit plugin provides a new mode for DataTable where all * values in the table can be edited simultaneously, controlled by the @@ -115,19 +90,16 @@ Y.Column.ATTRS.qeFormatter = *
  * function myQuickEditFormatter(o) {
  *   var markup =
- *     '<input type="text" class="{yiv} quickedit-field quickedit-key:{key}"/>' +
- *     Y.Plugin.DataTableQuickEdit.error_display_markup;
- *
- *   var qe = o.column.get('quickEdit');
- *   var td = this.createCell(o);
- *   td.set('innerHTML', Y.Lang.sub(markup, {
- *     key: o.column.get('key'),
- *     yiv: qe.validation ? (qe.validation.css || '') : ''
- *   }));
- *
- *   td.get('firstChild').set('value', extractMyEditableValue(o));
- *
- *   Y.Plugin.DataTableQuickEdit.copyDownFormatter.call(this, o, td);
+ *     '<input type="text" class="{yiv} quickedit-field quickedit-key:{key}" value="{value}"/>' +
+ *     '{cd}' + Y.Plugin.DataTableQuickEdit.error_display_markup;
+ *
+ *   var qe = o.column.quickEdit;
+ *   return Y.Lang.sub(markup, {
+ *     key: o.column.key,
+ *     value: o.value.toString().replace(/"/g, '"'),
+ *     yiv: qe.validation ? (qe.validation.css || '') : '',
+ *     cd: QuickEdit.copyDownFormatter.call(this, o)
+ *   });
  * };
  * 
* @@ -167,15 +139,13 @@ QuickEdit.ATTRS = } }; -var class_re_prefix = '(?:^|\\s)(?:', - class_re_suffix = ')(?:\\s|$)', - quick_edit_re = /quickedit-key:([^\s]+)/, +var quick_edit_re = /quickedit-key:([^\s]+)/, qe_row_status_prefix = 'quickedit-has', qe_row_status_pattern = qe_row_status_prefix + '([a-z]+)', - qe_row_status_re = new RegExp(class_re_prefix + qe_row_status_pattern + class_re_suffix), + qe_row_status_re = new RegExp(Y.Node.class_re_prefix + qe_row_status_pattern + Y.Node.class_re_suffix), qe_cell_status_prefix = 'quickedit-has', qe_cell_status_pattern = qe_cell_status_prefix + '([a-z]+)', - qe_cell_status_re = new RegExp(class_re_prefix + qe_cell_status_pattern + class_re_suffix); + qe_cell_status_re = new RegExp(Y.Node.class_re_prefix + qe_cell_status_pattern + Y.Node.class_re_suffix); /** * The CSS class that marks the container for the error message inside a cell. @@ -193,6 +163,14 @@ QuickEdit.error_text_class = 'quickedit-message-text'; */ QuickEdit.error_display_markup = '
'; +/** + * The CSS class that marks the "Copy Down" button inside a cell. + * + * @property Y.Plugin.DataTableQuickEdit.copy_down_button_class + * @type {String} + */ +QuickEdit.copy_down_button_class = 'quickedit-copy-down'; + /** * Called with exactly the same arguments as any other cell * formatter, this function displays an input field. @@ -203,20 +181,17 @@ QuickEdit.error_display_markup = '
'; QuickEdit.textFormatter = function(o) { var markup = - '' + - QuickEdit.error_display_markup; + '' + + '{cd}' + QuickEdit.error_display_markup; - var qe = o.column.get('quickEdit'); - var td = this.createCell(o); - td.set('innerHTML', Y.Lang.sub(markup, + var qe = o.column.quickEdit; + return Y.Lang.sub(markup, { - key: o.column.get('key'), - yiv: qe.validation ? (qe.validation.css || '') : '' - })); - - td.get('firstChild').set('value', o.value); - - QuickEdit.copyDownFormatter.call(this, o, td); + key: o.column.key, + value: o.value.toString().replace(/"/g, '"'), + yiv: qe.validation ? (qe.validation.css || '') : '', + cd: QuickEdit.copyDownFormatter.call(this, o) + }); }; /** @@ -229,20 +204,17 @@ QuickEdit.textFormatter = function(o) QuickEdit.textareaFormatter = function(o) { var markup = - '' + + '{cd}' + QuickEdit.error_display_markup; - var qe = o.column.get('quickEdit'); - var td = this.createCell(o); - td.set('innerHTML', Y.Lang.sub(markup, + var qe = o.column.quickEdit; + return Y.Lang.sub(markup, { - key: o.column.get('key'), - yiv: qe.validation ? (qe.validation.css || '') : '' - })); - - td.get('firstChild').set('value', o.value); - - QuickEdit.copyDownFormatter.call(this, o, td); + key: o.column.key, + value: o.value, + yiv: qe.validation ? (qe.validation.css || '') : '', + cd: QuickEdit.copyDownFormatter.call(this, o) + }); }; /** @@ -312,13 +284,11 @@ function getSiblingTdEl( * Copy value from first cell to all other cells in the column. * * @param e {Event} triggering event - * @param cell {Node} cell from which to copy * @private */ -function copyDown( - /* event */ e, - /* Node */ cell) +function copyDown(e) { + var cell = e.currentTarget.ancestor('.yui3-datatable-cell'); var field = cell.one('.quickedit-field'); if (!field) { @@ -358,15 +328,16 @@ function copyDown( */ QuickEdit.copyDownFormatter = function(o, td) { - if (o.column.get('quickEdit').copyDown && o.rowindex === 0) + if (o.column.quickEdit.copyDown && o.rowIndex === 0) { - var button = Y.Node.create(''); - button.set('title', 'Copy down'); - button.set('innerHTML', '↓'); - - td.insert(button, td.one('.' + QuickEdit.error_text_class)); - - button.on('click', copyDown, this, td); + return Y.Lang.sub('', + { + c: QuickEdit.copy_down_button_class + }); + } + else + { + return ''; } }; @@ -374,7 +345,14 @@ function wrapFormatter(editFmt, origFmt) { return function(o) { - return (o.record ? editFmt : origFmt).apply(this, arguments); + if (!o.record && Y.Lang.isString(origFmt)) + { + return origFmt; + } + else + { + return (o.record ? editFmt : origFmt).apply(this, arguments); + } }; } @@ -398,6 +376,39 @@ function moveFocus(e) } } +/** + * Parse the column configuration for easy lookup. + */ +function parseColumns() +{ + var forest = this.get('host').get('columns'); + var map = {}; + + function accumulate(list, node) + { + if (Y.Lang.isString(node)) + { + var col = { key: node }; + list.push(col); + map[node] = col; + } + else if (node.children) + { + list = Y.reduce(node.children, list, accumulate); + } + else + { + list.push(node); + map[ node.key ] = node; + } + + return list; + } + + this.column_list = Y.reduce(forest, [], accumulate); + this.column_map = map; +} + /** * Validate the given form fields. * @@ -409,14 +420,13 @@ function validateElements( /* NodeList */ list) { var host = this.get('host'); - var cols = host.get('columnset').keyHash; var status = true; var count = list.size(); for (var i=0; i");q.set("title","Copy down");q.set("innerHTML","↓");s.insert(q,s.one("."+d.error_text_class));q.on("click",f,this,s);}};function c(r,q){return function(s){return(s.record?r:q).apply(this,arguments);};}function p(s){var q=o.call(this,s.target,s.charCode==38?-1:+1);if(q){var r=q.one(".quickedit-field");if(r){r.focus();r.select();s.halt(true);}}}function g(w){var z=this.get("host");var x=z.get("columnset").keyHash;var s=true;var u=w.size();for(var t=0;t↓',{c:d.copy_down_button_class});}else{return"";}};function c(q,p){return function(r){if(!r.record&&b.Lang.isString(p)){return p;}else{return(r.record?q:p).apply(this,arguments);}};}function o(r){var p=n.call(this,r.target,r.charCode==38?-1:+1);if(p){var q=p.one(".quickedit-field");if(q){q.focus();q.select();r.halt(true);}}}function a(){var r=this.get("host").get("columns");var q={};function p(u,t){if(b.Lang.isString(t)){var s={key:t};u.push(s);q[t]=s;}else{if(t.children){u=b.reduce(t.children,u,p);}else{u.push(t);q[t.key]=t;}}return u;}this.column_list=b.reduce(r,[],p);this.column_map=q;}function g(v){var x=this.get("host");var r=true;var t=v.size();for(var s=0;sThe QuickEdit plugin provides a new mode for DataTable where all * values in the table can be edited simultaneously, controlled by the @@ -115,19 +90,16 @@ Y.Column.ATTRS.qeFormatter = *
  * function myQuickEditFormatter(o) {
  *   var markup =
- *     '<input type="text" class="{yiv} quickedit-field quickedit-key:{key}"/>' +
- *     Y.Plugin.DataTableQuickEdit.error_display_markup;
- *
- *   var qe = o.column.get('quickEdit');
- *   var td = this.createCell(o);
- *   td.set('innerHTML', Y.Lang.sub(markup, {
- *     key: o.column.get('key'),
- *     yiv: qe.validation ? (qe.validation.css || '') : ''
- *   }));
- *
- *   td.get('firstChild').set('value', extractMyEditableValue(o));
- *
- *   Y.Plugin.DataTableQuickEdit.copyDownFormatter.call(this, o, td);
+ *     '<input type="text" class="{yiv} quickedit-field quickedit-key:{key}" value="{value}"/>' +
+ *     '{cd}' + Y.Plugin.DataTableQuickEdit.error_display_markup;
+ *
+ *   var qe = o.column.quickEdit;
+ *   return Y.Lang.sub(markup, {
+ *     key: o.column.key,
+ *     value: o.value.toString().replace(/"/g, '"'),
+ *     yiv: qe.validation ? (qe.validation.css || '') : '',
+ *     cd: QuickEdit.copyDownFormatter.call(this, o)
+ *   });
  * };
  * 
* @@ -167,15 +139,13 @@ QuickEdit.ATTRS = } }; -var class_re_prefix = '(?:^|\\s)(?:', - class_re_suffix = ')(?:\\s|$)', - quick_edit_re = /quickedit-key:([^\s]+)/, +var quick_edit_re = /quickedit-key:([^\s]+)/, qe_row_status_prefix = 'quickedit-has', qe_row_status_pattern = qe_row_status_prefix + '([a-z]+)', - qe_row_status_re = new RegExp(class_re_prefix + qe_row_status_pattern + class_re_suffix), + qe_row_status_re = new RegExp(Y.Node.class_re_prefix + qe_row_status_pattern + Y.Node.class_re_suffix), qe_cell_status_prefix = 'quickedit-has', qe_cell_status_pattern = qe_cell_status_prefix + '([a-z]+)', - qe_cell_status_re = new RegExp(class_re_prefix + qe_cell_status_pattern + class_re_suffix); + qe_cell_status_re = new RegExp(Y.Node.class_re_prefix + qe_cell_status_pattern + Y.Node.class_re_suffix); /** * The CSS class that marks the container for the error message inside a cell. @@ -193,6 +163,14 @@ QuickEdit.error_text_class = 'quickedit-message-text'; */ QuickEdit.error_display_markup = '
'; +/** + * The CSS class that marks the "Copy Down" button inside a cell. + * + * @property Y.Plugin.DataTableQuickEdit.copy_down_button_class + * @type {String} + */ +QuickEdit.copy_down_button_class = 'quickedit-copy-down'; + /** * Called with exactly the same arguments as any other cell * formatter, this function displays an input field. @@ -203,20 +181,17 @@ QuickEdit.error_display_markup = '
'; QuickEdit.textFormatter = function(o) { var markup = - '' + - QuickEdit.error_display_markup; + '' + + '{cd}' + QuickEdit.error_display_markup; - var qe = o.column.get('quickEdit'); - var td = this.createCell(o); - td.set('innerHTML', Y.Lang.sub(markup, + var qe = o.column.quickEdit; + return Y.Lang.sub(markup, { - key: o.column.get('key'), - yiv: qe.validation ? (qe.validation.css || '') : '' - })); - - td.get('firstChild').set('value', o.value); - - QuickEdit.copyDownFormatter.call(this, o, td); + key: o.column.key, + value: o.value.toString().replace(/"/g, '"'), + yiv: qe.validation ? (qe.validation.css || '') : '', + cd: QuickEdit.copyDownFormatter.call(this, o) + }); }; /** @@ -229,20 +204,17 @@ QuickEdit.textFormatter = function(o) QuickEdit.textareaFormatter = function(o) { var markup = - '' + + '{cd}' + QuickEdit.error_display_markup; - var qe = o.column.get('quickEdit'); - var td = this.createCell(o); - td.set('innerHTML', Y.Lang.sub(markup, + var qe = o.column.quickEdit; + return Y.Lang.sub(markup, { - key: o.column.get('key'), - yiv: qe.validation ? (qe.validation.css || '') : '' - })); - - td.get('firstChild').set('value', o.value); - - QuickEdit.copyDownFormatter.call(this, o, td); + key: o.column.key, + value: o.value, + yiv: qe.validation ? (qe.validation.css || '') : '', + cd: QuickEdit.copyDownFormatter.call(this, o) + }); }; /** @@ -312,13 +284,11 @@ function getSiblingTdEl( * Copy value from first cell to all other cells in the column. * * @param e {Event} triggering event - * @param cell {Node} cell from which to copy * @private */ -function copyDown( - /* event */ e, - /* Node */ cell) +function copyDown(e) { + var cell = e.currentTarget.ancestor('.yui3-datatable-cell'); var field = cell.one('.quickedit-field'); if (!field) { @@ -358,15 +328,16 @@ function copyDown( */ QuickEdit.copyDownFormatter = function(o, td) { - if (o.column.get('quickEdit').copyDown && o.rowindex === 0) + if (o.column.quickEdit.copyDown && o.rowIndex === 0) { - var button = Y.Node.create(''); - button.set('title', 'Copy down'); - button.set('innerHTML', '↓'); - - td.insert(button, td.one('.' + QuickEdit.error_text_class)); - - button.on('click', copyDown, this, td); + return Y.Lang.sub('', + { + c: QuickEdit.copy_down_button_class + }); + } + else + { + return ''; } }; @@ -374,7 +345,14 @@ function wrapFormatter(editFmt, origFmt) { return function(o) { - return (o.record ? editFmt : origFmt).apply(this, arguments); + if (!o.record && Y.Lang.isString(origFmt)) + { + return origFmt; + } + else + { + return (o.record ? editFmt : origFmt).apply(this, arguments); + } }; } @@ -398,6 +376,39 @@ function moveFocus(e) } } +/** + * Parse the column configuration for easy lookup. + */ +function parseColumns() +{ + var forest = this.get('host').get('columns'); + var map = {}; + + function accumulate(list, node) + { + if (Y.Lang.isString(node)) + { + var col = { key: node }; + list.push(col); + map[node] = col; + } + else if (node.children) + { + list = Y.reduce(node.children, list, accumulate); + } + else + { + list.push(node); + map[ node.key ] = node; + } + + return list; + } + + this.column_list = Y.reduce(forest, [], accumulate); + this.column_map = map; +} + /** * Validate the given form fields. * @@ -409,14 +420,13 @@ function validateElements( /* NodeList */ list) { var host = this.get('host'); - var cols = host.get('columnset').keyHash; var status = true; var count = list.size(); for (var i=0; i + *
+ * cancel + *
+ *
+ * If the cancel method is called before the callback function, + * the callback function won't be called. + *
+ * + */ + + // Check for process.nextTick in Node.js. + if (Y.UA.nodejs && typeof process !== 'undefined' && 'nextTick' in process) { + _soon = function (callbackFunction) { + var id = Y.guid(_string_soon), + response = _store(id, callbackFunction); + + process.nextTick(function () { + _call(id); + }); + + return response; + }; + } else if (!((function () { + // Check for a native or already polyfilled implementation of setImmediate. + var setImmediate = 0; + + Y.Array.some([ + 'setImmediate', + 'mozSetImmediate', + 'msSetImmediate', + 'oSetImmediate', + 'webkitSetImmediate' + ], function (method) { + if (method in _window) { + setImmediate = _window[method]; + return true; + } + + return false; + }); + + if (setImmediate) { + _soon = function (callbackFunction) { + var id = Y.guid(_string_soon), + response = _store(id, callbackFunction); + + setImmediate(function () { + _call(id); + }); + + return response; + }; + } + + return _soon; + }()) || (function () { + // Check for postMessage support but make sure we're not in a WebWorker. + if (('postMessage' in _window) && !('importScripts' in _window)) { + // Check if postMessage is asynchronous. + var oldOnMessage = _window.onmessage, + postMessage = _window.postMessage, + postMessageIsAsynchronous = true; + + _window.onmessage = function () { + postMessageIsAsynchronous = false; + }; + + postMessage('', '*'); + _window.onmessage = oldOnMessage; + + if (postMessageIsAsynchronous) { + Y.on('message', function (eventFacade) { + var event = eventFacade._event; + + // Only listen to messages from this document. + if (event.source === _window && _call(event.data)) { + // Other listeners should't care about this message. + eventFacade.halt(true); + } + }); + + _soon = function (callbackFunction) { + var id = Y.guid(_string_soon), + response = _store(id, callbackFunction); + + postMessage(id, '*'); + + return response; + }; + } + } + + return _soon; + }()) || (function () { + // Check for MessageChannel support. + // It's very unlikely that a browser supports MessageChannel but fails the previous check for postMessage. + // I put them in this order because in my test, postMessage was way faster than MessageChannel in the most + // popular browsers. + if ('MessageChannel' in _window) { + var messageChannel = new MessageChannel(), + port2 = messageChannel.port2; + + messageChannel.port1.onmessage = function (event) { + _call(event.data); + }; + + _soon = function (callbackFunction) { + var id = Y.guid(_string_soon), + response = _store(id, callbackFunction); + + port2.postMessage(id); + + return response; + }; + } + + return _soon; + }()) || (function () { + // Check for a script node's readystatechange event. + var Node = Y.Node, + + scriptNode = Node.create('
` * `className` - Adds this string of CSS classes to the column header @@ -1503,10 +1504,10 @@ Y.namespace('DataTable').HeaderView = Y.Base.create('tableHeader', Y.View, [], { @property CELL_TEMPLATE @type {HTML} - @default '{content}{content}{content}{content}
` + * `headerTemplate` - Overrides the instance's `CELL_TEMPLATE` for cells + in this column only. The output structure is basically a simulation of the `
`. - * `rowindex` - The zero-based row number. + * `rowIndex` - The zero-based row number. * `rowClass` - Initially empty string to allow `formatter`s to add CSS classes to the cell's containing row `
{content}{content}{content}{content}
`. - * `rowindex` - The zero-based row number. + * `rowIndex` - The zero-based row number. * `rowClass` - Initially empty string to allow `formatter`s to add CSS classes to the cell's containing row `
`. * `bar-className` - Same. @@ -2417,7 +2420,7 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { record : model, className: '', rowClass : '', - rowindex : index + rowIndex : index }; if (typeof col.formatter === 'string') { @@ -2439,7 +2442,7 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { } } - if (value === undefined || value === '') { + if (value === undefined || value === null || value === '') { value = col.emptyCellValue || ''; } @@ -2465,16 +2468,19 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { _createRowTemplate: function (columns) { var html = '', cellTemplate = this.CELL_TEMPLATE, - i, len, col, key, token, tokenValues; + i, len, col, key, token, headers, tokenValues; for (i = 0, len = columns.length; i < len; ++i) { - col = columns[i]; - key = col.key; - token = col._id; + col = columns[i]; + key = col.key; + token = col._id; + // Only include headers if there are more than one + headers = (col._headers || []).length > 1 ? + 'headers="' + col._headers.join(' ') + '"' : ''; tokenValues = { content : '{' + token + '}', - headers : (col._headers || []).join(' '), + headers : headers, className: this.getClassName('col', token) + ' ' + (col.className || '') + ' ' + this.getClassName('cell') + @@ -2486,7 +2492,7 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { tokenValues.content = ''; } - html += fromTemplate(cellTemplate, tokenValues); + html += fromTemplate(col.cellTemplate || cellTemplate, tokenValues); } this._rowTemplate = fromTemplate(this.ROW_TEMPLATE, { @@ -2592,7 +2598,7 @@ Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], { **/ //_rowTemplate: null }); -}, 'gallery-2012.02.01-21-35', { requires: ['view', 'gallery-datatable-350-preview-core'] }); +}, 'gallery-2012.03.23-18-00', { requires: ['view', 'gallery-datatable-350-preview-core'] }); YUI.add('gallery-datatable-350-preview-base', function (Y) { /** @@ -2855,7 +2861,7 @@ Y.DataTable.Base = Y.Base.create('datatable', Y.Widget, [Y.DataTable.Core], Y.DataTable = Y.mix( Y.Base.create('datatable', Y.DataTable.Base, []), // Create the class Y.DataTable); // Migrate static and namespaced classes -}, 'gallery-2012.02.01-21-35', { requires: ['model-list', 'view', 'base-build', 'widget', 'escape', 'gallery-datatable-350-preview-core'] }); +}, 'gallery-2012.03.23-18-00', { requires: ['model-list', 'view', 'base-build', 'widget', 'escape', 'gallery-datatable-350-preview-core'] }); YUI.add('gallery-datatable-350-preview-mutable', function (Y) { var toArray = Y.Array, @@ -2900,7 +2906,10 @@ those methods to trigger per-operation sync. @default `false` **/ Mutable.ATTRS = { - autoSync: {} + autoSync: { + value: false, + validator: YLang.isBoolean + } }; Y.mix(Mutable.prototype, { @@ -3056,7 +3065,7 @@ Y.mix(Mutable.prototype, { @method addRow @param {Object} data The data or Model instance for the new record - @param {Object} [config]* Configuration to pass along + @param {Object} [config] Configuration to pass along @param {Function} [callback] Callback function for Model's `save()` @param {Error|null} callback.err If an error occurred or validation failed, this parameter will contain the error. If the sync operation @@ -3068,10 +3077,11 @@ Y.mix(Mutable.prototype, { @chainable **/ addRow: function (data, config) { + // Allow autoSync: true + addRow({ data }, { sync: false }) var sync = (config && ('sync' in config)) ? config.sync : this.get('autoSync'), - models, i, len, args; + models, model, i, len, args; if (this.data) { models = this.data.add.apply(this.data, arguments); @@ -3081,7 +3091,11 @@ Y.mix(Mutable.prototype, { args = toArray(arguments, 1, true); for (i = 0, len = models.length; i < len; ++i) { - models[i].save.apply(models[i], args); + model = models[i]; + + if (model.isNew()) { + models[i].save.apply(models[i], args); + } } } } @@ -3109,7 +3123,7 @@ Y.mix(Mutable.prototype, { @method removeRow @param {Object|String|Number} id The Model instance or identifier - @param {Object} [config]* Configuration to pass along + @param {Object} [config] Configuration to pass along @param {Function} [callback] Callback function for Model's `save()` @param {Error|null} callback.err If an error occurred or validation failed, this parameter will contain the error. If the sync operation @@ -3122,6 +3136,7 @@ Y.mix(Mutable.prototype, { **/ removeRow: function (id, config) { var modelList = this.data, + // Allow autoSync: true + addRow({ data }, { sync: false }) sync = (config && ('sync' in config)) ? config.sync : this.get('autoSync'), @@ -3181,7 +3196,7 @@ Y.mix(Mutable.prototype, { @method modifyRow @param {Object|String|Number} id The Model instance or identifier @param {Object} data New data values for the Model - @param {Object} [config]* Configuration to pass along to `setAttrs()` + @param {Object} [config] Configuration to pass along to `setAttrs()` @param {Function} [callback] Callback function for Model's `save()` @param {Error|null} callback.err If an error occurred or validation failed, this parameter will contain the error. If the sync operation @@ -3194,6 +3209,7 @@ Y.mix(Mutable.prototype, { **/ modifyRow: function (id, data, config) { var modelList = this.data, + // Allow autoSync: true + addRow({ data }, { sync: false }) sync = (config && ('sync' in config)) ? config.sync : this.get('autoSync'), @@ -3212,7 +3228,7 @@ Y.mix(Mutable.prototype, { model.setAttrs.apply(model, args); - if (sync) { + if (sync && !model.isNew()) { model.save.apply(model, args); } } @@ -3293,7 +3309,7 @@ Y.mix(Mutable.prototype, { fromCols, fromIndex, toCols, i, len; if (column) { - fromCols = column.parent ? column.parent.children : columns; + fromCols = column._parent ? column._parent.children : columns; fromIndex = arrayIndex(fromCols, column); if (fromIndex > -1) { @@ -3329,7 +3345,7 @@ Y.mix(Mutable.prototype, { cols, index; if (column) { - cols = column.parent ? column.parent.children : columns; + cols = column._parent ? column._parent.children : columns; index = Y.Array.indexOf(cols, column); if (index > -1) { @@ -3382,7 +3398,7 @@ as a callback to each Model's `save()` method. @method addRows @param {Object[]} data The data or Model instances to add -@param {Object} [config]* Configuration to pass along +@param {Object} [config] Configuration to pass along @param {Function} [callback] Callback function for each Model's `save()` @param {Error|null} callback.err If an error occurred or validation failed, this parameter will contain the error. If the sync operation @@ -3435,7 +3451,284 @@ Fired by the `moveColumn` method. @param {Object} index The destination index to move to **/ -}, 'gallery-2012.02.01-21-35', { requires: ['gallery-datatable-350-preview-base'] }); +}, 'gallery-2012.03.23-18-00', { requires: ['gallery-datatable-350-preview-base'] }); + +YUI.add('lang/gallery-datatable-350-preview-message', function (Y) { +Y.Intl.add('datatable-message', '', +{ + emptyMessage: "No data to display", + loadingMessage: "Loading..." +} +);}, 'gallery-2012.03.23-18-00'); + +YUI.add('lang/gallery-datatable-350-preview-message_en', function (Y) { +Y.Intl.add('datatable-message', 'en', +{ + emptyMessage: "No data to display", + loadingMessage: "Loading..." +} +);}, 'gallery-2012.03.23-18-00'); + +YUI.add('gallery-datatable-350-preview-message', function (Y) { +/** +Adds support for a message container to appear in the table. This can be used +to indicate loading progress, lack of records, or any other communication +needed. + +Features added to `Y.DataTable`, and made available for custom classes at +`Y.DataTable.Message`. + +@module datatable-message +@class DataTable.Message +@for DataTable +**/ +var Message; + +Y.namespace('DataTable').Message = Message = function () {}; + +Message.ATTRS = { + /** + Enables the display of messages in the table. Setting this to false will + prevent the message Node from being created and `showMessage` from doing + anything. + + @attribute showMessages + @type {Boolean} + @default true + **/ + showMessages: { + value: true, + validator: Y.Lang.isBoolean + } +}; + +Y.mix(Message.prototype, { + /** + Template used to generate the node that will be used to report messages. + + @property MESSAGE_TEMPLATE + @type {HTML} + @default
` used to display the messages. + + @method _afterMessageColumnsChange + @param {EventFacade} e The columnsChange event + @protected + **/ + _afterMessageColumnsChange: function (e) { + var contentNode; + + if (this._messageNode) { + contentNode = this._messageNode.one( + '.' + this.getClassName('message', 'content')); + + if (contentNode) { + contentNode.set('colSpan', this._displayColumns.length); + } + } + }, + + /** + Relays to `_uiSetMessage` to hide or show the message node. + + @method _afterMessageDataChange + @param {EventFacade} e The dataChange event + @protected + **/ + _afterMessageDataChange: function (e) { + this._uiSetMessage(); + }, + + /** + Removes the message node if `showMessages` is `false`, or relays to + `_uiSetMessage` if `true`. + + @method _afterShowMessagesChange + @param {EventFacade} e The showMessagesChange event + @protected + **/ + _afterShowMessagesChange: function (e) { + if (e.newVal) { + this._uiSetMessage(e); + } else if (this._messageNode) { + this.get('boundingBox').removeClass( + this.getClassName('message', 'visible')); + + this._messageNode.remove().destroy(true); + this._messageNode = null; + } + }, + + /** + Binds the events necessary to keep the message node in sync with the current + table and configuration state. + + @method _bindMessageUI + @protected + **/ + _bindMessageUI: function () { + this.after(['dataChange', '*:add', '*:remove', '*:reset'], + Y.bind('_afterMessageDataChange', this)); + + this.after('columnsChange', Y.bind('_afterMessageColumnsChange', this)); + + this.after('showMessagesChange', + Y.bind('_afterShowMessagesChange', this)); + }, + + /** + Merges in the message related strings and hooks into the rendering cycle to + also render and bind the message node. + + @method initializer + @protected + **/ + initializer: function () { + this._initMessageStrings(); + + if (this.get('showMessages')) { + this.after('renderBody', Y.bind('_initMessageNode', this)); + } + + this.after(Y.bind('_bindMessageUI', this), this, 'bindUI'); + this.after(Y.bind('_syncMessageUI', this), this, 'syncUI'); + }, + + /** + Creates the `_messageNode` property from the configured `MESSAGE_TEMPLATE` + and inserts it before the ``'s `` node. + + @method _initMessageNode + @protected + **/ + _initMessageNode: function () { + if (!this._messageNode) { + this._messageNode = Y.Node.create( + Y.Lang.sub(this.MESSAGE_TEMPLATE, { + className: this.getClassName('message'), + contentClass: this.getClassName('message', 'content'), + colspan: this._displayColumns.length || 1 + })); + + this._tableNode.insertBefore(this._messageNode, this._tbodyNode); + } + }, + + /** + Add the messaging related strings to the `strings` map. + + @method _initMessageStrings + @protected + **/ + _initMessageStrings: function () { + // Not a valueFn because other class extensions will want to add to it + this.set('strings', Y.mix((this.get('strings') || {}), + Y.Intl.get('datatable-message'))); + }, + + /** + Node used to display messages from `showMessage`. + + @property _messageNode + @type {Node} + @value `undefined` (not initially set) + **/ + //_messageNode: null, + + /** + Synchronizes the message UI with the table state. + + @method _syncMessageUI + @protected + **/ + _syncMessageUI: function () { + this._uiSetMessage(); + }, + + /** + Calls `hideMessage` or `showMessage` as appropriate based on the presence of + records in the `data` ModelList. + + This is called when `data` is reset or records are added or removed. Also, + if the `showMessages` attribute is updated. In either case, if the + triggering event has a `message` property on the EventFacade, it will be + passed to `showMessage` (if appropriate). If no such property is on the + facade, the `emptyMessage` will be used (see the strings). + + @method _uiSetMessage + @param {EventFacade} e The columnsChange event + @protected + **/ + _uiSetMessage: function (e) { + if (!this.data.size()) { + this.showMessage((e && e.message) || 'emptyMessage'); + } else { + this.hideMessage(); + } + } +}); + + +if (Y.Lang.isFunction(Y.DataTable)) { + Y.Base.mix(Y.DataTable, [ Message ]); +} +}, 'gallery-2012.03.23-18-00', { requires: ['gallery-datatable-350-preview-base'] }); YUI.add('gallery-datatable-350-preview-column-widths', function (Y) { /** @@ -3710,7 +4003,7 @@ Y.mix(ColumnWidths.prototype, { Y.DataTable.ColumnWidths = ColumnWidths; Y.Base.mix(Y.DataTable, [ColumnWidths]); -}, 'gallery-2012.02.01-21-35', { requires: ['gallery-datatable-350-preview-base'] }); +}, 'gallery-2012.03.23-18-00', { requires: ['gallery-datatable-350-preview-base'] }); YUI.add('gallery-datatable-350-preview-scroll', function (Y) { /** @@ -3737,7 +4030,7 @@ the following values: * 'y' - Activate vertical scrolling only. Requires the `height` attribute is also set. - @module @datatable-scroll + @module datatable-scroll @class DataTable.Scrollable @for DataTable **/ @@ -4471,11 +4764,40 @@ Y.mix(Scrollable.prototype, { **/ //_yScrollNode - // TODO: Add _xScrollNode + /** + Overflow Node used to contain the table headers and data in a horizontally + scrolling table. + + @property _xScrollNode + @type {Node} + @default undefined (not initially set) + @protected + **/ + //_xScrollNode }, true); Y.Base.mix(Y.DataTable, [Scrollable]); -}, 'gallery-2012.02.01-21-35', { requires: ['gallery-datatable-350-preview-base', 'gallery-datatable-350-preview-column-widths', 'dom-screen'] }); +}, 'gallery-2012.03.23-18-00', { requires: ['gallery-datatable-350-preview-base', 'gallery-datatable-350-preview-column-widths', 'dom-screen'] }); + +YUI.add('lang/gallery-datatable-350-preview-sort', function (Y) { +Y.Intl.add('datatable-sort', '', +{ + asc: "Ascending", + desc: "Descending", + sortBy: "Sort by {column}", + reverseSortBy: "Reverse sort by {column}" +} +);}, 'gallery-2012.03.23-18-00'); + +YUI.add('lang/gallery-datatable-350-preview-sort_en', function (Y) { +Y.Intl.add('datatable-sort', 'en', +{ + asc: "Ascending", + desc: "Descending", + sortBy: "Sort by {column}", + reverseSortBy: "Reverse sort by {column}" +} +);}, 'gallery-2012.03.23-18-00'); YUI.add('gallery-datatable-350-preview-sort', function (Y) { /** @@ -4565,7 +4887,8 @@ var YLang = Y.Lang, isArray = YLang.isArray, isObject = YLang.isObject, - toArray = Y.Array, + toArray = Y.Array, + sub = YLang.sub, dirMap = { asc : 1, @@ -4634,11 +4957,7 @@ Sortable.ATTRS = { @type {Object} @default (strings for current lang configured in the YUI instance config) **/ - strings: { - valueFn: function () { - return Y.Intl.get('datatable-sort'); - } - } + strings: {} }; Y.mix(Sortable.prototype, { @@ -4675,6 +4994,16 @@ Y.mix(Sortable.prototype, { })); }, + /** + Template for the node that will wrap the header content for sortable + columns. + + @property SORTABLE_HEADER_TEMPLATE + @type {HTML} + @value '
' + **/ + SORTABLE_HEADER_TEMPLATE: '
', + /** Reverse the current sort direction of one or more fields currently being sorted by. @@ -4733,15 +5062,39 @@ Y.mix(Sortable.prototype, { //-------------------------------------------------------------------------- // Protected properties and methods //-------------------------------------------------------------------------- + /** + Sorts the `data` ModelList based on the new `sortBy` configuration. + + @method _afterSortByChange + @param {EventFacade} e The `sortByChange` event + @protected + **/ + _afterSortByChange: function (e) { + // Can't use a setter because it's a chicken and egg problem. The + // columns need to be set up to translate, but columns are initialized + // from Core's initializer. So construction-time assignment would + // fail. + this._setSortBy(); + + // Don't sort unless sortBy has been set + if (this._sortBy.length) { + if (!this.data.comparator) { + this.data.comparator = this._sortComparator; + } + + this.data.sort(); + } + }, + /** Applies the sorting logic to the new ModelList if the `newVal` is a new ModelList. - @method _afterDataChange + @method _afterSortDataChange @param {EventFacade} e the `dataChange` event @protected **/ - _afterDataChange: function (e) { + _afterSortDataChange: function (e) { // object values always trigger a change event, but we only want to // call _initSortFn if the value passed to the `data` attribute was a // new ModelList, not a set of new data as an array, or even the same @@ -4752,26 +5105,21 @@ Y.mix(Sortable.prototype, { }, /** - Sorts the `data` ModelList based on the new `sortBy` configuration. + Checks if any of the fields in the modified record are fields that are + currently being sorted by, and if so, resorts the `data` ModelList. - @method _afterSortByChange - @param {EventFacade} e The `sortByChange` event + @method _afterSortRecordChange + @param {EventFacade} e The Model's `change` event @protected **/ - _afterSortByChange: function (e) { - // Can't use a setter because it's a chicken and egg problem. The - // columns need to be set up to translate, but columns are initialized - // from Core's initializer. So construction-time assignment would - // fail. - this._setSortBy(); + _afterSortRecordChange: function (e) { + var i, len; - // Don't sort unless sortBy has been set - if (this._sortBy.length) { - if (!this.data.comparator) { - this.data.comparator = this._sortComparator; + for (i = 0, len = this._sortBy.length; i < len; ++i) { + if (e.changed[this._sortBy[i].key]) { + this.data.sort(); + break; } - - this.data.sort(); } }, @@ -4885,12 +5233,15 @@ Y.mix(Sortable.prototype, { this._initSortFn(); + this._initSortStrings(); + this.after({ renderHeader : Y.bind('_renderSortable', this), - dataChange : Y.bind('_afterDataChange', this), + dataChange : Y.bind('_afterSortDataChange', this), sortByChange : Y.bind('_afterSortByChange', this), sortableChange: boundParseSortable, - columnsChange : boundParseSortable + columnsChange : boundParseSortable, + "*:change" : Y.bind('_afterSortRecordChange', this) }); this.publish('sort', { @@ -4948,6 +5299,18 @@ Y.mix(Sortable.prototype, { } }, + /** + Add the sort related strings to the `strings` map. + + @method _initSortStrings + @protected + **/ + _initSortStrings: function () { + // Not a valueFn because other class extensions will want to add to it + this.set('strings', Y.mix((this.get('strings') || {}), + Y.Intl.get('datatable-sort'))); + }, + /** Fires the `sort` event in response to user clicks on sortable column headers. @@ -5156,7 +5519,7 @@ Y.mix(Sortable.prototype, { ascClass = this.getClassName('sorted'), descClass = this.getClassName('sorted', 'desc'), linerClass = this.getClassName('sort', 'liner'), - i, len, col, node, content; + i, len, col, node, content, title; this.get('boundingBox').toggleClass( this.getClassName('sortable'), @@ -5189,7 +5552,16 @@ Y.mix(Sortable.prototype, { } } - Y.Node.create('
') + title = sub(this.getString( + (col.sortDir === 1) ? 'reverseSortBy' : 'sortBy'), { + column: col.abbr || col.label || + col.key || ('column ' + i) + }); + + Y.Node.create(Y.Lang.sub(this.SORTABLE_HEADER_TEMPLATE, { + className: linerClass, + title : title + })) .append(node.get('childNodes').toFrag()) .appendTo(node); } @@ -5228,16 +5600,21 @@ Y.mix(Sortable.prototype, { Y.DataTable.Sortable = Sortable; Y.Base.mix(Y.DataTable, [Sortable]); -}, 'gallery-2012.02.01-21-35', { requires: ['gallery-datatable-350-preview-base'] }); +}, 'gallery-2012.03.23-18-00', { requires: ['gallery-datatable-350-preview-base'] }); Y.use('gallery-datatable-350-preview-core', 'gallery-datatable-350-preview-head', 'gallery-datatable-350-preview-body', 'gallery-datatable-350-preview-base', 'gallery-datatable-350-preview-mutable', + 'lang/gallery-datatable-350-preview-message', + 'lang/gallery-datatable-350-preview-message_en', + 'gallery-datatable-350-preview-message', 'gallery-datatable-350-preview-column-widths', 'gallery-datatable-350-preview-scroll', - 'gallery-datatable-350-preview-sort'); + 'gallery-datatable-350-preview-sort', + 'lang/gallery-datatable-350-preview-sort', + 'lang/gallery-datatable-350-preview-sort_en'); -}, 'gallery-2012.02.01-21-35' ,{requires:['base-build', 'widget', 'model-list', 'view', 'escape', 'dom-screen']}); +}, 'gallery-2012.03.23-18-00' ,{requires:['base-build', 'widget', 'model-list', 'view', 'escape', 'dom-screen', 'intl']}); diff --git a/build/gallery-dimensions/gallery-dimensions-debug.js b/build/gallery-dimensions/gallery-dimensions-debug.js index 4b51d55454..27c0abe5eb 100644 --- a/build/gallery-dimensions/gallery-dimensions-debug.js +++ b/build/gallery-dimensions/gallery-dimensions-debug.js @@ -34,6 +34,7 @@ var em_div = null, /********************************************************************** * @method emToPx + * @param em_count {Number} the number of em's to convert (defaults to 1) * @return {Number} the size of one em in pixels * @static */ @@ -52,7 +53,7 @@ Y.Node.emToPx = function( em_div.style.height = '10em'; Y.config.doc.body.appendChild(em_div); } - return em_count * (em_div.offsetWidth / 10.0); + return (em_count || 1) * (em_div.offsetWidth / 10.0); }; /********************************************************************** @@ -156,4 +157,4 @@ Y.Node.prototype.parseDimensionStyle = function( }; -}, 'gallery-2012.01.04-22-09' ,{requires:['node-style','array-extras']}); +}, 'gallery-2012.03.23-18-00' ,{requires:['node-style','array-extras']}); diff --git a/build/gallery-dimensions/gallery-dimensions-min.js b/build/gallery-dimensions/gallery-dimensions-min.js index e96516a5c3..6b34be7868 100644 --- a/build/gallery-dimensions/gallery-dimensions-min.js +++ b/build/gallery-dimensions/gallery-dimensions-min.js @@ -1 +1 @@ -YUI.add("gallery-dimensions",function(d){var b=null,c=["marginLeft","borderLeftWidth","paddingLeft","paddingRight","borderRightWidth","marginRight"],a=["marginTop","borderTopWidth","paddingTop","paddingBottom","borderBottomWidth","marginBottom"];d.Node.emToPx=function(e){if(!b){b=d.config.doc.createElement("div");b.style.position="absolute";b.style.top="-10000px";b.style.left="-10000px";b.style.visibility="hidden";b.style.width="10em";b.style.height="10em";d.config.doc.body.appendChild(b);}return e*(b.offsetWidth/10);};d.Node.prototype.totalWidth=function(){return this._node.offsetWidth+this.parseDimensionStyle("marginLeft")+this.parseDimensionStyle("marginRight");};d.Node.prototype.totalHeight=function(){return this._node.offsetHeight+this.parseDimensionStyle("marginTop")+this.parseDimensionStyle("marginBottom");};d.Node.prototype.insideWidth=function(){return this._node.clientWidth-this.parseDimensionStyle("paddingLeft")-this.parseDimensionStyle("paddingRight");};d.Node.prototype.insideHeight=function(){return this._node.clientHeight-this.parseDimensionStyle("paddingTop")-this.parseDimensionStyle("paddingBottom");};d.Node.prototype.horizMarginBorderPadding=function(){return d.Array.reduce(c,0,function(e,f){return e+this.parseDimensionStyle(f);},this);};d.Node.prototype.vertMarginBorderPadding=function(){return d.Array.reduce(a,0,function(f,e){return f+this.parseDimensionStyle(e);},this);};d.Node.prototype.parseDimensionStyle=function(g){var f=this.getComputedStyle(g);if(!f||!/^[0-9]/.test(f)){return 0;}var e=parseFloat(f,10);if(/em$/.test(f)){e*=d.Node.emToPx(1);}return Math.round(e);};},"gallery-2012.01.04-22-09",{requires:["node-style","array-extras"]}); \ No newline at end of file +YUI.add("gallery-dimensions",function(d){var b=null,c=["marginLeft","borderLeftWidth","paddingLeft","paddingRight","borderRightWidth","marginRight"],a=["marginTop","borderTopWidth","paddingTop","paddingBottom","borderBottomWidth","marginBottom"];d.Node.emToPx=function(e){if(!b){b=d.config.doc.createElement("div");b.style.position="absolute";b.style.top="-10000px";b.style.left="-10000px";b.style.visibility="hidden";b.style.width="10em";b.style.height="10em";d.config.doc.body.appendChild(b);}return(e||1)*(b.offsetWidth/10);};d.Node.prototype.totalWidth=function(){return this._node.offsetWidth+this.parseDimensionStyle("marginLeft")+this.parseDimensionStyle("marginRight");};d.Node.prototype.totalHeight=function(){return this._node.offsetHeight+this.parseDimensionStyle("marginTop")+this.parseDimensionStyle("marginBottom");};d.Node.prototype.insideWidth=function(){return this._node.clientWidth-this.parseDimensionStyle("paddingLeft")-this.parseDimensionStyle("paddingRight");};d.Node.prototype.insideHeight=function(){return this._node.clientHeight-this.parseDimensionStyle("paddingTop")-this.parseDimensionStyle("paddingBottom");};d.Node.prototype.horizMarginBorderPadding=function(){return d.Array.reduce(c,0,function(e,f){return e+this.parseDimensionStyle(f);},this);};d.Node.prototype.vertMarginBorderPadding=function(){return d.Array.reduce(a,0,function(f,e){return f+this.parseDimensionStyle(e);},this);};d.Node.prototype.parseDimensionStyle=function(g){var f=this.getComputedStyle(g);if(!f||!/^[0-9]/.test(f)){return 0;}var e=parseFloat(f,10);if(/em$/.test(f)){e*=d.Node.emToPx(1);}return Math.round(e);};},"gallery-2012.03.23-18-00",{requires:["node-style","array-extras"]}); \ No newline at end of file diff --git a/build/gallery-dimensions/gallery-dimensions.js b/build/gallery-dimensions/gallery-dimensions.js index 4b51d55454..27c0abe5eb 100644 --- a/build/gallery-dimensions/gallery-dimensions.js +++ b/build/gallery-dimensions/gallery-dimensions.js @@ -34,6 +34,7 @@ var em_div = null, /********************************************************************** * @method emToPx + * @param em_count {Number} the number of em's to convert (defaults to 1) * @return {Number} the size of one em in pixels * @static */ @@ -52,7 +53,7 @@ Y.Node.emToPx = function( em_div.style.height = '10em'; Y.config.doc.body.appendChild(em_div); } - return em_count * (em_div.offsetWidth / 10.0); + return (em_count || 1) * (em_div.offsetWidth / 10.0); }; /********************************************************************** @@ -156,4 +157,4 @@ Y.Node.prototype.parseDimensionStyle = function( }; -}, 'gallery-2012.01.04-22-09' ,{requires:['node-style','array-extras']}); +}, 'gallery-2012.03.23-18-00' ,{requires:['node-style','array-extras']}); diff --git a/build/gallery-expiration-cache/gallery-expiration-cache-debug.js b/build/gallery-expiration-cache/gallery-expiration-cache-debug.js new file mode 100644 index 0000000000..4108b7ecad --- /dev/null +++ b/build/gallery-expiration-cache/gallery-expiration-cache-debug.js @@ -0,0 +1,191 @@ +YUI.add('gallery-expiration-cache', function(Y) { + +/********************************************************************** + *

Cache which drops items based on a user-defined expiration criterion, + * e.g., age. By default, expired items are only removed when they are + * requested. If you want to "stop the world" and clean out the cache, + * call clean().

+ * + * @module gallery-expiration-cache + * @class ExpirationCache + * @constructor + * @param config {Object} + *
+ *
store
+ *
Data store which implements get,put,remove,clear,keys. If not specified, a new instance of Y.InstanceManager is created.
+ *
meta
+ *
Function which attaches meta data to an item when it is added to the cache. It receives the value as an argument. If not specified, the default is to timestamp the item.
+ *
expire
+ *
(Required) Function which returns true if the item has expired. It receives the meta data and the value as arguments. If a number is specified, it is assumed to be a duration in milliseconds.
+ *
stats
+ *
Pass true if you want to collect basic statistics. Pass a function if you want to control what information is stored for each key. The function receives the key, the value, and the stat object.
+ *
+ */ + +function ExpirationCache(config) +{ + this._store = config.store || new Y.InstanceManager(); + this._meta = config.meta || timestamp; + this._expire = Y.Lang.isNumber(config.expire) ? Y.rbind(expire, null, config.expire) : config.expire; + this._stats = config.stats ? initStats() : null; + + if (Y.Lang.isFunction(config.stats)) + { + this._stats_key_meta = config.stats; + } +} + +function timestamp() +{ + return new Date().getTime(); +} + +function expire(timestamp, value, delta) +{ + var elapsed = new Date().getTime() - timestamp; + return (elapsed > delta); +} + +function initStats() +{ + return { gets: 0, keys: {} }; +} + +function initKeyStats(keys, key) +{ + if (!keys[key]) + { + keys[key] = { puts: 0, gets: 0 }; + } +} + +ExpirationCache.prototype = +{ + /** + * Retrieve a value. + * + * @param key {String} the key of the object to retrieve + * @return {Mixed} the stored object, or undefined if the slot is empty + */ + get: function( + /* string */ key) + { + var obj = this._store.get(key); + if (obj && this._expire(obj.meta, obj.data)) + { + this._store.remove(key); + } + else if (obj) + { + if (this._stats) + { + this._stats.gets++; + + initKeyStats(this._stats.keys, key); + this._stats.keys[key].gets++; + } + + return obj.data; + } + }, + + /** + * Store a value. + * + * @param key {String} the key of the value + * @param value {Object} the value to store + * @return {boolean} false if the key has already been used + */ + put: function( + /* string */ key, + /* obj/fn */ value) + { + var obj = + { + data: value, + meta: this._meta(value) + }; + + if (!this._store.put(key, obj)) + { + return false; + } + + if (this._stats) + { + initKeyStats(this._stats.keys, key); + this._stats.keys[key].puts++; + + if (this._stats_key_meta) + { + this._stats_key_meta(key, value, this._stats.keys[key]); + } + } + return true; + }, + + /** + * Store a value. + * + * @param key {String} the key of the value + * @param value {Object} the value to store + * @return {Mixed} the original value that was in the slot, or undefined if the slot is empty + */ + replace: function( + /* string */ key, + /* obj/fn */ value) + { + var orig = this.remove(key); + this.put(key, value); + return orig; + }, + + /** + * Remove an value. + * + * @param key {String} the key of the value + * @return {mixed} the value that was removed, or undefined if the slot was empty + */ + remove: function( + /* string */ key) + { + var orig = this._store.remove(key); + if (orig) + { + return orig.data; + } + }, + + /** + * Remove all values. + */ + clear: function() + { + this._store.clear(); + }, + + /** + * Remove all expired values. + */ + clean: function() + { + Y.each(this._store.keys(), this.get, this); + }, + + /** + * This resets all the values. + * + * @return {Object} the current stats + */ + dumpStats: function() + { + var stats = this._stats; + this._stats = initStats(); + return stats; + } +}; + +Y.ExpirationCache = ExpirationCache; + + +}, 'gallery-2012.03.23-18-00' ,{requires:['gallery-instancemanager']}); diff --git a/build/gallery-expiration-cache/gallery-expiration-cache-min.js b/build/gallery-expiration-cache/gallery-expiration-cache-min.js new file mode 100644 index 0000000000..68df6ce43d --- /dev/null +++ b/build/gallery-expiration-cache/gallery-expiration-cache-min.js @@ -0,0 +1 @@ +YUI.add("gallery-expiration-cache",function(f){function c(g){this._store=g.store||new f.InstanceManager();this._meta=g.meta||d;this._expire=f.Lang.isNumber(g.expire)?f.rbind(b,null,g.expire):g.expire;this._stats=g.stats?a():null;if(f.Lang.isFunction(g.stats)){this._stats_key_meta=g.stats;}}function d(){return new Date().getTime();}function b(i,h,j){var g=new Date().getTime()-i;return(g>j);}function a(){return{gets:0,keys:{}};}function e(h,g){if(!h[g]){h[g]={puts:0,gets:0};}}c.prototype={get:function(g){var h=this._store.get(g);if(h&&this._expire(h.meta,h.data)){this._store.remove(g);}else{if(h){if(this._stats){this._stats.gets++;e(this._stats.keys,g);this._stats.keys[g].gets++;}return h.data;}}},put:function(g,h){var i={data:h,meta:this._meta(h)};if(!this._store.put(g,i)){return false;}if(this._stats){e(this._stats.keys,g);this._stats.keys[g].puts++;if(this._stats_key_meta){this._stats_key_meta(g,h,this._stats.keys[g]);}}return true;},replace:function(g,h){var i=this.remove(g);this.put(g,h);return i;},remove:function(g){var h=this._store.remove(g);if(h){return h.data;}},clear:function(){this._store.clear();},clean:function(){f.each(this._store.keys(),this.get,this);},dumpStats:function(){var g=this._stats;this._stats=a();return g;}};f.ExpirationCache=c;},"gallery-2012.03.23-18-00",{requires:["gallery-instancemanager"]}); \ No newline at end of file diff --git a/build/gallery-expiration-cache/gallery-expiration-cache.js b/build/gallery-expiration-cache/gallery-expiration-cache.js new file mode 100644 index 0000000000..4108b7ecad --- /dev/null +++ b/build/gallery-expiration-cache/gallery-expiration-cache.js @@ -0,0 +1,191 @@ +YUI.add('gallery-expiration-cache', function(Y) { + +/********************************************************************** + *

Cache which drops items based on a user-defined expiration criterion, + * e.g., age. By default, expired items are only removed when they are + * requested. If you want to "stop the world" and clean out the cache, + * call clean().

+ * + * @module gallery-expiration-cache + * @class ExpirationCache + * @constructor + * @param config {Object} + *
+ *
store
+ *
Data store which implements get,put,remove,clear,keys. If not specified, a new instance of Y.InstanceManager is created.
+ *
meta
+ *
Function which attaches meta data to an item when it is added to the cache. It receives the value as an argument. If not specified, the default is to timestamp the item.
+ *
expire
+ *
(Required) Function which returns true if the item has expired. It receives the meta data and the value as arguments. If a number is specified, it is assumed to be a duration in milliseconds.
+ *
stats
+ *
Pass true if you want to collect basic statistics. Pass a function if you want to control what information is stored for each key. The function receives the key, the value, and the stat object.
+ *
+ */ + +function ExpirationCache(config) +{ + this._store = config.store || new Y.InstanceManager(); + this._meta = config.meta || timestamp; + this._expire = Y.Lang.isNumber(config.expire) ? Y.rbind(expire, null, config.expire) : config.expire; + this._stats = config.stats ? initStats() : null; + + if (Y.Lang.isFunction(config.stats)) + { + this._stats_key_meta = config.stats; + } +} + +function timestamp() +{ + return new Date().getTime(); +} + +function expire(timestamp, value, delta) +{ + var elapsed = new Date().getTime() - timestamp; + return (elapsed > delta); +} + +function initStats() +{ + return { gets: 0, keys: {} }; +} + +function initKeyStats(keys, key) +{ + if (!keys[key]) + { + keys[key] = { puts: 0, gets: 0 }; + } +} + +ExpirationCache.prototype = +{ + /** + * Retrieve a value. + * + * @param key {String} the key of the object to retrieve + * @return {Mixed} the stored object, or undefined if the slot is empty + */ + get: function( + /* string */ key) + { + var obj = this._store.get(key); + if (obj && this._expire(obj.meta, obj.data)) + { + this._store.remove(key); + } + else if (obj) + { + if (this._stats) + { + this._stats.gets++; + + initKeyStats(this._stats.keys, key); + this._stats.keys[key].gets++; + } + + return obj.data; + } + }, + + /** + * Store a value. + * + * @param key {String} the key of the value + * @param value {Object} the value to store + * @return {boolean} false if the key has already been used + */ + put: function( + /* string */ key, + /* obj/fn */ value) + { + var obj = + { + data: value, + meta: this._meta(value) + }; + + if (!this._store.put(key, obj)) + { + return false; + } + + if (this._stats) + { + initKeyStats(this._stats.keys, key); + this._stats.keys[key].puts++; + + if (this._stats_key_meta) + { + this._stats_key_meta(key, value, this._stats.keys[key]); + } + } + return true; + }, + + /** + * Store a value. + * + * @param key {String} the key of the value + * @param value {Object} the value to store + * @return {Mixed} the original value that was in the slot, or undefined if the slot is empty + */ + replace: function( + /* string */ key, + /* obj/fn */ value) + { + var orig = this.remove(key); + this.put(key, value); + return orig; + }, + + /** + * Remove an value. + * + * @param key {String} the key of the value + * @return {mixed} the value that was removed, or undefined if the slot was empty + */ + remove: function( + /* string */ key) + { + var orig = this._store.remove(key); + if (orig) + { + return orig.data; + } + }, + + /** + * Remove all values. + */ + clear: function() + { + this._store.clear(); + }, + + /** + * Remove all expired values. + */ + clean: function() + { + Y.each(this._store.keys(), this.get, this); + }, + + /** + * This resets all the values. + * + * @return {Object} the current stats + */ + dumpStats: function() + { + var stats = this._stats; + this._stats = initStats(); + return stats; + } +}; + +Y.ExpirationCache = ExpirationCache; + + +}, 'gallery-2012.03.23-18-00' ,{requires:['gallery-instancemanager']}); diff --git a/build/gallery-formmgr/gallery-formmgr-debug.js b/build/gallery-formmgr/gallery-formmgr-debug.js index a82ebcdfa8..5abe49a9e8 100644 --- a/build/gallery-formmgr/gallery-formmgr-debug.js +++ b/build/gallery-formmgr/gallery-formmgr-debug.js @@ -168,11 +168,6 @@ function FormManager( this.has_file_inputs = false; } -// CSS class pattern bookends - -var class_re_prefix = '(?:^|\\s)(?:'; -var class_re_suffix = ')(?:\\s|$)'; - /** * The CSS class which marks each row of the form. Typically, each field * (or a very tightly coupled set of fields) is placed in a separate row. @@ -267,7 +262,7 @@ function rowStatusRegex() { if (!cached_row_status_regex) { - cached_row_status_regex = new RegExp(class_re_prefix + rowStatusPattern() + class_re_suffix); + cached_row_status_regex = new RegExp(Y.Node.class_re_prefix + rowStatusPattern() + Y.Node.class_re_suffix); } return cached_row_status_regex; } @@ -442,7 +437,7 @@ FormManager.displayMessage = function( { if (Y.Lang.isUndefined(scroll)) { - scroll = true; + scroll = !had_messages; } e = Y.one(e); @@ -476,7 +471,7 @@ FormManager.displayMessage = function( fieldset.addClass(FormManager.row_status_prefix + type); } - if (!had_messages && scroll) + if (scroll && e.get('offsetHeight') !== 0) { p.scrollIntoView(); try @@ -1149,4 +1144,4 @@ Y.aggregate(FormManager, Y.FormManager); Y.FormManager = FormManager; -}, 'gallery-2011.08.24-23-44' ,{requires:['pluginhost-base','gallery-node-optimizations','gallery-formmgr-css-validation'], optional:['gallery-scrollintoview']}); +}, 'gallery-2012.03.23-18-00' ,{requires:['pluginhost-base','gallery-node-optimizations','gallery-formmgr-css-validation'], optional:['gallery-scrollintoview']}); diff --git a/build/gallery-formmgr/gallery-formmgr-min.js b/build/gallery-formmgr/gallery-formmgr-min.js index e77b759f22..7f3978b1e5 100644 --- a/build/gallery-formmgr/gallery-formmgr-min.js +++ b/build/gallery-formmgr/gallery-formmgr-min.js @@ -1,2 +1,2 @@ -YUI.add("gallery-formmgr",function(d){function j(n,m){m=m||{};j.superclass.constructor.call(this,m);this.form_name=n;this.status_node=d.one(m.status_node);this.enabled=true;this.default_value_map=m.default_value_map;this.validation={fn:{},regex:{}};this.validation_msgs={};this.has_messages=false;this.has_errors=false;this.button_list=[];this.user_button_list=[];this.has_file_inputs=false;}var e="(?:^|\\s)(?:";var h=")(?:\\s|$)";j.row_marker_class="formmgr-row";j.field_marker_class="formmgr-field";j.status_marker_class="formmgr-message-text";j.status_none_class="formmgr-status-hidden";j.status_success_class="formmgr-status-success";j.status_failure_class="formmgr-status-failure";j.row_status_prefix="formmgr-has";var g;var l;var k;function b(){if(!g){g=j.status_success_class+"|"+j.status_failure_class;}return g;}function c(){if(!l){l=j.row_status_prefix+"([^\\s]+)";}return l;}function a(){if(!k){k=new RegExp(e+c()+h);}return k;}j.getElementStatus=function(o){var n=d.one(o).get("className").match(a());return(n&&n.length>1?n[1]:false);};function f(m){if(d.Lang.isString(m)){return m.replace(/^#/,"");}else{if(m instanceof d.Node){return m.get("id");}else{return m.id;}}}function i(){var q=(this.button_list.length===0);for(var p=0;p=0&&t.options[t.selectedIndex].value!==m.toString()){t.selectedIndex=-1;}}else{if(o=="textarea"){t.value=m;}}}}}}}}j.clearMessage=function(n){var m=d.one(n).getAncestorByClassName(d.FormManager.row_marker_class);if(m&&m.hasClass(c())){m.all("."+d.FormManager.status_marker_class).set("innerHTML","");m.removeClass(c());m.all("."+d.FormManager.field_marker_class).removeClass(c());}};j.displayMessage=function(s,n,u,o,v){if(d.Lang.isUndefined(v)){v=true;}s=d.one(s);var m=s.getAncestorByClassName(j.row_marker_class);if(m&&j.statusTakesPrecedence(j.getElementStatus(m),u)){var r=m.all("."+j.field_marker_class);if(r){r.removeClass(c());}if(n){m.one("."+j.status_marker_class).set("innerHTML",n);}var q=j.row_status_prefix+u;m.replaceClass(c(),q);r=s.getAncestorByClassName(j.field_marker_class,true);if(r){r.replaceClass(c(),q);}var w=s.getAncestorByTagName("fieldset");if(w&&j.statusTakesPrecedence(j.getElementStatus(w),u)){w.removeClass(c());w.addClass(j.row_status_prefix+u);}if(!o&&v){m.scrollIntoView();try{s.focus();}catch(t){}}return true;}return false;};d.extend(j,d.Plugin.Host,{getForm:function(){if(!this.form){this.form=d.config.doc.forms[this.form_name];}return this.form;},hasFileInputs:function(){return this.has_file_inputs;},setStatusNode:function(m){this.status_node=d.one(m);},setDefaultValues:function(m){this.default_value_map=m;},setDefaultValue:function(n,m){this.default_value_map[n]=m;},saveCurrentValuesAsDefault:function(){this.default_value_map={};this.button_list=[];i.call(this);},setFunction:function(n,m){this.validation.fn[f(n)]=m;},setRegex:function(o,n,m){o=f(o);if(d.Lang.isString(n)){this.validation.regex[o]=new RegExp(n,m);}else{this.validation.regex[o]=n;}if(!this.validation_msgs[o]||!this.validation_msgs[o].regex){d.error(d.substitute("No error message provided for regex validation of {id}!",{id:o}),null,"FormManager");}},setErrorMessages:function(n,m){this.validation_msgs[f(n)]=m;},addErrorMessage:function(o,m,n){o=f(o);if(!this.validation_msgs[o]){this.validation_msgs[o]={};}this.validation_msgs[o][m]=n;},clearForm:function(){this.clearMessages();this.form.reset();this.postPopulateForm();},populateForm:function(){if(!this.default_value_map){this.default_value_map={};}this.clearMessages();i.call(this);this.postPopulateForm();},postPopulateForm:function(){},isChanged:function(){for(var o=0;o1?k[1]:false);};function e(k){if(c.Lang.isString(k)){return k.replace(/^#/,"");}else{if(k instanceof c.Node){return k.get("id");}else{return k.id;}}}function g(){var o=(this.button_list.length===0);for(var n=0;n=0&&r.options[r.selectedIndex].value!==k.toString()){r.selectedIndex=-1;}}else{if(m=="textarea"){r.value=k;}}}}}}}}h.clearMessage=function(l){var k=c.one(l).getAncestorByClassName(c.FormManager.row_marker_class);if(k&&k.hasClass(d())){k.all("."+c.FormManager.status_marker_class).set("innerHTML","");k.removeClass(d());k.all("."+c.FormManager.field_marker_class).removeClass(d());}};h.displayMessage=function(q,l,s,m,t){if(c.Lang.isUndefined(t)){t=!m;}q=c.one(q);var k=q.getAncestorByClassName(h.row_marker_class);if(k&&h.statusTakesPrecedence(h.getElementStatus(k),s)){var o=k.all("."+h.field_marker_class);if(o){o.removeClass(d());}if(l){k.one("."+h.status_marker_class).set("innerHTML",l);}var n=h.row_status_prefix+s;k.replaceClass(d(),n);o=q.getAncestorByClassName(h.field_marker_class,true);if(o){o.replaceClass(d(),n);}var u=q.getAncestorByTagName("fieldset");if(u&&h.statusTakesPrecedence(h.getElementStatus(u),s)){u.removeClass(d());u.addClass(h.row_status_prefix+s);}if(t&&q.get("offsetHeight")!==0){k.scrollIntoView();try{q.focus();}catch(r){}}return true;}return false;};c.extend(h,c.Plugin.Host,{getForm:function(){if(!this.form){this.form=c.config.doc.forms[this.form_name];}return this.form;},hasFileInputs:function(){return this.has_file_inputs;},setStatusNode:function(k){this.status_node=c.one(k);},setDefaultValues:function(k){this.default_value_map=k;},setDefaultValue:function(l,k){this.default_value_map[l]=k;},saveCurrentValuesAsDefault:function(){this.default_value_map={};this.button_list=[];g.call(this);},setFunction:function(l,k){this.validation.fn[e(l)]=k;},setRegex:function(m,l,k){m=e(m);if(c.Lang.isString(l)){this.validation.regex[m]=new RegExp(l,k);}else{this.validation.regex[m]=l;}if(!this.validation_msgs[m]||!this.validation_msgs[m].regex){c.error(c.substitute("No error message provided for regex validation of {id}!",{id:m}),null,"FormManager");}},setErrorMessages:function(l,k){this.validation_msgs[e(l)]=k;},addErrorMessage:function(m,k,l){m=e(m);if(!this.validation_msgs[m]){this.validation_msgs[m]={};}this.validation_msgs[m][k]=l;},clearForm:function(){this.clearMessages();this.form.reset();this.postPopulateForm();},populateForm:function(){if(!this.default_value_map){this.default_value_map={};}this.clearMessages();g.call(this);this.postPopulateForm();},postPopulateForm:function(){},isChanged:function(){for(var m=0;mfalse if the slot was empty + * @param id {String} the id of the object + * @return {mixed} the object that was removed, or false if the slot was empty */ remove: function( /* string */ id) @@ -118,11 +120,19 @@ InstanceManager.prototype = this._map = {}; }, + /** + * Returns list of all stored keys. + */ + keys: function() + { + return Y.Object.keys(this._map); + }, + /** * Call a function on every object. * - * @param behavior {Function|String|Object} The function to call or the name of the function or an object {fn:,scope:} - * @param arguments {Array} The arguments to pass to the function. + * @param behavior {Function|String|Object} the function to call or the name of the function or an object {fn:,scope:} + * @param arguments {Array} the arguments to pass to the function * @param skip_unconstructed {boolean} Optional. Pass true to skip unconstructed slots. */ applyToAll: function( @@ -168,4 +178,4 @@ InstanceManager.prototype = Y.InstanceManager = InstanceManager; -}, 'gallery-2011.06.01-20-18' ); +}, 'gallery-2012.03.23-18-00' ); diff --git a/build/gallery-instancemanager/gallery-instancemanager-min.js b/build/gallery-instancemanager/gallery-instancemanager-min.js index 30df73a684..f5e819b8f7 100644 --- a/build/gallery-instancemanager/gallery-instancemanager-min.js +++ b/build/gallery-instancemanager/gallery-instancemanager-min.js @@ -1 +1 @@ -YUI.add("gallery-instancemanager",function(b){function a(){this._map={};this._constructors={};}a.prototype={get:function(g){if(this._map[g]===null&&this._constructors[g]){var f=this._constructors[g];var d=f.fn.prototype?b.Object(f.fn.prototype):null;var e=f.fn.apply(d,f.args);this._map[g]=b.Lang.isUndefined(e)?d:e;}return this._map[g]||false;},getIfConstructed:function(c){return this._map[c]||false;},put:function(e,d,c){if(this._map[e]){return false;}else{if(b.Lang.isFunction(d)){this._constructors[e]={fn:d,args:b.Lang.isArray(c)?c:[c]};this._map[e]=null;return true;}else{this._map[e]=d;return true;}}},remove:function(d){if(this._map[d]){var c=this._map[d];delete this._map[d];return c;}else{return false;}},clear:function(){this._map={};},applyToAll:function(f,e,d){var g=this._map,h=b.Lang.isFunction(f),c=b.Lang.isObject(f);b.Object.each(g,function(l,i){if(!l&&d){return;}else{if(!l){l=this.get(i);}}if(h||c){var k=h?f:f.fn,j=h?window:f.scope;k.apply(j,[{key:i,value:l}].concat(e));}else{if(l&&b.Lang.isFunction(l[f])){l[f].apply(l,e);}}},this);}};b.InstanceManager=a;},"gallery-2011.06.01-20-18"); \ No newline at end of file +YUI.add("gallery-instancemanager",function(b){function a(){this._map={};this._constructors={};}a.prototype={get:function(g){if(this._map[g]===null&&this._constructors[g]){var f=this._constructors[g];var d=f.fn.prototype?b.Object(f.fn.prototype):null;var e=f.fn.apply(d,f.args);this._map[g]=b.Lang.isUndefined(e)?d:e;}return this._map[g]||false;},getIfConstructed:function(c){return this._map[c]||false;},put:function(e,d,c){if(this._map[e]){return false;}else{if(b.Lang.isFunction(d)){this._constructors[e]={fn:d,args:b.Lang.isArray(c)?c:[c]};this._map[e]=null;return true;}else{this._map[e]=d;return true;}}},remove:function(d){if(this._map[d]){var c=this._map[d];delete this._map[d];return c;}else{return false;}},clear:function(){this._map={};},keys:function(){return b.Object.keys(this._map);},applyToAll:function(f,e,d){var g=this._map,h=b.Lang.isFunction(f),c=b.Lang.isObject(f);b.Object.each(g,function(l,i){if(!l&&d){return;}else{if(!l){l=this.get(i);}}if(h||c){var k=h?f:f.fn,j=h?window:f.scope;k.apply(j,[{key:i,value:l}].concat(e));}else{if(l&&b.Lang.isFunction(l[f])){l[f].apply(l,e);}}},this);}};b.InstanceManager=a;},"gallery-2012.03.23-18-00"); \ No newline at end of file diff --git a/build/gallery-instancemanager/gallery-instancemanager.js b/build/gallery-instancemanager/gallery-instancemanager.js index 04ebfe5dc0..b80e571ae3 100644 --- a/build/gallery-instancemanager/gallery-instancemanager.js +++ b/build/gallery-instancemanager/gallery-instancemanager.js @@ -27,7 +27,8 @@ InstanceManager.prototype = /** * Retrieve an object. * - * @param id {String} The id of the object to retrieve. + * @param id {String} the id of the object to retrieve + * @return {Mixed} the stored object, or false if the slot is empty */ get: function( /* string */ id) @@ -46,7 +47,8 @@ InstanceManager.prototype = /** * Retrieve an object only if it has already been constructed. * - * @param id {String} The id of the object to retrieve. + * @param id {String} the id of the object to retrieve + * @return {Mixed} the stored object, or false if the slot is empty */ getIfConstructed: function( /* string */ id) @@ -57,10 +59,10 @@ InstanceManager.prototype = /** * Store an object or ctor+args. * - * @param id {String} The id of the object. - * @param objOrCtor {Object|Function} The object or the object's constructor or a factory method. - * @param args {Array} The array of arguments to pass to the constructor. - * @return {boolean} false if the id has already been used + * @param id {String} the id of the object + * @param objOrCtor {Object|Function} the object or the object's constructor or a factory method + * @param args {Array} the array of arguments to pass to the constructor + * @return {Boolean} false if the id has already been used */ put: function( /* string */ id, @@ -92,8 +94,8 @@ InstanceManager.prototype = /** * Remove an object. * - * @param id {String} The id of the object. - * @return {mixed} the object that was removed or false if the slot was empty + * @param id {String} the id of the object + * @return {mixed} the object that was removed, or false if the slot was empty */ remove: function( /* string */ id) @@ -118,11 +120,19 @@ InstanceManager.prototype = this._map = {}; }, + /** + * Returns list of all stored keys. + */ + keys: function() + { + return Y.Object.keys(this._map); + }, + /** * Call a function on every object. * - * @param behavior {Function|String|Object} The function to call or the name of the function or an object {fn:,scope:} - * @param arguments {Array} The arguments to pass to the function. + * @param behavior {Function|String|Object} the function to call or the name of the function or an object {fn:,scope:} + * @param arguments {Array} the arguments to pass to the function * @param skip_unconstructed {boolean} Optional. Pass true to skip unconstructed slots. */ applyToAll: function( @@ -168,4 +178,4 @@ InstanceManager.prototype = Y.InstanceManager = InstanceManager; -}, 'gallery-2011.06.01-20-18' ); +}, 'gallery-2012.03.23-18-00' ); diff --git a/build/gallery-intl-arabicnumerals/gallery-intl-arabicnumerals-debug.js b/build/gallery-intl-arabicnumerals/gallery-intl-arabicnumerals-debug.js new file mode 100644 index 0000000000..be4a2f0143 --- /dev/null +++ b/build/gallery-intl-arabicnumerals/gallery-intl-arabicnumerals-debug.js @@ -0,0 +1,33 @@ +YUI.add('gallery-intl-arabicnumerals', function(Y) { + +Y.mix(Y.namespace("Intl"), { + _arabicNumeralsTrans: {"\u0660": 0, "\u0661": 1, "\u0662": 2, "\u0663": 3, "\u0664": 4, "\u0665": 5, "\u0666": 6, "\u0667": 7, "\u0668": 8, "\u0669": 9}, + _extendedArabicNumeralsTrans: {"\u06F0": 0, "\u06F1": 1, "\u06F2": 2, "\u06F3": 3, "\u06F4": 4, "\u06F5": 5, "\u06F6": 6, "\u06F7": 7, "\u06F8": 8, "\u06F9": 9}, + _replaceArabicNumerals: function (str) { + var _this = this; + str = str.replace(/[\u0660-\u0669]/g, function (c) {return _this._arabicNumeralsTrans[c]; }); + return str.replace(/[\u06F0-\u06F9]/g, function (c) {return _this._extendedArabicNumeralsTrans[c]; }); + }, + parseInt: function (str, base) { + if (base === null) { base = 10; } // Not sure if anybody would use another base, but if so, it's here + return parseInt(this._replaceArabicNumerals(str), base); + }, + parseFloat: function (str) { + str = this._replaceArabicNumerals(str); + return parseFloat(str.replace("\u066B", ".")); + + }, + textToArabicNumerals: function (str) { + // Change to the Proper Decimal place + str = str.replace(/(?=(^|\D)\d+)(\.)(?=\d+(\D|$))/g, "\u066B"); + return str.replace(/\d/g, function (c) { return String.fromCharCode(parseInt(c, 10) + 0x0660); }); + }, + textToExtendedArabicNumerals: function (str) { + // Change to the Proper Decimal place + str = str.replace(/(?=(^|\D)\d+)(\.)(?=\d+(\D|$))/g, "\u066B"); + return str.replace(/\d/g, function (c) { return String.fromCharCode(parseInt(c, 10) + 0x06F0); }); + } +}); + + +}, 'gallery-2012.03.23-18-00' ,{skinnable:false}); diff --git a/build/gallery-intl-arabicnumerals/gallery-intl-arabicnumerals-min.js b/build/gallery-intl-arabicnumerals/gallery-intl-arabicnumerals-min.js new file mode 100644 index 0000000000..5f92ce16b3 --- /dev/null +++ b/build/gallery-intl-arabicnumerals/gallery-intl-arabicnumerals-min.js @@ -0,0 +1 @@ +YUI.add("gallery-intl-arabicnumerals",function(a){a.mix(a.namespace("Intl"),{_arabicNumeralsTrans:{"\u0660":0,"\u0661":1,"\u0662":2,"\u0663":3,"\u0664":4,"\u0665":5,"\u0666":6,"\u0667":7,"\u0668":8,"\u0669":9},_extendedArabicNumeralsTrans:{"\u06F0":0,"\u06F1":1,"\u06F2":2,"\u06F3":3,"\u06F4":4,"\u06F5":5,"\u06F6":6,"\u06F7":7,"\u06F8":8,"\u06F9":9},_replaceArabicNumerals:function(b){var c=this;b=b.replace(/[\u0660-\u0669]/g,function(d){return c._arabicNumeralsTrans[d];});return b.replace(/[\u06F0-\u06F9]/g,function(d){return c._extendedArabicNumeralsTrans[d];});},parseInt:function(c,b){if(b===null){b=10;}return parseInt(this._replaceArabicNumerals(c),b);},parseFloat:function(b){b=this._replaceArabicNumerals(b);return parseFloat(b.replace("\u066B","."));},textToArabicNumerals:function(b){b=b.replace(/(?=(^|\D)\d+)(\.)(?=\d+(\D|$))/g,"\u066B");return b.replace(/\d/g,function(d){return String.fromCharCode(parseInt(d,10)+1632);});},textToExtendedArabicNumerals:function(b){b=b.replace(/(?=(^|\D)\d+)(\.)(?=\d+(\D|$))/g,"\u066B");return b.replace(/\d/g,function(d){return String.fromCharCode(parseInt(d,10)+1776);});}});},"gallery-2012.03.23-18-00",{skinnable:false}); \ No newline at end of file diff --git a/build/gallery-intl-arabicnumerals/gallery-intl-arabicnumerals.js b/build/gallery-intl-arabicnumerals/gallery-intl-arabicnumerals.js new file mode 100644 index 0000000000..be4a2f0143 --- /dev/null +++ b/build/gallery-intl-arabicnumerals/gallery-intl-arabicnumerals.js @@ -0,0 +1,33 @@ +YUI.add('gallery-intl-arabicnumerals', function(Y) { + +Y.mix(Y.namespace("Intl"), { + _arabicNumeralsTrans: {"\u0660": 0, "\u0661": 1, "\u0662": 2, "\u0663": 3, "\u0664": 4, "\u0665": 5, "\u0666": 6, "\u0667": 7, "\u0668": 8, "\u0669": 9}, + _extendedArabicNumeralsTrans: {"\u06F0": 0, "\u06F1": 1, "\u06F2": 2, "\u06F3": 3, "\u06F4": 4, "\u06F5": 5, "\u06F6": 6, "\u06F7": 7, "\u06F8": 8, "\u06F9": 9}, + _replaceArabicNumerals: function (str) { + var _this = this; + str = str.replace(/[\u0660-\u0669]/g, function (c) {return _this._arabicNumeralsTrans[c]; }); + return str.replace(/[\u06F0-\u06F9]/g, function (c) {return _this._extendedArabicNumeralsTrans[c]; }); + }, + parseInt: function (str, base) { + if (base === null) { base = 10; } // Not sure if anybody would use another base, but if so, it's here + return parseInt(this._replaceArabicNumerals(str), base); + }, + parseFloat: function (str) { + str = this._replaceArabicNumerals(str); + return parseFloat(str.replace("\u066B", ".")); + + }, + textToArabicNumerals: function (str) { + // Change to the Proper Decimal place + str = str.replace(/(?=(^|\D)\d+)(\.)(?=\d+(\D|$))/g, "\u066B"); + return str.replace(/\d/g, function (c) { return String.fromCharCode(parseInt(c, 10) + 0x0660); }); + }, + textToExtendedArabicNumerals: function (str) { + // Change to the Proper Decimal place + str = str.replace(/(?=(^|\D)\d+)(\.)(?=\d+(\D|$))/g, "\u066B"); + return str.replace(/\d/g, function (c) { return String.fromCharCode(parseInt(c, 10) + 0x06F0); }); + } +}); + + +}, 'gallery-2012.03.23-18-00' ,{skinnable:false}); diff --git a/build/gallery-iterable-extras/gallery-iterable-extras-debug.js b/build/gallery-iterable-extras/gallery-iterable-extras-debug.js new file mode 100644 index 0000000000..b93970cd6a --- /dev/null +++ b/build/gallery-iterable-extras/gallery-iterable-extras-debug.js @@ -0,0 +1,259 @@ +YUI.add('gallery-iterable-extras', function(Y) { + +/********************************************************************** + *

Functional programming support for iterable classes. The class must + * implement the iterator() (which must return an object that implements + * next() and atEnd()) and newInstance() methods.

+ * + *

Iterable classes must mix these functions: Y.mix(SomeClass, + * Y.Iterable, false, null, 4); Passing false as the third argument + * allows your class to provide optimized implementations of individual + * functions.

+ * + * @module gallery-iterable-extras + * @class Iterable + */ + +Y.Iterable = +{ + /** + * Executes the supplied function on each item in the list. The + * function receives the value, the index, and the list itself as + * parameters (in that order). + * + * @method each + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + */ + each: function(f, c) + { + var iter = this.iterator(), i = 0; + while (!iter.atEnd()) + { + f.call(c, iter.next(), i, this); + i++; + } + }, + + /** + * Executes the supplied function on each item in the list. Iteration + * stops if the supplied function does not return a truthy value. The + * function receives the value, the index, and the list itself as + * parameters (in that order). + * + * @method every + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Boolean} true if every item in the array returns true from the supplied function, false otherwise + */ + every: function(f, c) + { + var iter = this.iterator(), i = 0; + while (!iter.atEnd()) + { + if (!f.call(c, iter.next(), i, this)) + { + return false; + } + i++; + } + + return true; + }, + + /** + * Executes the supplied function on each item in the list. Returns a + * new list containing the items for which the supplied function + * returned a truthy value. The function receives the value, the + * index, and the object itself as parameters (in that order). + * + * @method filter + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Object} list of items for which the supplied function returned a truthy value (empty if it never returned a truthy value) + */ + filter: function(f, c) + { + var result = this.newInstance(); + + var iter = this.iterator(), i = 0; + while (!iter.atEnd()) + { + var item = iter.next(); + if (f.call(c, item, i, this)) + { + result.append(item); + } + i++; + } + + return result; + }, + + /** + * Executes the supplied function on each item in the list, searching + * for the first item that matches the supplied function. The function + * receives the value, the index, and the object itself as parameters + * (in that order). + * + * @method find + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Mixed} the first item for which the supplied function returns true, or null if it never returns true + */ + find: function(f, c) + { + var iter = this.iterator(), i = 0; + while (!iter.atEnd()) + { + var item = iter.next(); + if (f.call(c, item, i, this)) + { + return item; + } + i++; + } + + return null; + }, + + /** + * Executes the supplied function on each item in the list and returns + * a new list with the results. The function receives the value, the + * index, and the object itself as parameters (in that order). + * + * @method map + * @param f {String} the function to invoke + * @param c {Object} optional context object + * @return {Object} list of all return values + */ + map: function(f, c) + { + var result = this.newInstance(); + + var iter = this.iterator(), i = 0; + while (!iter.atEnd()) + { + result.append(f.call(c, iter.next(), i, this)); + i++; + } + + return result; + }, + + /** + * Partitions an list into two new list, one with the items for which + * the supplied function returns true, and one with the items for which + * the function returns false. The function receives the value, the + * index, and the object itself as parameters (in that order). + * + * @method partition + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Object} object with two properties: matches and rejects. Each is a list containing the items that were selected or rejected by the test function (or an empty object if none). + */ + partition: function(f, c) + { + var result = + { + matches: this.newInstance(), + rejects: this.newInstance() + }; + + var iter = this.iterator(), i = 0; + while (!iter.atEnd()) + { + var item = iter.next(); + result[ f.call(c, item, i, this) ? 'matches' : 'rejects' ].append(item); + i++; + } + + return result; + }, + + /** + * Executes the supplied function on each item in the list, folding the + * list into a single value. The function receives the value returned + * by the previous iteration (or the initial value if this is the first + * iteration), the value being iterated, the index, and the list itself + * as parameters (in that order). The function must return the updated + * value. + * + * @method reduce + * @param init {Mixed} the initial value + * @param f {String} the function to invoke + * @param c {Object} optional context object + * @return {Mixed} final result from iteratively applying the given function to each item in the list + */ + reduce: function(init, f, c) + { + var result = init; + + var iter = this.iterator(), i = 0; + while (!iter.atEnd()) + { + result = f.call(c, result, iter.next(), i, this); + i++; + } + + return result; + }, + + /** + * Executes the supplied function on each item in the list. Returns a + * new list containing the items for which the supplied function + * returned a falsey value. The function receives the value, the + * index, and the object itself as parameters (in that order). + * + * @method reject + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Object} array or object of items for which the supplied function returned a falsey value (empty if it never returned a falsey value) + */ + reject: function(f, c) + { + var result = this.newInstance(); + + var iter = this.iterator(), i = 0; + while (!iter.atEnd()) + { + var item = iter.next(); + if (!f.call(c, item, i, this)) + { + result.append(item); + } + i++; + } + + return result; + }, + + /** + * Executes the supplied function on each item in the list. Iteration + * stops if the supplied function returns a truthy value. The function + * receives the value, the index, and the list itself as parameters + * (in that order). + * + * @method some + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Boolean} true if the function returns a truthy value on any of the items in the array, false otherwise + */ + some: function(f, c) + { + var iter = this.iterator(), i = 0; + while (!iter.atEnd()) + { + if (f.call(c, iter.next(), i, this)) + { + return true; + } + i++; + } + + return false; + } +}; + + +}, 'gallery-2012.03.23-18-00' ,{optional:['gallery-funcprog']}); diff --git a/build/gallery-iterable-extras/gallery-iterable-extras-min.js b/build/gallery-iterable-extras/gallery-iterable-extras-min.js new file mode 100644 index 0000000000..5caae387a0 --- /dev/null +++ b/build/gallery-iterable-extras/gallery-iterable-extras-min.js @@ -0,0 +1 @@ +YUI.add("gallery-iterable-extras",function(a){a.Iterable={each:function(e,g){var b=this.iterator(),d=0;while(!b.atEnd()){e.call(g,b.next(),d,this);d++;}},every:function(e,g){var b=this.iterator(),d=0;while(!b.atEnd()){if(!e.call(g,b.next(),d,this)){return false;}d++;}return true;},filter:function(h,j){var b=this.newInstance();var d=this.iterator(),e=0;while(!d.atEnd()){var g=d.next();if(h.call(j,g,e,this)){b.append(g);}e++;}return b;},find:function(g,h){var b=this.iterator(),d=0;while(!b.atEnd()){var e=b.next();if(g.call(h,e,d,this)){return e;}d++;}return null;},map:function(g,h){var b=this.newInstance();var d=this.iterator(),e=0;while(!d.atEnd()){b.append(g.call(h,d.next(),e,this));e++;}return b;},partition:function(h,j){var b={matches:this.newInstance(),rejects:this.newInstance()};var d=this.iterator(),e=0;while(!d.atEnd()){var g=d.next();b[h.call(j,g,e,this)?"matches":"rejects"].append(g);e++;}return b;},reduce:function(h,g,j){var b=h;var d=this.iterator(),e=0;while(!d.atEnd()){b=g.call(j,b,d.next(),e,this);e++;}return b;},reject:function(h,j){var b=this.newInstance();var d=this.iterator(),e=0;while(!d.atEnd()){var g=d.next();if(!h.call(j,g,e,this)){b.append(g);}e++;}return b;},some:function(e,g){var b=this.iterator(),d=0;while(!b.atEnd()){if(e.call(g,b.next(),d,this)){return true;}d++;}return false;}};},"gallery-2012.03.23-18-00",{optional:["gallery-funcprog"]}); \ No newline at end of file diff --git a/build/gallery-iterable-extras/gallery-iterable-extras.js b/build/gallery-iterable-extras/gallery-iterable-extras.js new file mode 100644 index 0000000000..b93970cd6a --- /dev/null +++ b/build/gallery-iterable-extras/gallery-iterable-extras.js @@ -0,0 +1,259 @@ +YUI.add('gallery-iterable-extras', function(Y) { + +/********************************************************************** + *

Functional programming support for iterable classes. The class must + * implement the iterator() (which must return an object that implements + * next() and atEnd()) and newInstance() methods.

+ * + *

Iterable classes must mix these functions: Y.mix(SomeClass, + * Y.Iterable, false, null, 4); Passing false as the third argument + * allows your class to provide optimized implementations of individual + * functions.

+ * + * @module gallery-iterable-extras + * @class Iterable + */ + +Y.Iterable = +{ + /** + * Executes the supplied function on each item in the list. The + * function receives the value, the index, and the list itself as + * parameters (in that order). + * + * @method each + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + */ + each: function(f, c) + { + var iter = this.iterator(), i = 0; + while (!iter.atEnd()) + { + f.call(c, iter.next(), i, this); + i++; + } + }, + + /** + * Executes the supplied function on each item in the list. Iteration + * stops if the supplied function does not return a truthy value. The + * function receives the value, the index, and the list itself as + * parameters (in that order). + * + * @method every + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Boolean} true if every item in the array returns true from the supplied function, false otherwise + */ + every: function(f, c) + { + var iter = this.iterator(), i = 0; + while (!iter.atEnd()) + { + if (!f.call(c, iter.next(), i, this)) + { + return false; + } + i++; + } + + return true; + }, + + /** + * Executes the supplied function on each item in the list. Returns a + * new list containing the items for which the supplied function + * returned a truthy value. The function receives the value, the + * index, and the object itself as parameters (in that order). + * + * @method filter + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Object} list of items for which the supplied function returned a truthy value (empty if it never returned a truthy value) + */ + filter: function(f, c) + { + var result = this.newInstance(); + + var iter = this.iterator(), i = 0; + while (!iter.atEnd()) + { + var item = iter.next(); + if (f.call(c, item, i, this)) + { + result.append(item); + } + i++; + } + + return result; + }, + + /** + * Executes the supplied function on each item in the list, searching + * for the first item that matches the supplied function. The function + * receives the value, the index, and the object itself as parameters + * (in that order). + * + * @method find + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Mixed} the first item for which the supplied function returns true, or null if it never returns true + */ + find: function(f, c) + { + var iter = this.iterator(), i = 0; + while (!iter.atEnd()) + { + var item = iter.next(); + if (f.call(c, item, i, this)) + { + return item; + } + i++; + } + + return null; + }, + + /** + * Executes the supplied function on each item in the list and returns + * a new list with the results. The function receives the value, the + * index, and the object itself as parameters (in that order). + * + * @method map + * @param f {String} the function to invoke + * @param c {Object} optional context object + * @return {Object} list of all return values + */ + map: function(f, c) + { + var result = this.newInstance(); + + var iter = this.iterator(), i = 0; + while (!iter.atEnd()) + { + result.append(f.call(c, iter.next(), i, this)); + i++; + } + + return result; + }, + + /** + * Partitions an list into two new list, one with the items for which + * the supplied function returns true, and one with the items for which + * the function returns false. The function receives the value, the + * index, and the object itself as parameters (in that order). + * + * @method partition + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Object} object with two properties: matches and rejects. Each is a list containing the items that were selected or rejected by the test function (or an empty object if none). + */ + partition: function(f, c) + { + var result = + { + matches: this.newInstance(), + rejects: this.newInstance() + }; + + var iter = this.iterator(), i = 0; + while (!iter.atEnd()) + { + var item = iter.next(); + result[ f.call(c, item, i, this) ? 'matches' : 'rejects' ].append(item); + i++; + } + + return result; + }, + + /** + * Executes the supplied function on each item in the list, folding the + * list into a single value. The function receives the value returned + * by the previous iteration (or the initial value if this is the first + * iteration), the value being iterated, the index, and the list itself + * as parameters (in that order). The function must return the updated + * value. + * + * @method reduce + * @param init {Mixed} the initial value + * @param f {String} the function to invoke + * @param c {Object} optional context object + * @return {Mixed} final result from iteratively applying the given function to each item in the list + */ + reduce: function(init, f, c) + { + var result = init; + + var iter = this.iterator(), i = 0; + while (!iter.atEnd()) + { + result = f.call(c, result, iter.next(), i, this); + i++; + } + + return result; + }, + + /** + * Executes the supplied function on each item in the list. Returns a + * new list containing the items for which the supplied function + * returned a falsey value. The function receives the value, the + * index, and the object itself as parameters (in that order). + * + * @method reject + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Object} array or object of items for which the supplied function returned a falsey value (empty if it never returned a falsey value) + */ + reject: function(f, c) + { + var result = this.newInstance(); + + var iter = this.iterator(), i = 0; + while (!iter.atEnd()) + { + var item = iter.next(); + if (!f.call(c, item, i, this)) + { + result.append(item); + } + i++; + } + + return result; + }, + + /** + * Executes the supplied function on each item in the list. Iteration + * stops if the supplied function returns a truthy value. The function + * receives the value, the index, and the list itself as parameters + * (in that order). + * + * @method some + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Boolean} true if the function returns a truthy value on any of the items in the array, false otherwise + */ + some: function(f, c) + { + var iter = this.iterator(), i = 0; + while (!iter.atEnd()) + { + if (f.call(c, iter.next(), i, this)) + { + return true; + } + i++; + } + + return false; + } +}; + + +}, 'gallery-2012.03.23-18-00' ,{optional:['gallery-funcprog']}); diff --git a/build/gallery-layout-cols/gallery-layout-cols-debug.js b/build/gallery-layout-cols/gallery-layout-cols-debug.js new file mode 100644 index 0000000000..600f8bcfbe --- /dev/null +++ b/build/gallery-layout-cols/gallery-layout-cols-debug.js @@ -0,0 +1,383 @@ +YUI.add('gallery-layout-cols', function(Y) { + +"use strict"; + +var has_explosive_modules_bug = (0 < Y.UA.ie && Y.UA.ie < 8); + +/** + * PageLayout plugin for managing horizontally stacked columns on a page, + * sandwiched vertically between header and footer. Each column contains + * one or more modules. + * + * @module gallery-layout + * @submodule gallery-layout-cols + */ + +Y.namespace('PageLayoutCols'); + +// must be done after defining Y.PageLayoutCols + +Y.PageLayoutCols.collapse_classes = +{ + vert_parent_class: Y.PageLayout.module_class, + horiz_parent_class: Y.PageLayout.module_cols_class, + collapse_parent_pattern: '(' + Y.PageLayout.expand_left_nub_class + '|' + Y.PageLayout.expand_right_nub_class + ')' +}; + +function adjustHeight( + /* int */ total_height, + /* object */ children) +{ + var h = total_height; + + if (children.hd) + { + h -= children.hd.get('offsetHeight'); + } + if (children.ft) + { + h -= children.ft.get('offsetHeight'); + } + + h -= children.bd.vertMarginBorderPadding(); + + return Math.max(h, Y.PageLayout.min_module_height); +} + +function getHeight( + /* int */ body_height, + /* array */ row_heights, + /* int */ col_index, + /* int */ row_index, + /* object */ module, + /* object */ module_info) +{ + module_info.mbp = module.vertMarginBorderPadding(); + return Math.max(1, Math.floor(body_height * row_heights[ col_index ][ row_index ] / 100.0) - module_info.mbp); +} + +Y.PageLayoutCols.resize = function( + /* PageLayout */ host, + /* enum */ mode, + /* int */ body_width, + /* int */ body_height) +{ + var match_heights = host.get('matchColumnHeights'); + var col_count = host.body_info.outers.size(); + + // fit-to-viewport: adjust for vertically collapsed modules + + if (mode === Y.PageLayout.FIT_TO_VIEWPORT) + { + var row_heights = [], + col_heights = []; + for (var i=0; i 0) + { + uncollapsed_count++; + sum += heights[j]; + } + } + + if (uncollapsed_count < count) + { + for (var j=0; j 0) + { + heights[j] *= (100.0 / sum); + } + } + } + } + } + + // adjust for horizontally collapsed or fixed width modules + + var module_info = {}; + var col_widths = host.body_info.outer_sizes.slice(0); + + var uncollapsed_count = 0, + sum = 0; + for (var i=0; i=0; j--) + { + var module1 = modules.item(j); + if (count == 1 || + (!module && + !module1.hasClass(Y.PageLayout.collapsed_vert_class) && + host.body_info.inner_sizes[i][j] > 0)) + { + module = module1; + w1 = ftc_size[i][j][1]; + } + else + { + var bd = ftc_size[i][j][0]; + host.fire('afterResizeModule', + { + bd: bd, + height: bd.insideHeight(), + width: ftc_size[i][j][1] + }); + } + } + + if (module) + { + var delta = h - host.body_info.outers.item(i).get('offsetHeight'); + if (delta > 0 && module.get('parentNode').hasClass(Y.PageLayout.collapsed_horiz_class)) + { + module.setStyle('height', (module.insideHeight() + delta)+'px'); + } + else // always fire afterResizeModule + { + var children = host._analyzeModule(module); + if (children.bd) + { + var h1 = children.bd.insideHeight() + delta; + module.setStyle('height', 'auto'); + children.bd.setStyle('height', h1+'px'); + host.fire('afterResizeModule', { bd: children.bd, height: h1, width: w1 }); + } + } + } + } + } +}; + + +}, 'gallery-2012.03.23-18-00' ,{requires:['gallery-layout']}); diff --git a/build/gallery-layout-cols/gallery-layout-cols-min.js b/build/gallery-layout-cols/gallery-layout-cols-min.js new file mode 100644 index 0000000000..0210473b0c --- /dev/null +++ b/build/gallery-layout-cols/gallery-layout-cols-min.js @@ -0,0 +1 @@ +YUI.add("gallery-layout-cols",function(b){var d=(00){N++;n+=O[K];}}}if(N0){O[K]*=(100/n);}}}}}var I={};var l=F.body_info.outer_sizes.slice(0);var N=0,n=0;for(var L=0;L=0;K--){var v=Q.item(K);if(y==1||(!g&&!v.hasClass(b.PageLayout.collapsed_vert_class)&&F.body_info.inner_sizes[L][K]>0)){g=v;B=p[L][K][1];}else{var o=p[L][K][0];F.fire("afterResizeModule",{bd:o,height:o.insideHeight(),width:p[L][K][1]});}}if(g){var R=M-F.body_info.outers.item(L).get("offsetHeight");if(R>0&&g.get("parentNode").hasClass(b.PageLayout.collapsed_horiz_class)){g.setStyle("height",(g.insideHeight()+R)+"px");}else{var q=F._analyzeModule(g);if(q.bd){var z=q.bd.insideHeight()+R;g.setStyle("height","auto");q.bd.setStyle("height",z+"px");F.fire("afterResizeModule",{bd:q.bd,height:z,width:B});}}}}}};},"gallery-2012.03.23-18-00",{requires:["gallery-layout"]}); \ No newline at end of file diff --git a/build/gallery-layout-cols/gallery-layout-cols.js b/build/gallery-layout-cols/gallery-layout-cols.js new file mode 100644 index 0000000000..600f8bcfbe --- /dev/null +++ b/build/gallery-layout-cols/gallery-layout-cols.js @@ -0,0 +1,383 @@ +YUI.add('gallery-layout-cols', function(Y) { + +"use strict"; + +var has_explosive_modules_bug = (0 < Y.UA.ie && Y.UA.ie < 8); + +/** + * PageLayout plugin for managing horizontally stacked columns on a page, + * sandwiched vertically between header and footer. Each column contains + * one or more modules. + * + * @module gallery-layout + * @submodule gallery-layout-cols + */ + +Y.namespace('PageLayoutCols'); + +// must be done after defining Y.PageLayoutCols + +Y.PageLayoutCols.collapse_classes = +{ + vert_parent_class: Y.PageLayout.module_class, + horiz_parent_class: Y.PageLayout.module_cols_class, + collapse_parent_pattern: '(' + Y.PageLayout.expand_left_nub_class + '|' + Y.PageLayout.expand_right_nub_class + ')' +}; + +function adjustHeight( + /* int */ total_height, + /* object */ children) +{ + var h = total_height; + + if (children.hd) + { + h -= children.hd.get('offsetHeight'); + } + if (children.ft) + { + h -= children.ft.get('offsetHeight'); + } + + h -= children.bd.vertMarginBorderPadding(); + + return Math.max(h, Y.PageLayout.min_module_height); +} + +function getHeight( + /* int */ body_height, + /* array */ row_heights, + /* int */ col_index, + /* int */ row_index, + /* object */ module, + /* object */ module_info) +{ + module_info.mbp = module.vertMarginBorderPadding(); + return Math.max(1, Math.floor(body_height * row_heights[ col_index ][ row_index ] / 100.0) - module_info.mbp); +} + +Y.PageLayoutCols.resize = function( + /* PageLayout */ host, + /* enum */ mode, + /* int */ body_width, + /* int */ body_height) +{ + var match_heights = host.get('matchColumnHeights'); + var col_count = host.body_info.outers.size(); + + // fit-to-viewport: adjust for vertically collapsed modules + + if (mode === Y.PageLayout.FIT_TO_VIEWPORT) + { + var row_heights = [], + col_heights = []; + for (var i=0; i 0) + { + uncollapsed_count++; + sum += heights[j]; + } + } + + if (uncollapsed_count < count) + { + for (var j=0; j 0) + { + heights[j] *= (100.0 / sum); + } + } + } + } + } + + // adjust for horizontally collapsed or fixed width modules + + var module_info = {}; + var col_widths = host.body_info.outer_sizes.slice(0); + + var uncollapsed_count = 0, + sum = 0; + for (var i=0; i=0; j--) + { + var module1 = modules.item(j); + if (count == 1 || + (!module && + !module1.hasClass(Y.PageLayout.collapsed_vert_class) && + host.body_info.inner_sizes[i][j] > 0)) + { + module = module1; + w1 = ftc_size[i][j][1]; + } + else + { + var bd = ftc_size[i][j][0]; + host.fire('afterResizeModule', + { + bd: bd, + height: bd.insideHeight(), + width: ftc_size[i][j][1] + }); + } + } + + if (module) + { + var delta = h - host.body_info.outers.item(i).get('offsetHeight'); + if (delta > 0 && module.get('parentNode').hasClass(Y.PageLayout.collapsed_horiz_class)) + { + module.setStyle('height', (module.insideHeight() + delta)+'px'); + } + else // always fire afterResizeModule + { + var children = host._analyzeModule(module); + if (children.bd) + { + var h1 = children.bd.insideHeight() + delta; + module.setStyle('height', 'auto'); + children.bd.setStyle('height', h1+'px'); + host.fire('afterResizeModule', { bd: children.bd, height: h1, width: w1 }); + } + } + } + } + } +}; + + +}, 'gallery-2012.03.23-18-00' ,{requires:['gallery-layout']}); diff --git a/build/gallery-layout-rows/gallery-layout-rows-debug.js b/build/gallery-layout-rows/gallery-layout-rows-debug.js new file mode 100644 index 0000000000..77098ff79a --- /dev/null +++ b/build/gallery-layout-rows/gallery-layout-rows-debug.js @@ -0,0 +1,403 @@ +YUI.add('gallery-layout-rows', function(Y) { + +"use strict"; + +var has_no_recalc_auto_bug = (0 < Y.UA.ie && Y.UA.ie < 8), + has_explosive_modules_bug = (0 < Y.UA.ie && Y.UA.ie < 8), + is_borked_dom_access = (0 < Y.UA.ie && Y.UA.ie < 8); + +/** + * PageLayout plugin for managing vertically stacked rows on a page, + * sandwiched vertically between header and footer. Each row contains one + * or more modules. + * + * @module gallery-layout + * @submodule gallery-layout-rows + */ + +Y.namespace('PageLayoutRows'); + +// must be done after defining Y.PageLayoutRows + +Y.PageLayoutRows.collapse_classes = +{ + vert_parent_class: Y.PageLayout.module_rows_class, + horiz_parent_class: Y.PageLayout.module_class, + collapse_parent_pattern: Y.PageLayout.expand_vert_nub_class +}; + +function adjustHeight( + /* int */ total_height, + /* object */ children) +{ + var h = total_height; + + if (is_borked_dom_access) + { + var access_dom_so_it_will_be_right_next_time = children.bd.get('offsetHeight'); + } + + var b = children.root.get('offsetHeight') - children.bd.get('offsetHeight'); + + if (children.hd) + { + h -= children.hd.get('offsetHeight'); + b -= children.hd.get('offsetHeight'); + } + if (children.ft) + { + h -= children.ft.get('offsetHeight'); + b -= children.ft.get('offsetHeight'); + } + + h -= b; + + h -= children.bd.vertMarginBorderPadding(); + + return Math.max(h, Y.PageLayout.min_module_height); +} + +function getWidth( + /* int */ body_width, + /* array */ col_widths, + /* int */ row_index, + /* int */ col_index, + /* object */ module, + /* object */ module_info) +{ + module_info.mbp = module.horizMarginBorderPadding(); + return Math.max(1, Math.floor(body_width * col_widths[ row_index ][ col_index ] / 100.0) - module_info.mbp); +} + +Y.PageLayoutRows.resize = function( + /* PageLayout */ host, + /* enum */ mode, + /* int */ body_width, + /* int */ body_height) +{ + var row_count = host.body_info.outers.size(); + + // reset module heights + // adjust for horizontally collapsed or fixed width modules + + var col_widths = [], + row_widths = []; + for (var i=0; i 0) + { + uncollapsed_count++; + sum += widths[j]; + } + } + + if (uncollapsed_count < count) + { + for (var j=0; j 0) + { + widths[j] *= (100.0 / sum); + } + } + } + } + + // smart fit: if only one module, fit-to-content until it won't fit inside viewport + + var module_info = {}; + if (host.single_module) + { + var module = host.body_info.modules[0].item(0); + var children = host._analyzeModule(module); + if (children.bd) + { + var w = getWidth(row_widths[0], col_widths, 0, 0, module, module_info); + var w1 = Math.max(1, w - children.bd.horizMarginBorderPadding()); + host.fire('beforeResizeModule', { bd: children.bd, height: 'auto', width: w1 }); + host._setWidth(children, w); + children.root.setStyle('height', 'auto'); + children.bd.setStyle('height', 'auto'); + } + + var h = module.totalHeight(); + mode = (h > body_height ? Y.PageLayout.FIT_TO_VIEWPORT : Y.PageLayout.FIT_TO_CONTENT); + + host.body_container.removeClass('FIT_TO_[A-Z_]+'); + } + + // fit-to-content: compute height of each row; requires setting module widths + // fit-to-viewport: adjust for vertically collapsed modules + + if (mode === Y.PageLayout.FIT_TO_CONTENT) + { + var row_heights = []; + for (var i=0; i0){K++;m+=u[G];}}}if(K0){u[G]*=(100/m);}}}}var D={};if(B.single_module){var g=B.body_info.modules[0].item(0);var o=B._analyzeModule(g);if(o.bd){var x=a(I[0],l,0,0,g,D);var v=Math.max(1,x-o.bd.horizMarginBorderPadding());B.fire("beforeResizeModule",{bd:o.bd,height:"auto",width:v});B._setWidth(o,x);o.root.setStyle("height","auto");o.bd.setStyle("height","auto");}var J=g.totalHeight();A=(J>r?d.PageLayout.FIT_TO_VIEWPORT:d.PageLayout.FIT_TO_CONTENT);B.body_container.removeClass("FIT_TO_[A-Z_]+");}if(A===d.PageLayout.FIT_TO_CONTENT){var p=[];for(var H=0;H 0) + { + uncollapsed_count++; + sum += widths[j]; + } + } + + if (uncollapsed_count < count) + { + for (var j=0; j 0) + { + widths[j] *= (100.0 / sum); + } + } + } + } + + // smart fit: if only one module, fit-to-content until it won't fit inside viewport + + var module_info = {}; + if (host.single_module) + { + var module = host.body_info.modules[0].item(0); + var children = host._analyzeModule(module); + if (children.bd) + { + var w = getWidth(row_widths[0], col_widths, 0, 0, module, module_info); + var w1 = Math.max(1, w - children.bd.horizMarginBorderPadding()); + host.fire('beforeResizeModule', { bd: children.bd, height: 'auto', width: w1 }); + host._setWidth(children, w); + children.root.setStyle('height', 'auto'); + children.bd.setStyle('height', 'auto'); + } + + var h = module.totalHeight(); + mode = (h > body_height ? Y.PageLayout.FIT_TO_VIEWPORT : Y.PageLayout.FIT_TO_CONTENT); + + host.body_container.removeClass('FIT_TO_[A-Z_]+'); + } + + // fit-to-content: compute height of each row; requires setting module widths + // fit-to-viewport: adjust for vertically collapsed modules + + if (mode === Y.PageLayout.FIT_TO_CONTENT) + { + var row_heights = []; + for (var i=0; iManages header (layout-hd), body (layout-bd), footer (layout-ft) + * stacked vertically to either fit inside the viewport (fit-to-viewport) + * or adjust to the size of the body content (fit-to-content).

+ * + *

The body content is sub-divided into modules, arranged either in rows + * or columns. The layout is automatically detected based on the marker + * classes attached to the two layers of divs inside layout-bd: either + * layout-module-row > layout-module or layout-module-col > layout-module

+ * + *

Each module has an optional header (layout-m-hd), a body + * (layout-m-bd), and an optional footer (layout-m-ft). You can have + * multiple layout-m-bd's, but only one can be visible at a time. If you + * change the DOM in any way that affects the height of any module header, + * body, or footer, or if you switch bodies, you must call + * elementResized() to reflow the layout. (Technically, you + * do not have to call elementResized() if you modify a module + * body in fit-to-viewport mode, but if you later decide to switch to + * fit-to-content, your optimization will cause trouble.)

+ * + *

If you want a row, column, or module to have a fixed size, add the + * class layout-not-managed to the layout-module-row, layout-module-column, + * or layout-module. Then use CSS to set the width of layout-module (for a + * row) or layout-module-col (for a col), or the height of layout-m-bd.

+ * + *

If the body content is a single module, it expands as the content + * expands (fit-to-content) until it would push the footer below the fold. + * Then it switches to fit-to-viewport so the scrollbar appears on the + * module instead of the entire viewport.

+ * + *

Note that a non-zero margin-top on the top element or a non-zero + * margin-bottom on the bottom element inside any container will break the + * layout because browsers lie about the total height of the container in + * this case. Use padding instead of margin on elements inside headers and + * footers.

+ * + * @module gallery-layout + * @class PageLayout + * @extends Base + * @constructor + * @param config {Object} + */ + +function PageLayout() +{ + PageLayout.superclass.constructor.apply(this, arguments); +} + +PageLayout.NAME = "pagelayout"; + +/** + * @property Y.PageLayout.FIT_TO_VIEWPORT + */ +PageLayout.FIT_TO_VIEWPORT = 0; + +/** + * @property Y.PageLayout.FIT_TO_CONTENT + */ +PageLayout.FIT_TO_CONTENT = 1; + +PageLayout.ATTRS = +{ + /** + * FIT_TO_VIEWPORT sizes the rows to fit everything inside the + * browser's viewport. FIT_TO_CONTENT sizes the rows to eliminate all + * scrollbars on module bodies. Note that you can configure this + * property by putting the CSS class "FIT_TO_VIEWPORT" or + * "FIT_TO_CONTENT" on layout-bd. + * + * @config mode + * @type PageLayout.FIT_TO_VIEWPORT or PageLayout.FIT_TO_CONTENT + * @default PageLayout.FIT_TO_VIEWPORT + */ + mode: + { + value: PageLayout.FIT_TO_VIEWPORT, + validator: function(value) + { + return (value === PageLayout.FIT_TO_VIEWPORT || value === PageLayout.FIT_TO_CONTENT); + } + }, + + /** + * Minimum page width, measured in em's. The page content will not + * collapse narrower than this width. If the viewport is smaller, the + * brower's horizontal scrollbar will appear. + * + * @config minWidth + * @type {Number} em's + * @default 73 (em) 950px @ 13px font + */ + minWidth: + { + value: 73, + validator: function(value) + { + return (Y.Lang.isNumber(value) && value > 0); + } + }, + + /** + * Minimum page height in FIT_TO_VIEWPORT mode, measured in em's. The + * page content will not collapse lower than this height. If the + * viewport is smaller, the brower's vertical scrollbar will appear. + * + * @config minHeight + * @type {Number} em's + * @default 44 (em) 570px @ 13px font + */ + minHeight: + { + value: 44, + validator: function(value) + { + return (Y.Lang.isNumber(value) && value > 0); + } + }, + + /** + * In FIT_TO_CONTENT mode, set this to true to make the footer stick to + * the bottom of the viewport. The default is for the footer to scroll + * along with the rest of the page content. + * + * @config stickyFooter + * @type {Boolean} + * @default false + */ + stickyFooter: + { + value: false, + validator: Y.Lang.isBoolean + }, + + /** + * When organizing modules into columns in FIT_TO_CONTENT mode, set + * this to false to allow each column to be a different height. + * + * @config matchColumnHeights + * @type {Boolean} + * @default true + */ + matchColumnHeights: + { + value: true, + validator: Y.Lang.isBoolean + } +}; + +/** + * @event beforeReflow + * @description Fires before the layout is reflowed. + */ +/** + * @event afterReflow + * @description Fires after the layout is completely reflowed, including viewport scrollbar changes. + */ + +/** + * @event beforeExpandModule + * @description Fires before a module is expanded. + * @param bd {Node} the module body (layout-m-bd) + */ +/** + * @event afterExpandModule + * @description Fires after a module is expanded. + * @param bd {Node} the module body (layout-m-bd) + */ + +/** + * @event beforeCollapseModule + * @description Fires before a module is collapsed. + * @param bd {Node} the module body (layout-m-bd) + */ +/** + * @event afterCollapseModule + * @description Fires after a module is collapsed. + * @param bd {Node} the module body (layout-m-bd) + */ + +/** + * @event beforeResizeModule + * @description Fires before a module is resized. + * @param bd {Node} the module body (layout-m-bd) + * @param height {Number} new height in pixels or "auto" + * @param width {Number} new width in pixels or "auto" + */ +/** + * @event afterResizeModule + * @description Fires after a module is resized. + * @param bd {Node} the module body (layout-m-bd) + * @param height {Number} new height in pixels + * @param width {Number} new width in pixels + */ + +/** + * @property Y.PageLayout.page_header_class + * @type {String} + * @default "layout-hd" + */ +PageLayout.page_header_class = 'layout-hd'; + +/** + * @property Y.PageLayout.page_body_class + * @type {String} + * @default "layout-bd" + */ +PageLayout.page_body_class = 'layout-bd'; + +/** + * @property Y.PageLayout.page_footer_class + * @type {String} + * @default "layout-ft" + */ +PageLayout.page_footer_class = 'layout-ft'; + +/** + * @property Y.PageLayout.module_rows_class + * @type {String} + * @value "layout-module-row" + */ +PageLayout.module_rows_class = 'layout-module-row'; + +/** + * @property Y.PageLayout.module_cols_class + * @type {String} + * @value "layout-module-col" + */ +PageLayout.module_cols_class = 'layout-module-col'; + +/** + * @property Y.PageLayout.module_class + * @type {String} + * @default "layout-module" + */ +PageLayout.module_class = 'layout-module'; + +/** + * @property Y.PageLayout.module_header_class + * @type {String} + * @default "layout-m-hd" + */ +PageLayout.module_header_class = 'layout-m-hd'; + +/** + * @property Y.PageLayout.module_body_class + * @type {String} + * @default "layout-m-bd" + */ +PageLayout.module_body_class = 'layout-m-bd'; + +/** + * @property Y.PageLayout.module_footer_class + * @type {String} + * @default "layout-m-ft" + */ +PageLayout.module_footer_class = 'layout-m-ft'; + +/** + * @property Y.PageLayout.not_managed_class + * @type {String} + * @default "layout-not-managed" + */ +PageLayout.not_managed_class = 'layout-not-managed'; + +/** + * @property Y.PageLayout.collapse_vert_nub_class + * @type {String} + * @default "layout-vert-collapse-nub" + */ +PageLayout.collapse_vert_nub_class = 'layout-vert-collapse-nub'; + +/** + * @property Y.PageLayout.collapse_left_nub_class + * @type {String} + * @default "layout-left-collapse-nub" + */ +PageLayout.collapse_left_nub_class = 'layout-left-collapse-nub'; + +/** + * @property Y.PageLayout.collapse_right_nub_class + * @type {String} + * @default "layout-right-collapse-nub" + */ +PageLayout.collapse_right_nub_class = 'layout-right-collapse-nub'; + +/** + * @property Y.PageLayout.expand_vert_nub_class + * @type {String} + * @default "layout-vert-expand-nub" + */ +PageLayout.expand_vert_nub_class = 'layout-vert-expand-nub'; + +/** + * @property Y.PageLayout.expand_left_nub_class + * @type {String} + * @default "layout-left-expand-nub" + */ +PageLayout.expand_left_nub_class = 'layout-left-expand-nub'; + +/** + * @property Y.PageLayout.expand_right_nub_class + * @type {String} + * @default "layout-right-expand-nub" + */ +PageLayout.expand_right_nub_class = 'layout-right-expand-nub'; + +/** + * @property Y.PageLayout.collapsed_vert_class + * @type {String} + * @default "layout-collapsed-vert" + */ +PageLayout.collapsed_vert_class = 'layout-collapsed-vert'; + +/** + * @property Y.PageLayout.collapsed_horiz_class + * @type {String} + * @default "layout-collapsed-horiz" + */ +PageLayout.collapsed_horiz_class = 'layout-collapsed-horiz'; + +/** + * @property Y.PageLayout.min_module_height + * @type {Number} + * @default 10 (px) + */ +PageLayout.min_module_height = 10; // px + +PageLayout.unmanaged_size = -1; // smaller than any module size (collapsed size = - normal size) + +var mode_regex = /\bFIT_TO_[A-Z_]+/, + row_height_class_re = /(?:^|\s)height:([0-9]+)%/, + col_width_class_re = /(?:^|\s)width:([0-9]+)%/, + + reflow_delay = 100, // ms + + plugin_info = + { + row: + { + module: 'gallery-layout-rows', + plugin: 'PageLayoutRows', + outer_size: row_height_class_re, + inner_size: col_width_class_re + }, + col: + { + module: 'gallery-layout-cols', + plugin: 'PageLayoutCols', + outer_size: col_width_class_re, + inner_size: row_height_class_re + } + }; +/* + dd_group_name: 'satg-layout-dd-group', + drag_target_class: 'satg-layout-dd-target', + drag_nub_class: 'satg-layout-dragnub', + module_header_drag_class: 'satg-layout-draggable', + module_no_drag_class: 'satg-layout-drag-disabled', + bomb_sight_class: 'satg-layout-bomb-sight satg-layout-bomb-sight-rows', + + the_dd_targets = {}; + the_dd_nubs = {}; +*/ + +/* + * Normalize the list of sizes so they add up to 100%. + */ +function normalizeSizes( + /* array */ list, + /* regex */ pattern) +{ + // collect sizes + + var sizes = Y.map(list, function(module) + { + if (module.hasClass(PageLayout.not_managed_class)) + { + return PageLayout.unmanaged_size; + } + + var m = module.get('className').match(pattern); + return (m && m.length ? parseInt(m[1], 10) : 0); + }); + + // analyze + + var info = Y.reduce(sizes, [0,0], function(value, size) + { + if (size > 0) + { + value[0] += size; + } + else if (size === 0) + { + value[1]++; + } + return value; + }); + + var sum = info[0], blank_count = info[1]; + + // fill in blanks + + if (blank_count > 0) + { + var blank_size = Math.max((100 - sum) / blank_count, 10); + + sizes = Y.map(sizes, function(size) + { + return (size === 0 ? blank_size : size); + }); + + sum = Y.reduce(sizes, 0, function(sum, size, i) + { + return (size < 0 ? sum : sum + size); + }); + } + + // normalize + + return Y.map(sizes, function(size) + { + return (size > 0 ? size * (100.0 / sum) : size); + }); +} + +function updateFitClass() +{ + this.body_container.replaceClass('FIT_TO_(VIEWPORT|CONTENT)', + this.get('mode') === PageLayout.FIT_TO_VIEWPORT ? 'FIT_TO_VIEWPORT' : 'FIT_TO_CONTENT'); +} + +function reparentFooter() +{ + if (!this.footer_container) + { + return; + } + + if (this.get('mode') === PageLayout.FIT_TO_VIEWPORT || this.get('stickyFooter')) + { + this.body_container.get('parentNode').insertBefore(this.footer_container, this.body_container.next(function(node) + { + return node.get('tagName').toLowerCase() != 'script'; + })); + } + else + { + this.body_container.appendChild(this.footer_container); + } +} + +function resize() +{ + if (!this.layout_plugin || !this.body_container) + { + return; + } + + // check if viewport changed + + var mode = this.single_module ? Y.PageLayout.FIT_TO_VIEWPORT : this.get('mode'); + var sticky_footer = this.get('stickyFooter'); + + this.body_container.setStyle('overflowX', + mode === Y.PageLayout.FIT_TO_CONTENT ? 'auto' : 'hidden'); + this.body_container.setStyle('overflowY', + mode === Y.PageLayout.FIT_TO_CONTENT ? 'scroll' : 'hidden'); + + var viewport = + { + w: this.body_container.get('winWidth'), + h: this.body_container.get('winHeight') + }; + + var resize_event = arguments[0] && arguments[0].type == 'resize'; // IE7 generates no-op's + if (resize_event && + (viewport.w === this.viewport.w && + viewport.h === this.viewport.h)) + { + return; + } + + this.viewport = viewport; + + this.fire('beforeReflow'); // after confirming that viewport really has changed + + // set width of hd,bd,ft and height of bd + + var min_width = Y.Node.emToPx(this.get('minWidth')); + var body_width = Math.max(this.viewport.w, min_width); + if (this.header_container) + { + this.header_container.setStyle('width', body_width+'px'); + } + this.body_container.setStyle('width', (body_width - this.body_horiz_mbp)+'px'); + if (this.footer_container) + { + this.footer_container.setStyle('width', sticky_footer ? body_width+'px' : 'auto'); + } + body_width = this.body_container.get('clientWidth') - this.body_horiz_mbp; + + this.viewport.bcw = this.body_container.get('clientWidth'); + + var h = this.viewport.h; + var h_min = Y.Node.emToPx(this.get('minHeight')); + if (mode === Y.PageLayout.FIT_TO_VIEWPORT && h < h_min) + { + h = h_min; + Y.one(document.documentElement).setStyle('overflowY', 'auto'); + } + else if (!window.console || !window.console.layout_force_viewport_scrollbars) // remove inactive vertical scrollbar in IE + { + Y.one(document.documentElement).setStyle('overflowY', 'hidden'); + } + + if (this.header_container) + { + h -= this.header_container.get('offsetHeight'); + } + if (this.footer_container && + (mode === Y.PageLayout.FIT_TO_VIEWPORT || sticky_footer)) + { + h -= this.footer_container.get('offsetHeight'); + } + + if (mode === Y.PageLayout.FIT_TO_VIEWPORT) + { + var body_height = h - this.body_vert_mbp; + } + else if (h < 0) // FIT_TO_CONTENT doesn't enforce min height + { + h = 10 + this.body_vert_mbp; // arbitrary, positive number + } + + this.body_container.setStyle('height', (h - this.body_vert_mbp)+'px'); + + // resize modules + + this.layout_plugin.resize(this, mode, body_width, body_height); + + // show body and footer + + this.body_container.setStyle('visibility', 'visible'); + if (this.footer_container) + { + this.footer_container.setStyle('visibility', 'visible'); + } + + Y.Lang.later(100, this, checkViewportSize); +} + +/* + * Check if the viewport size has changed, usually due to the browser + * removing no-longer-needed scrollbars. If the viewport size is + * stable, fires the afterReflow event. + */ +function checkViewportSize() +{ + if (this.body_container.get('winWidth') != this.viewport.w || + this.body_container.get('winHeight') != this.viewport.h || + this.body_container.get('clientWidth') != this.viewport.bcw) + { + resize.call(this); + } + else + { + this.fire('afterReflow'); + } +} + +/* + * Expand the module containing the event target. + */ +function expandModule( + /* event */ e) +{ + var node = e.currentTarget; + + function expand( + /* string */ parent_class_name, + /* string */ collapsed_class) + { + var p = node.getAncestorByClassName(this.layout_plugin.collapse_classes[parent_class_name]); + if (p && p.hasClass(collapsed_class)) + { + var children = this._analyzeModule(p); + this.fire('beforeExpandModule', { bd: children.bd }); + + p.removeClass(collapsed_class); + resize.call(this); + + this.fire('afterExpandModule', { bd: children.bd }); + } + } + + if (node.hasClass(PageLayout.expand_vert_nub_class)) + { + expand.call(this, 'vert_parent_class', PageLayout.collapsed_vert_class); + } + else + { + expand.call(this, 'horiz_parent_class', PageLayout.collapsed_horiz_class); + } +} + +/* + * Collapse the module containing the event target. + */ +function collapseModule( + /* event */ e) +{ + var node = e.currentTarget; + + function collapse( + /* string */ parent_class_name, + /* string */ collapsed_class) + { + var p = node.getAncestorByClassName(this.layout_plugin.collapse_classes[parent_class_name]); + if (p && !p.hasClass(collapsed_class)) + { + var children = this._analyzeModule(p); + this.fire('beforeCollapseModule', { bd: children.bd }); + + p.addClass(collapsed_class); + resize.call(this); + + this.fire('afterCollapseModule', { bd: children.bd }); + } + } + + if (node.hasClass(PageLayout.collapse_vert_nub_class)) + { + collapse.call(this, 'vert_parent_class', PageLayout.collapsed_vert_class); + } + else + { + collapse.call(this, 'horiz_parent_class', PageLayout.collapsed_horiz_class); + } +} + +Y.extend(PageLayout, Y.Base, +{ + initializer: function() + { + this.viewport = + { + w: 0, + h: 0, + bcw: 0 + }; + + // find header, body, footer + + var page_blocks = Y.one('body').get('children'); + + var list = page_blocks.filter('.'+PageLayout.page_header_class); + if (list.size() > 1) + { + throw Error('There must be at most one div with class ' + PageLayout.page_header_class); + } + this.header_container = (list.isEmpty() ? null : list.item(0)); + + list = page_blocks.filter('.'+PageLayout.page_body_class); + if (list.size() != 1) + { + throw Error('There must be exactly one div with class ' + PageLayout.page_body_class); + } + this.body_container = list.item(0); + + this.body_horiz_mbp = this.body_container.horizMarginBorderPadding(); + this.body_vert_mbp = this.body_container.vertMarginBorderPadding(); + + var m = this.body_container.get('className').match(mode_regex); + if (m && m.length) + { + this.set('mode', PageLayout[ m[0] ]); + } + + list = page_blocks.filter('.'+PageLayout.page_footer_class); + if (list.size() > 1) + { + throw Error('There must be at most one div with class ' + PageLayout.page_footer_class); + } + this.footer_container = (list.isEmpty() ? null : list.item(0)); + + Y.one(Y.config.win).on('resize', resize, this); +// SDom.textResizeEvent.subscribe(resize, null, this); + + updateFitClass.call(this); + reparentFooter.call(this); + this.rescanBody(); + + // stay in sync + + this.after('modeChange', function() + { + updateFitClass.call(this); + + if (this.body_container) + { + this.body_container.scrollTop = 0; + } + + reparentFooter.call(this); + resize.call(this); + }); + + this.after('minWidthChange', resize); + this.after('minHeightChange', resize); + + this.after('stickyFooterChange', function() + { + reparentFooter.call(this); + resize.call(this); + }); + + this.after('matchColumnHeightsChange', resize); + }, + + /** + * Call this after manually adding or removing modules on the page. + */ + rescanBody: function() + { + Y.detach('PageLayoutCollapse|click'); + + this.body_info = + { + outers: [], + modules: [], // list of modules inside each row + outer_sizes: [], // list of percentages + inner_sizes: [] // list of lists of percentages + }; + + var outer_list = this.body_container.all('div.' + PageLayout.module_rows_class); + var plugin_data = plugin_info.row; + if (outer_list.isEmpty()) + { + outer_list = this.body_container.all('div.' + PageLayout.module_cols_class); + plugin_data = plugin_info.col; + } + if (outer_list.isEmpty()) + { + throw Error('There must be at least one ' + PageLayout.module_rows_class + ' or ' + PageLayout.module_cols_class + ' inside ' + PageLayout.page_body_class + '.'); + } + this.body_info.outers = outer_list; + + var collapse_nub_pattern = + '(' + + PageLayout.collapse_vert_nub_class + '|' + + PageLayout.collapse_left_nub_class + '|' + + PageLayout.collapse_right_nub_class + + ')'; + + var expand_nub_pattern = + '(' + + PageLayout.expand_vert_nub_class + '|' + + PageLayout.expand_left_nub_class + '|' + + PageLayout.expand_right_nub_class + + ')'; + + var row_count = this.body_info.outers.size(); + Y.each(this.body_info.outers, function(row) + { + var row_id = row.generateID(); + this.body_info.outer_sizes.push(100.0/row_count); + + var list = row.all('div.' + PageLayout.module_class); + if (list.isEmpty()) + { + this.body_info.outers = []; + this.body_info.modules = []; + throw Error('There must be at least one ' + PageLayout.module_class + ' inside ' + PageLayout.module_rows_class + '.'); + } + + this.body_info.modules.push(list); + + Y.each(list, function(module) + { + var nub = module.getFirstElementByClassName(collapse_nub_pattern); + if (nub) + { + Y.on('PageLayoutCollapse|click', collapseModule, nub, this); + } + + nub = module.getFirstElementByClassName(expand_nub_pattern); + if (nub) + { + Y.on('PageLayoutCollapse|click', expandModule, nub, this); + } + }, + this); +/* + if (PageLayoutDDProxy) + { + var has_nubs = false; + Y.each(list, function(module) + { + var id = module.generateID(); + module.removeClass(PageLayout.module_no_drag_class); + + if (the_dd_nubs[id]) + { + has_nubs = (the_dd_nubs[id] != 'none'); + } + else + { + var nub = module.getFirstElementByClassName(PageLayout.drag_nub_class); + if (nub) + { + var children = this._analyzeModule(module); + if (children.hd) + { + children.hd.addClass(PageLayout.module_header_drag_class); + the_dd_nubs[id] = + new PageLayoutDDProxy(this, id, children.hd, PageLayout.dd_group_name); + has_nubs = true; + } + } + + if (!the_dd_nubs[id]) + { + the_dd_nubs[id] = 'none'; + } + } + }, + this); + + if (!the_dd_targets[ row_id ] && + (has_nubs || row.hasClass(PageLayout.drag_target_class))) + { + the_dd_targets[ row_id ] = new DDTarget(row_id, PageLayout.dd_group_name); + } + + if (list.size() == 1) + { + list.item(0).addClass(PageLayout.module_no_drag_class); + } + } +*/ + this.body_info.inner_sizes.push( + normalizeSizes(list, plugin_data.inner_size)); + }, + this); + + this.body_info.outer_sizes = + normalizeSizes(this.body_info.outers, plugin_data.outer_size); + + this.single_module = false; + if (this.body_info.outers.size() == 1 && this.body_info.modules[0].size() == 1) + { + plugin_data = plugin_info.row; + this.single_module = true; + } + + var self = this; + Y.use(plugin_data.module, function(Y) + { + self.layout_plugin = Y[ plugin_data.plugin ]; + updateFitClass.call(self); // plugin may modify it + resize.call(self); + }); + }, + + /** + * @return {Number} the height of the sticky header in pixels + */ + getHeaderHeight: function() + { + return (this.header_container ? this.header_container.get('offsetHeight') : 0); + }, + + /** + * @return {Node} the header container (layout-hd) or null if there is no header + */ + getHeaderContainer: function() + { + return this.header_container; + }, + + /** + * @return {Number} the height of the scrolling body in pixels + */ + getBodyHeight: function() + { + return this.body_container.get('offsetHeight'); + }, + + /** + * @return {Node} the body container (layout-bd) + */ + getBodyContainer: function() + { + return this.body_container; + }, + + /** + * @return {Number} the height of the sticky footer in pixels or zero if the footer is not sticky + */ + getFooterHeight: function() + { + return (this.get('stickyFooter') && this.footer_container ? + this.footer_container.get('offsetHeight') : 0); + }, + + /** + * @return {Node} the footer container (layout-ft), or null if there is no footer + */ + getFooterContainer: function() + { + return this.footer_container; + }, + + /** + * @param node {String|Node} .layout-module + * @return {Boolean} true if module is collapsed + */ + moduleIsCollapsed: function( + /* string/Node */ node) + { + var collapsed_pattern = + '(' + + PageLayout.collapsed_horiz_class + '|' + + PageLayout.collapses_vert_class + + ')'; + + node = Y.one(node); + if (node.getFirstElementByClassName(this.layout_plugin.collapse_classes.collapse_parent_pattern)) + { + node = node.get('parentNode'); + } + + return node.hasClass(collapsed_pattern); + }, + + /** + * Expand the specified module. + * + * @param node {String|Node} .layout-module + */ + expandModule: function( + /* string/Node */ node) + { + node = Y.one(node); + var nub = node.getFirstElementByClassName(PageLayout.expand_vert_nub_class); + if (!nub) + { + var expand_horiz_nub_pattern = + '(' + + PageLayout.expand_left_nub_class + '|' + + PageLayout.expand_right_nub_class + + ')'; + + nub = node.getFirstElementByClassName(expand_horiz_nub_pattern); + } + + if (nub) + { + expandModule.call(this, { currentTarget: nub }); + } + }, + + /** + * Collapse the specified module. + * + * @param node {String|Node} .layout-module + */ + collapseModule: function( + /* string/Node */ node) + { + node = Y.one(node); + var nub = node.getFirstElementByClassName(PageLayout.collapse_vert_nub_class); + if (!nub) + { + var collapse_horiz_nub_pattern = + '(' + + PageLayout.collapse_left_nub_class + '|' + + PageLayout.collapse_right_nub_class + + ')'; + + nub = node.getFirstElementByClassName(collapse_horiz_nub_pattern); + } + + if (nub) + { + collapseModule.call(this, { currentTarget: nub }); + } + }, + + /** + * Toggle the collapsed state of the specified layout-module. + * + * @param module {String|Node} .layout-module + */ + toggleModule: function( + /* string/Node */ module) + { + module = Y.one(module); // optimization + if (this.moduleIsCollapsed(module)) + { + this.expandModule(module); + } + else + { + this.collapseModule(module); + } + }, + + /** + * Call this when something changes size, to request a reflow of the + * layout. + * + * @param el {String|Node} element that changed size + * @return {Boolean} true if the element is inside the managed containers + */ + elementResized: function( + /* string/Node */ el) + { + el = Y.one(el); + + if ((this.header_container && this.header_container.contains(el)) || + this.body_container.contains(el) || + (this.footer_container && this.footer_container.contains(el))) + { + if (this.refresh_timer) + { + this.refresh_timer.cancel(); + } + + var t1 = (new Date()).getTime(); + this.refresh_timer = Y.later(reflow_delay, this, function() + { + this.refresh_timer = null; + + // if JS is really busy, wait a bit longer + + var t2 = (new Date()).getTime(); + if (t2 > t1 + 2*reflow_delay) + { + Y.log('deferred reflow: ' + (t2-t1), 'info', 'layout'); + this.elementResized(el); + } + else + { + resize.call(this); + } + }); + + return true; + } + else + { + return false; + } + }, + + /** + * Returns the components of the module. + * + * @param root {Node} .layout-module + * @return {Object} root,hd,bd,ft + * @private + */ + _analyzeModule: function( + /* node */ root) + { + var result = + { + root: root, + hd: null, + bd: null, + ft: null + }; + + // two step process avoid scanning into the module body + + var bd = root.one('.'+PageLayout.module_body_class); + if (!bd) + { + return result; + } + + var list = bd.siblings().filter('.'+PageLayout.module_body_class); + list.unshift(bd); + result.bd = list.find(function(n) + { + return (n.get('offsetWidth') > 0); + }); + if (!result.bd) + { + result.bd = bd; + } + + if (result.bd) + { + result.hd = result.bd.siblings().filter('.'+PageLayout.module_header_class).item(0); + result.ft = result.bd.siblings().filter('.'+PageLayout.module_footer_class).item(0); + } + + return result; + }, + + /** + * Set the width of a module. + * + * @param children {Object} root,hd,bd,ft + * @param w {Number} width in pixels + * @private + */ + _setWidth: function( + /* object */ children, + /* int */ w) + { + children.root.setStyle('width', w+'px'); + } +}); + +Y.PageLayout = PageLayout; + + +}, 'gallery-2012.03.23-18-00' ,{skinnable:true, optional:['gallery-layout-rows','gallery-layout-cols'], requires:['base','gallery-funcprog','gallery-node-optimizations','gallery-dimensions','gallery-nodelist-extras2']}); diff --git a/build/gallery-layout/gallery-layout-min.js b/build/gallery-layout/gallery-layout-min.js new file mode 100644 index 0000000000..d75e084bc7 --- /dev/null +++ b/build/gallery-layout/gallery-layout-min.js @@ -0,0 +1,2 @@ +YUI.add("gallery-layout",function(a){function m(){m.superclass.constructor.apply(this,arguments);}m.NAME="pagelayout";m.FIT_TO_VIEWPORT=0;m.FIT_TO_CONTENT=1;m.ATTRS={mode:{value:m.FIT_TO_VIEWPORT,validator:function(o){return(o===m.FIT_TO_VIEWPORT||o===m.FIT_TO_CONTENT);}},minWidth:{value:73,validator:function(o){return(a.Lang.isNumber(o)&&o>0);}},minHeight:{value:44,validator:function(o){return(a.Lang.isNumber(o)&&o>0);}},stickyFooter:{value:false,validator:a.Lang.isBoolean},matchColumnHeights:{value:true,validator:a.Lang.isBoolean}};m.page_header_class="layout-hd";m.page_body_class="layout-bd";m.page_footer_class="layout-ft";m.module_rows_class="layout-module-row";m.module_cols_class="layout-module-col";m.module_class="layout-module";m.module_header_class="layout-m-hd";m.module_body_class="layout-m-bd";m.module_footer_class="layout-m-ft";m.not_managed_class="layout-not-managed";m.collapse_vert_nub_class="layout-vert-collapse-nub";m.collapse_left_nub_class="layout-left-collapse-nub";m.collapse_right_nub_class="layout-right-collapse-nub";m.expand_vert_nub_class="layout-vert-expand-nub";m.expand_left_nub_class="layout-left-expand-nub";m.expand_right_nub_class="layout-right-expand-nub";m.collapsed_vert_class="layout-collapsed-vert";m.collapsed_horiz_class="layout-collapsed-horiz";m.min_module_height=10;m.unmanaged_size=-1;var h=/\bFIT_TO_[A-Z_]+/,c=/(?:^|\s)height:([0-9]+)%/,f=/(?:^|\s)width:([0-9]+)%/,d=100,n={row:{module:"gallery-layout-rows",plugin:"PageLayoutRows",outer_size:c,inner_size:f},col:{module:"gallery-layout-cols",plugin:"PageLayoutCols",outer_size:f,inner_size:c}};function g(t,s){var r=a.map(t,function(w){if(w.hasClass(m.not_managed_class)){return m.unmanaged_size;}var v=w.get("className").match(s);return(v&&v.length?parseInt(v[1],10):0);});var u=a.reduce(r,[0,0],function(w,v){if(v>0){w[0]+=v;}else{if(v===0){w[1]++;}}return w;});var q=u[0],p=u[1];if(p>0){var o=Math.max((100-q)/p,10);r=a.map(r,function(v){return(v===0?o:v);});q=a.reduce(r,0,function(x,w,v){return(w<0?x:x+w);});}return a.map(r,function(v){return(v>0?v*(100/q):v);});}function j(){this.body_container.replaceClass("FIT_TO_(VIEWPORT|CONTENT)",this.get("mode")===m.FIT_TO_VIEWPORT?"FIT_TO_VIEWPORT":"FIT_TO_CONTENT");}function e(){if(!this.footer_container){return;}if(this.get("mode")===m.FIT_TO_VIEWPORT||this.get("stickyFooter")){this.body_container.get("parentNode").insertBefore(this.footer_container,this.body_container.next(function(o){return o.get("tagName").toLowerCase()!="script";}));}else{this.body_container.appendChild(this.footer_container);}}function b(){if(!this.layout_plugin||!this.body_container){return;}var s=this.single_module?a.PageLayout.FIT_TO_VIEWPORT:this.get("mode");var v=this.get("stickyFooter");this.body_container.setStyle("overflowX",s===a.PageLayout.FIT_TO_CONTENT?"auto":"hidden");this.body_container.setStyle("overflowY",s===a.PageLayout.FIT_TO_CONTENT?"scroll":"hidden");var u={w:this.body_container.get("winWidth"),h:this.body_container.get("winHeight")};var o=arguments[0]&&arguments[0].type=="resize";if(o&&(u.w===this.viewport.w&&u.h===this.viewport.h)){return;}this.viewport=u;this.fire("beforeReflow");var p=a.Node.emToPx(this.get("minWidth"));var q=Math.max(this.viewport.w,p);if(this.header_container){this.header_container.setStyle("width",q+"px");}this.body_container.setStyle("width",(q-this.body_horiz_mbp)+"px");if(this.footer_container){this.footer_container.setStyle("width",v?q+"px":"auto");}q=this.body_container.get("clientWidth")-this.body_horiz_mbp;this.viewport.bcw=this.body_container.get("clientWidth");var r=this.viewport.h;var t=a.Node.emToPx(this.get("minHeight"));if(s===a.PageLayout.FIT_TO_VIEWPORT&&r1){throw Error("There must be at most one div with class "+m.page_header_class);}this.header_container=(q.isEmpty()?null:q.item(0));q=p.filter("."+m.page_body_class);if(q.size()!=1){throw Error("There must be exactly one div with class "+m.page_body_class);}this.body_container=q.item(0); +this.body_horiz_mbp=this.body_container.horizMarginBorderPadding();this.body_vert_mbp=this.body_container.vertMarginBorderPadding();var o=this.body_container.get("className").match(h);if(o&&o.length){this.set("mode",m[o[0]]);}q=p.filter("."+m.page_footer_class);if(q.size()>1){throw Error("There must be at most one div with class "+m.page_footer_class);}this.footer_container=(q.isEmpty()?null:q.item(0));a.one(a.config.win).on("resize",b,this);j.call(this);e.call(this);this.rescanBody();this.after("modeChange",function(){j.call(this);if(this.body_container){this.body_container.scrollTop=0;}e.call(this);b.call(this);});this.after("minWidthChange",b);this.after("minHeightChange",b);this.after("stickyFooterChange",function(){e.call(this);b.call(this);});this.after("matchColumnHeightsChange",b);},rescanBody:function(){a.detach("PageLayoutCollapse|click");this.body_info={outers:[],modules:[],outer_sizes:[],inner_sizes:[]};var p=this.body_container.all("div."+m.module_rows_class);var t=n.row;if(p.isEmpty()){p=this.body_container.all("div."+m.module_cols_class);t=n.col;}if(p.isEmpty()){throw Error("There must be at least one "+m.module_rows_class+" or "+m.module_cols_class+" inside "+m.page_body_class+".");}this.body_info.outers=p;var q="("+m.collapse_vert_nub_class+"|"+m.collapse_left_nub_class+"|"+m.collapse_right_nub_class+")";var r="("+m.expand_vert_nub_class+"|"+m.expand_left_nub_class+"|"+m.expand_right_nub_class+")";var s=this.body_info.outers.size();a.each(this.body_info.outers,function(w){var u=w.generateID();this.body_info.outer_sizes.push(100/s);var v=w.all("div."+m.module_class);if(v.isEmpty()){this.body_info.outers=[];this.body_info.modules=[];throw Error("There must be at least one "+m.module_class+" inside "+m.module_rows_class+".");}this.body_info.modules.push(v);a.each(v,function(y){var x=y.getFirstElementByClassName(q);if(x){a.on("PageLayoutCollapse|click",i,x,this);}x=y.getFirstElementByClassName(r);if(x){a.on("PageLayoutCollapse|click",l,x,this);}},this);this.body_info.inner_sizes.push(g(v,t.inner_size));},this);this.body_info.outer_sizes=g(this.body_info.outers,t.outer_size);this.single_module=false;if(this.body_info.outers.size()==1&&this.body_info.modules[0].size()==1){t=n.row;this.single_module=true;}var o=this;a.use(t.module,function(u){o.layout_plugin=u[t.plugin];j.call(o);b.call(o);});},getHeaderHeight:function(){return(this.header_container?this.header_container.get("offsetHeight"):0);},getHeaderContainer:function(){return this.header_container;},getBodyHeight:function(){return this.body_container.get("offsetHeight");},getBodyContainer:function(){return this.body_container;},getFooterHeight:function(){return(this.get("stickyFooter")&&this.footer_container?this.footer_container.get("offsetHeight"):0);},getFooterContainer:function(){return this.footer_container;},moduleIsCollapsed:function(p){var o="("+m.collapsed_horiz_class+"|"+m.collapses_vert_class+")";p=a.one(p);if(p.getFirstElementByClassName(this.layout_plugin.collapse_classes.collapse_parent_pattern)){p=p.get("parentNode");}return p.hasClass(o);},expandModule:function(q){q=a.one(q);var p=q.getFirstElementByClassName(m.expand_vert_nub_class);if(!p){var o="("+m.expand_left_nub_class+"|"+m.expand_right_nub_class+")";p=q.getFirstElementByClassName(o);}if(p){l.call(this,{currentTarget:p});}},collapseModule:function(p){p=a.one(p);var o=p.getFirstElementByClassName(m.collapse_vert_nub_class);if(!o){var q="("+m.collapse_left_nub_class+"|"+m.collapse_right_nub_class+")";o=p.getFirstElementByClassName(q);}if(o){i.call(this,{currentTarget:o});}},toggleModule:function(o){o=a.one(o);if(this.moduleIsCollapsed(o)){this.expandModule(o);}else{this.collapseModule(o);}},elementResized:function(o){o=a.one(o);if((this.header_container&&this.header_container.contains(o))||this.body_container.contains(o)||(this.footer_container&&this.footer_container.contains(o))){if(this.refresh_timer){this.refresh_timer.cancel();}var p=(new Date()).getTime();this.refresh_timer=a.later(d,this,function(){this.refresh_timer=null;var q=(new Date()).getTime();if(q>p+2*d){this.elementResized(o);}else{b.call(this);}});return true;}else{return false;}},_analyzeModule:function(p){var o={root:p,hd:null,bd:null,ft:null};var r=p.one("."+m.module_body_class);if(!r){return o;}var q=r.siblings().filter("."+m.module_body_class);q.unshift(r);o.bd=q.find(function(s){return(s.get("offsetWidth")>0);});if(!o.bd){o.bd=r;}if(o.bd){o.hd=o.bd.siblings().filter("."+m.module_header_class).item(0);o.ft=o.bd.siblings().filter("."+m.module_footer_class).item(0);}return o;},_setWidth:function(p,o){p.root.setStyle("width",o+"px");}});a.PageLayout=m;},"gallery-2012.03.23-18-00",{skinnable:true,optional:["gallery-layout-rows","gallery-layout-cols"],requires:["base","gallery-funcprog","gallery-node-optimizations","gallery-dimensions","gallery-nodelist-extras2"]}); \ No newline at end of file diff --git a/build/gallery-layout/gallery-layout.js b/build/gallery-layout/gallery-layout.js new file mode 100644 index 0000000000..9796fca636 --- /dev/null +++ b/build/gallery-layout/gallery-layout.js @@ -0,0 +1,1125 @@ +YUI.add('gallery-layout', function(Y) { + +"use strict"; + +/** + *

Manages header (layout-hd), body (layout-bd), footer (layout-ft) + * stacked vertically to either fit inside the viewport (fit-to-viewport) + * or adjust to the size of the body content (fit-to-content).

+ * + *

The body content is sub-divided into modules, arranged either in rows + * or columns. The layout is automatically detected based on the marker + * classes attached to the two layers of divs inside layout-bd: either + * layout-module-row > layout-module or layout-module-col > layout-module

+ * + *

Each module has an optional header (layout-m-hd), a body + * (layout-m-bd), and an optional footer (layout-m-ft). You can have + * multiple layout-m-bd's, but only one can be visible at a time. If you + * change the DOM in any way that affects the height of any module header, + * body, or footer, or if you switch bodies, you must call + * elementResized() to reflow the layout. (Technically, you + * do not have to call elementResized() if you modify a module + * body in fit-to-viewport mode, but if you later decide to switch to + * fit-to-content, your optimization will cause trouble.)

+ * + *

If you want a row, column, or module to have a fixed size, add the + * class layout-not-managed to the layout-module-row, layout-module-column, + * or layout-module. Then use CSS to set the width of layout-module (for a + * row) or layout-module-col (for a col), or the height of layout-m-bd.

+ * + *

If the body content is a single module, it expands as the content + * expands (fit-to-content) until it would push the footer below the fold. + * Then it switches to fit-to-viewport so the scrollbar appears on the + * module instead of the entire viewport.

+ * + *

Note that a non-zero margin-top on the top element or a non-zero + * margin-bottom on the bottom element inside any container will break the + * layout because browsers lie about the total height of the container in + * this case. Use padding instead of margin on elements inside headers and + * footers.

+ * + * @module gallery-layout + * @class PageLayout + * @extends Base + * @constructor + * @param config {Object} + */ + +function PageLayout() +{ + PageLayout.superclass.constructor.apply(this, arguments); +} + +PageLayout.NAME = "pagelayout"; + +/** + * @property Y.PageLayout.FIT_TO_VIEWPORT + */ +PageLayout.FIT_TO_VIEWPORT = 0; + +/** + * @property Y.PageLayout.FIT_TO_CONTENT + */ +PageLayout.FIT_TO_CONTENT = 1; + +PageLayout.ATTRS = +{ + /** + * FIT_TO_VIEWPORT sizes the rows to fit everything inside the + * browser's viewport. FIT_TO_CONTENT sizes the rows to eliminate all + * scrollbars on module bodies. Note that you can configure this + * property by putting the CSS class "FIT_TO_VIEWPORT" or + * "FIT_TO_CONTENT" on layout-bd. + * + * @config mode + * @type PageLayout.FIT_TO_VIEWPORT or PageLayout.FIT_TO_CONTENT + * @default PageLayout.FIT_TO_VIEWPORT + */ + mode: + { + value: PageLayout.FIT_TO_VIEWPORT, + validator: function(value) + { + return (value === PageLayout.FIT_TO_VIEWPORT || value === PageLayout.FIT_TO_CONTENT); + } + }, + + /** + * Minimum page width, measured in em's. The page content will not + * collapse narrower than this width. If the viewport is smaller, the + * brower's horizontal scrollbar will appear. + * + * @config minWidth + * @type {Number} em's + * @default 73 (em) 950px @ 13px font + */ + minWidth: + { + value: 73, + validator: function(value) + { + return (Y.Lang.isNumber(value) && value > 0); + } + }, + + /** + * Minimum page height in FIT_TO_VIEWPORT mode, measured in em's. The + * page content will not collapse lower than this height. If the + * viewport is smaller, the brower's vertical scrollbar will appear. + * + * @config minHeight + * @type {Number} em's + * @default 44 (em) 570px @ 13px font + */ + minHeight: + { + value: 44, + validator: function(value) + { + return (Y.Lang.isNumber(value) && value > 0); + } + }, + + /** + * In FIT_TO_CONTENT mode, set this to true to make the footer stick to + * the bottom of the viewport. The default is for the footer to scroll + * along with the rest of the page content. + * + * @config stickyFooter + * @type {Boolean} + * @default false + */ + stickyFooter: + { + value: false, + validator: Y.Lang.isBoolean + }, + + /** + * When organizing modules into columns in FIT_TO_CONTENT mode, set + * this to false to allow each column to be a different height. + * + * @config matchColumnHeights + * @type {Boolean} + * @default true + */ + matchColumnHeights: + { + value: true, + validator: Y.Lang.isBoolean + } +}; + +/** + * @event beforeReflow + * @description Fires before the layout is reflowed. + */ +/** + * @event afterReflow + * @description Fires after the layout is completely reflowed, including viewport scrollbar changes. + */ + +/** + * @event beforeExpandModule + * @description Fires before a module is expanded. + * @param bd {Node} the module body (layout-m-bd) + */ +/** + * @event afterExpandModule + * @description Fires after a module is expanded. + * @param bd {Node} the module body (layout-m-bd) + */ + +/** + * @event beforeCollapseModule + * @description Fires before a module is collapsed. + * @param bd {Node} the module body (layout-m-bd) + */ +/** + * @event afterCollapseModule + * @description Fires after a module is collapsed. + * @param bd {Node} the module body (layout-m-bd) + */ + +/** + * @event beforeResizeModule + * @description Fires before a module is resized. + * @param bd {Node} the module body (layout-m-bd) + * @param height {Number} new height in pixels or "auto" + * @param width {Number} new width in pixels or "auto" + */ +/** + * @event afterResizeModule + * @description Fires after a module is resized. + * @param bd {Node} the module body (layout-m-bd) + * @param height {Number} new height in pixels + * @param width {Number} new width in pixels + */ + +/** + * @property Y.PageLayout.page_header_class + * @type {String} + * @default "layout-hd" + */ +PageLayout.page_header_class = 'layout-hd'; + +/** + * @property Y.PageLayout.page_body_class + * @type {String} + * @default "layout-bd" + */ +PageLayout.page_body_class = 'layout-bd'; + +/** + * @property Y.PageLayout.page_footer_class + * @type {String} + * @default "layout-ft" + */ +PageLayout.page_footer_class = 'layout-ft'; + +/** + * @property Y.PageLayout.module_rows_class + * @type {String} + * @value "layout-module-row" + */ +PageLayout.module_rows_class = 'layout-module-row'; + +/** + * @property Y.PageLayout.module_cols_class + * @type {String} + * @value "layout-module-col" + */ +PageLayout.module_cols_class = 'layout-module-col'; + +/** + * @property Y.PageLayout.module_class + * @type {String} + * @default "layout-module" + */ +PageLayout.module_class = 'layout-module'; + +/** + * @property Y.PageLayout.module_header_class + * @type {String} + * @default "layout-m-hd" + */ +PageLayout.module_header_class = 'layout-m-hd'; + +/** + * @property Y.PageLayout.module_body_class + * @type {String} + * @default "layout-m-bd" + */ +PageLayout.module_body_class = 'layout-m-bd'; + +/** + * @property Y.PageLayout.module_footer_class + * @type {String} + * @default "layout-m-ft" + */ +PageLayout.module_footer_class = 'layout-m-ft'; + +/** + * @property Y.PageLayout.not_managed_class + * @type {String} + * @default "layout-not-managed" + */ +PageLayout.not_managed_class = 'layout-not-managed'; + +/** + * @property Y.PageLayout.collapse_vert_nub_class + * @type {String} + * @default "layout-vert-collapse-nub" + */ +PageLayout.collapse_vert_nub_class = 'layout-vert-collapse-nub'; + +/** + * @property Y.PageLayout.collapse_left_nub_class + * @type {String} + * @default "layout-left-collapse-nub" + */ +PageLayout.collapse_left_nub_class = 'layout-left-collapse-nub'; + +/** + * @property Y.PageLayout.collapse_right_nub_class + * @type {String} + * @default "layout-right-collapse-nub" + */ +PageLayout.collapse_right_nub_class = 'layout-right-collapse-nub'; + +/** + * @property Y.PageLayout.expand_vert_nub_class + * @type {String} + * @default "layout-vert-expand-nub" + */ +PageLayout.expand_vert_nub_class = 'layout-vert-expand-nub'; + +/** + * @property Y.PageLayout.expand_left_nub_class + * @type {String} + * @default "layout-left-expand-nub" + */ +PageLayout.expand_left_nub_class = 'layout-left-expand-nub'; + +/** + * @property Y.PageLayout.expand_right_nub_class + * @type {String} + * @default "layout-right-expand-nub" + */ +PageLayout.expand_right_nub_class = 'layout-right-expand-nub'; + +/** + * @property Y.PageLayout.collapsed_vert_class + * @type {String} + * @default "layout-collapsed-vert" + */ +PageLayout.collapsed_vert_class = 'layout-collapsed-vert'; + +/** + * @property Y.PageLayout.collapsed_horiz_class + * @type {String} + * @default "layout-collapsed-horiz" + */ +PageLayout.collapsed_horiz_class = 'layout-collapsed-horiz'; + +/** + * @property Y.PageLayout.min_module_height + * @type {Number} + * @default 10 (px) + */ +PageLayout.min_module_height = 10; // px + +PageLayout.unmanaged_size = -1; // smaller than any module size (collapsed size = - normal size) + +var mode_regex = /\bFIT_TO_[A-Z_]+/, + row_height_class_re = /(?:^|\s)height:([0-9]+)%/, + col_width_class_re = /(?:^|\s)width:([0-9]+)%/, + + reflow_delay = 100, // ms + + plugin_info = + { + row: + { + module: 'gallery-layout-rows', + plugin: 'PageLayoutRows', + outer_size: row_height_class_re, + inner_size: col_width_class_re + }, + col: + { + module: 'gallery-layout-cols', + plugin: 'PageLayoutCols', + outer_size: col_width_class_re, + inner_size: row_height_class_re + } + }; +/* + dd_group_name: 'satg-layout-dd-group', + drag_target_class: 'satg-layout-dd-target', + drag_nub_class: 'satg-layout-dragnub', + module_header_drag_class: 'satg-layout-draggable', + module_no_drag_class: 'satg-layout-drag-disabled', + bomb_sight_class: 'satg-layout-bomb-sight satg-layout-bomb-sight-rows', + + the_dd_targets = {}; + the_dd_nubs = {}; +*/ + +/* + * Normalize the list of sizes so they add up to 100%. + */ +function normalizeSizes( + /* array */ list, + /* regex */ pattern) +{ + // collect sizes + + var sizes = Y.map(list, function(module) + { + if (module.hasClass(PageLayout.not_managed_class)) + { + return PageLayout.unmanaged_size; + } + + var m = module.get('className').match(pattern); + return (m && m.length ? parseInt(m[1], 10) : 0); + }); + + // analyze + + var info = Y.reduce(sizes, [0,0], function(value, size) + { + if (size > 0) + { + value[0] += size; + } + else if (size === 0) + { + value[1]++; + } + return value; + }); + + var sum = info[0], blank_count = info[1]; + + // fill in blanks + + if (blank_count > 0) + { + var blank_size = Math.max((100 - sum) / blank_count, 10); + + sizes = Y.map(sizes, function(size) + { + return (size === 0 ? blank_size : size); + }); + + sum = Y.reduce(sizes, 0, function(sum, size, i) + { + return (size < 0 ? sum : sum + size); + }); + } + + // normalize + + return Y.map(sizes, function(size) + { + return (size > 0 ? size * (100.0 / sum) : size); + }); +} + +function updateFitClass() +{ + this.body_container.replaceClass('FIT_TO_(VIEWPORT|CONTENT)', + this.get('mode') === PageLayout.FIT_TO_VIEWPORT ? 'FIT_TO_VIEWPORT' : 'FIT_TO_CONTENT'); +} + +function reparentFooter() +{ + if (!this.footer_container) + { + return; + } + + if (this.get('mode') === PageLayout.FIT_TO_VIEWPORT || this.get('stickyFooter')) + { + this.body_container.get('parentNode').insertBefore(this.footer_container, this.body_container.next(function(node) + { + return node.get('tagName').toLowerCase() != 'script'; + })); + } + else + { + this.body_container.appendChild(this.footer_container); + } +} + +function resize() +{ + if (!this.layout_plugin || !this.body_container) + { + return; + } + + // check if viewport changed + + var mode = this.single_module ? Y.PageLayout.FIT_TO_VIEWPORT : this.get('mode'); + var sticky_footer = this.get('stickyFooter'); + + this.body_container.setStyle('overflowX', + mode === Y.PageLayout.FIT_TO_CONTENT ? 'auto' : 'hidden'); + this.body_container.setStyle('overflowY', + mode === Y.PageLayout.FIT_TO_CONTENT ? 'scroll' : 'hidden'); + + var viewport = + { + w: this.body_container.get('winWidth'), + h: this.body_container.get('winHeight') + }; + + var resize_event = arguments[0] && arguments[0].type == 'resize'; // IE7 generates no-op's + if (resize_event && + (viewport.w === this.viewport.w && + viewport.h === this.viewport.h)) + { + return; + } + + this.viewport = viewport; + + this.fire('beforeReflow'); // after confirming that viewport really has changed + + // set width of hd,bd,ft and height of bd + + var min_width = Y.Node.emToPx(this.get('minWidth')); + var body_width = Math.max(this.viewport.w, min_width); + if (this.header_container) + { + this.header_container.setStyle('width', body_width+'px'); + } + this.body_container.setStyle('width', (body_width - this.body_horiz_mbp)+'px'); + if (this.footer_container) + { + this.footer_container.setStyle('width', sticky_footer ? body_width+'px' : 'auto'); + } + body_width = this.body_container.get('clientWidth') - this.body_horiz_mbp; + + this.viewport.bcw = this.body_container.get('clientWidth'); + + var h = this.viewport.h; + var h_min = Y.Node.emToPx(this.get('minHeight')); + if (mode === Y.PageLayout.FIT_TO_VIEWPORT && h < h_min) + { + h = h_min; + Y.one(document.documentElement).setStyle('overflowY', 'auto'); + } + else if (!window.console || !window.console.layout_force_viewport_scrollbars) // remove inactive vertical scrollbar in IE + { + Y.one(document.documentElement).setStyle('overflowY', 'hidden'); + } + + if (this.header_container) + { + h -= this.header_container.get('offsetHeight'); + } + if (this.footer_container && + (mode === Y.PageLayout.FIT_TO_VIEWPORT || sticky_footer)) + { + h -= this.footer_container.get('offsetHeight'); + } + + if (mode === Y.PageLayout.FIT_TO_VIEWPORT) + { + var body_height = h - this.body_vert_mbp; + } + else if (h < 0) // FIT_TO_CONTENT doesn't enforce min height + { + h = 10 + this.body_vert_mbp; // arbitrary, positive number + } + + this.body_container.setStyle('height', (h - this.body_vert_mbp)+'px'); + + // resize modules + + this.layout_plugin.resize(this, mode, body_width, body_height); + + // show body and footer + + this.body_container.setStyle('visibility', 'visible'); + if (this.footer_container) + { + this.footer_container.setStyle('visibility', 'visible'); + } + + Y.Lang.later(100, this, checkViewportSize); +} + +/* + * Check if the viewport size has changed, usually due to the browser + * removing no-longer-needed scrollbars. If the viewport size is + * stable, fires the afterReflow event. + */ +function checkViewportSize() +{ + if (this.body_container.get('winWidth') != this.viewport.w || + this.body_container.get('winHeight') != this.viewport.h || + this.body_container.get('clientWidth') != this.viewport.bcw) + { + resize.call(this); + } + else + { + this.fire('afterReflow'); + } +} + +/* + * Expand the module containing the event target. + */ +function expandModule( + /* event */ e) +{ + var node = e.currentTarget; + + function expand( + /* string */ parent_class_name, + /* string */ collapsed_class) + { + var p = node.getAncestorByClassName(this.layout_plugin.collapse_classes[parent_class_name]); + if (p && p.hasClass(collapsed_class)) + { + var children = this._analyzeModule(p); + this.fire('beforeExpandModule', { bd: children.bd }); + + p.removeClass(collapsed_class); + resize.call(this); + + this.fire('afterExpandModule', { bd: children.bd }); + } + } + + if (node.hasClass(PageLayout.expand_vert_nub_class)) + { + expand.call(this, 'vert_parent_class', PageLayout.collapsed_vert_class); + } + else + { + expand.call(this, 'horiz_parent_class', PageLayout.collapsed_horiz_class); + } +} + +/* + * Collapse the module containing the event target. + */ +function collapseModule( + /* event */ e) +{ + var node = e.currentTarget; + + function collapse( + /* string */ parent_class_name, + /* string */ collapsed_class) + { + var p = node.getAncestorByClassName(this.layout_plugin.collapse_classes[parent_class_name]); + if (p && !p.hasClass(collapsed_class)) + { + var children = this._analyzeModule(p); + this.fire('beforeCollapseModule', { bd: children.bd }); + + p.addClass(collapsed_class); + resize.call(this); + + this.fire('afterCollapseModule', { bd: children.bd }); + } + } + + if (node.hasClass(PageLayout.collapse_vert_nub_class)) + { + collapse.call(this, 'vert_parent_class', PageLayout.collapsed_vert_class); + } + else + { + collapse.call(this, 'horiz_parent_class', PageLayout.collapsed_horiz_class); + } +} + +Y.extend(PageLayout, Y.Base, +{ + initializer: function() + { + this.viewport = + { + w: 0, + h: 0, + bcw: 0 + }; + + // find header, body, footer + + var page_blocks = Y.one('body').get('children'); + + var list = page_blocks.filter('.'+PageLayout.page_header_class); + if (list.size() > 1) + { + throw Error('There must be at most one div with class ' + PageLayout.page_header_class); + } + this.header_container = (list.isEmpty() ? null : list.item(0)); + + list = page_blocks.filter('.'+PageLayout.page_body_class); + if (list.size() != 1) + { + throw Error('There must be exactly one div with class ' + PageLayout.page_body_class); + } + this.body_container = list.item(0); + + this.body_horiz_mbp = this.body_container.horizMarginBorderPadding(); + this.body_vert_mbp = this.body_container.vertMarginBorderPadding(); + + var m = this.body_container.get('className').match(mode_regex); + if (m && m.length) + { + this.set('mode', PageLayout[ m[0] ]); + } + + list = page_blocks.filter('.'+PageLayout.page_footer_class); + if (list.size() > 1) + { + throw Error('There must be at most one div with class ' + PageLayout.page_footer_class); + } + this.footer_container = (list.isEmpty() ? null : list.item(0)); + + Y.one(Y.config.win).on('resize', resize, this); +// SDom.textResizeEvent.subscribe(resize, null, this); + + updateFitClass.call(this); + reparentFooter.call(this); + this.rescanBody(); + + // stay in sync + + this.after('modeChange', function() + { + updateFitClass.call(this); + + if (this.body_container) + { + this.body_container.scrollTop = 0; + } + + reparentFooter.call(this); + resize.call(this); + }); + + this.after('minWidthChange', resize); + this.after('minHeightChange', resize); + + this.after('stickyFooterChange', function() + { + reparentFooter.call(this); + resize.call(this); + }); + + this.after('matchColumnHeightsChange', resize); + }, + + /** + * Call this after manually adding or removing modules on the page. + */ + rescanBody: function() + { + Y.detach('PageLayoutCollapse|click'); + + this.body_info = + { + outers: [], + modules: [], // list of modules inside each row + outer_sizes: [], // list of percentages + inner_sizes: [] // list of lists of percentages + }; + + var outer_list = this.body_container.all('div.' + PageLayout.module_rows_class); + var plugin_data = plugin_info.row; + if (outer_list.isEmpty()) + { + outer_list = this.body_container.all('div.' + PageLayout.module_cols_class); + plugin_data = plugin_info.col; + } + if (outer_list.isEmpty()) + { + throw Error('There must be at least one ' + PageLayout.module_rows_class + ' or ' + PageLayout.module_cols_class + ' inside ' + PageLayout.page_body_class + '.'); + } + this.body_info.outers = outer_list; + + var collapse_nub_pattern = + '(' + + PageLayout.collapse_vert_nub_class + '|' + + PageLayout.collapse_left_nub_class + '|' + + PageLayout.collapse_right_nub_class + + ')'; + + var expand_nub_pattern = + '(' + + PageLayout.expand_vert_nub_class + '|' + + PageLayout.expand_left_nub_class + '|' + + PageLayout.expand_right_nub_class + + ')'; + + var row_count = this.body_info.outers.size(); + Y.each(this.body_info.outers, function(row) + { + var row_id = row.generateID(); + this.body_info.outer_sizes.push(100.0/row_count); + + var list = row.all('div.' + PageLayout.module_class); + if (list.isEmpty()) + { + this.body_info.outers = []; + this.body_info.modules = []; + throw Error('There must be at least one ' + PageLayout.module_class + ' inside ' + PageLayout.module_rows_class + '.'); + } + + this.body_info.modules.push(list); + + Y.each(list, function(module) + { + var nub = module.getFirstElementByClassName(collapse_nub_pattern); + if (nub) + { + Y.on('PageLayoutCollapse|click', collapseModule, nub, this); + } + + nub = module.getFirstElementByClassName(expand_nub_pattern); + if (nub) + { + Y.on('PageLayoutCollapse|click', expandModule, nub, this); + } + }, + this); +/* + if (PageLayoutDDProxy) + { + var has_nubs = false; + Y.each(list, function(module) + { + var id = module.generateID(); + module.removeClass(PageLayout.module_no_drag_class); + + if (the_dd_nubs[id]) + { + has_nubs = (the_dd_nubs[id] != 'none'); + } + else + { + var nub = module.getFirstElementByClassName(PageLayout.drag_nub_class); + if (nub) + { + var children = this._analyzeModule(module); + if (children.hd) + { + children.hd.addClass(PageLayout.module_header_drag_class); + the_dd_nubs[id] = + new PageLayoutDDProxy(this, id, children.hd, PageLayout.dd_group_name); + has_nubs = true; + } + } + + if (!the_dd_nubs[id]) + { + the_dd_nubs[id] = 'none'; + } + } + }, + this); + + if (!the_dd_targets[ row_id ] && + (has_nubs || row.hasClass(PageLayout.drag_target_class))) + { + the_dd_targets[ row_id ] = new DDTarget(row_id, PageLayout.dd_group_name); + } + + if (list.size() == 1) + { + list.item(0).addClass(PageLayout.module_no_drag_class); + } + } +*/ + this.body_info.inner_sizes.push( + normalizeSizes(list, plugin_data.inner_size)); + }, + this); + + this.body_info.outer_sizes = + normalizeSizes(this.body_info.outers, plugin_data.outer_size); + + this.single_module = false; + if (this.body_info.outers.size() == 1 && this.body_info.modules[0].size() == 1) + { + plugin_data = plugin_info.row; + this.single_module = true; + } + + var self = this; + Y.use(plugin_data.module, function(Y) + { + self.layout_plugin = Y[ plugin_data.plugin ]; + updateFitClass.call(self); // plugin may modify it + resize.call(self); + }); + }, + + /** + * @return {Number} the height of the sticky header in pixels + */ + getHeaderHeight: function() + { + return (this.header_container ? this.header_container.get('offsetHeight') : 0); + }, + + /** + * @return {Node} the header container (layout-hd) or null if there is no header + */ + getHeaderContainer: function() + { + return this.header_container; + }, + + /** + * @return {Number} the height of the scrolling body in pixels + */ + getBodyHeight: function() + { + return this.body_container.get('offsetHeight'); + }, + + /** + * @return {Node} the body container (layout-bd) + */ + getBodyContainer: function() + { + return this.body_container; + }, + + /** + * @return {Number} the height of the sticky footer in pixels or zero if the footer is not sticky + */ + getFooterHeight: function() + { + return (this.get('stickyFooter') && this.footer_container ? + this.footer_container.get('offsetHeight') : 0); + }, + + /** + * @return {Node} the footer container (layout-ft), or null if there is no footer + */ + getFooterContainer: function() + { + return this.footer_container; + }, + + /** + * @param node {String|Node} .layout-module + * @return {Boolean} true if module is collapsed + */ + moduleIsCollapsed: function( + /* string/Node */ node) + { + var collapsed_pattern = + '(' + + PageLayout.collapsed_horiz_class + '|' + + PageLayout.collapses_vert_class + + ')'; + + node = Y.one(node); + if (node.getFirstElementByClassName(this.layout_plugin.collapse_classes.collapse_parent_pattern)) + { + node = node.get('parentNode'); + } + + return node.hasClass(collapsed_pattern); + }, + + /** + * Expand the specified module. + * + * @param node {String|Node} .layout-module + */ + expandModule: function( + /* string/Node */ node) + { + node = Y.one(node); + var nub = node.getFirstElementByClassName(PageLayout.expand_vert_nub_class); + if (!nub) + { + var expand_horiz_nub_pattern = + '(' + + PageLayout.expand_left_nub_class + '|' + + PageLayout.expand_right_nub_class + + ')'; + + nub = node.getFirstElementByClassName(expand_horiz_nub_pattern); + } + + if (nub) + { + expandModule.call(this, { currentTarget: nub }); + } + }, + + /** + * Collapse the specified module. + * + * @param node {String|Node} .layout-module + */ + collapseModule: function( + /* string/Node */ node) + { + node = Y.one(node); + var nub = node.getFirstElementByClassName(PageLayout.collapse_vert_nub_class); + if (!nub) + { + var collapse_horiz_nub_pattern = + '(' + + PageLayout.collapse_left_nub_class + '|' + + PageLayout.collapse_right_nub_class + + ')'; + + nub = node.getFirstElementByClassName(collapse_horiz_nub_pattern); + } + + if (nub) + { + collapseModule.call(this, { currentTarget: nub }); + } + }, + + /** + * Toggle the collapsed state of the specified layout-module. + * + * @param module {String|Node} .layout-module + */ + toggleModule: function( + /* string/Node */ module) + { + module = Y.one(module); // optimization + if (this.moduleIsCollapsed(module)) + { + this.expandModule(module); + } + else + { + this.collapseModule(module); + } + }, + + /** + * Call this when something changes size, to request a reflow of the + * layout. + * + * @param el {String|Node} element that changed size + * @return {Boolean} true if the element is inside the managed containers + */ + elementResized: function( + /* string/Node */ el) + { + el = Y.one(el); + + if ((this.header_container && this.header_container.contains(el)) || + this.body_container.contains(el) || + (this.footer_container && this.footer_container.contains(el))) + { + if (this.refresh_timer) + { + this.refresh_timer.cancel(); + } + + var t1 = (new Date()).getTime(); + this.refresh_timer = Y.later(reflow_delay, this, function() + { + this.refresh_timer = null; + + // if JS is really busy, wait a bit longer + + var t2 = (new Date()).getTime(); + if (t2 > t1 + 2*reflow_delay) + { + this.elementResized(el); + } + else + { + resize.call(this); + } + }); + + return true; + } + else + { + return false; + } + }, + + /** + * Returns the components of the module. + * + * @param root {Node} .layout-module + * @return {Object} root,hd,bd,ft + * @private + */ + _analyzeModule: function( + /* node */ root) + { + var result = + { + root: root, + hd: null, + bd: null, + ft: null + }; + + // two step process avoid scanning into the module body + + var bd = root.one('.'+PageLayout.module_body_class); + if (!bd) + { + return result; + } + + var list = bd.siblings().filter('.'+PageLayout.module_body_class); + list.unshift(bd); + result.bd = list.find(function(n) + { + return (n.get('offsetWidth') > 0); + }); + if (!result.bd) + { + result.bd = bd; + } + + if (result.bd) + { + result.hd = result.bd.siblings().filter('.'+PageLayout.module_header_class).item(0); + result.ft = result.bd.siblings().filter('.'+PageLayout.module_footer_class).item(0); + } + + return result; + }, + + /** + * Set the width of a module. + * + * @param children {Object} root,hd,bd,ft + * @param w {Number} width in pixels + * @private + */ + _setWidth: function( + /* object */ children, + /* int */ w) + { + children.root.setStyle('width', w+'px'); + } +}); + +Y.PageLayout = PageLayout; + + +}, 'gallery-2012.03.23-18-00' ,{skinnable:true, optional:['gallery-layout-rows','gallery-layout-cols'], requires:['base','gallery-funcprog','gallery-node-optimizations','gallery-dimensions','gallery-nodelist-extras2']}); diff --git a/build/gallery-lazy-load/gallery-lazy-load-debug.js b/build/gallery-lazy-load/gallery-lazy-load-debug.js new file mode 100644 index 0000000000..e64abd26fc --- /dev/null +++ b/build/gallery-lazy-load/gallery-lazy-load-debug.js @@ -0,0 +1,79 @@ +YUI.add('gallery-lazy-load', function(Y) { + +(function (Y) { + 'use strict'; + + var _Array = Y.Array, + _Env = Y.Env, + _Lang = Y.Lang, + + _attached = _Env._attached, + _config = Y.config, + _loader = _Env._loader, + + _each = Y.each, + _isArray = _Lang.isArray, + _isFunction = _Lang.isFunction, + _use = Y.use; + + Y.lazyLoad = function () { + var args = _Array(arguments), + alreadyAttached = {}, + callbackFunction = args[args.length - 1], + errors = [], + loadErrorFn = _config.loadErrorFn, + onFailure = _loader.onFailure, + onTimeout = _loader.onTimeout; + + if (_isFunction(callbackFunction)) { + args.pop(); + } else { + callbackFunction = null; + } + + if (_isArray(args[0])) { + args = args[0]; + } + + if (!callbackFunction) { + return _use.apply(Y, args); + } + + _each(_attached, function (value, key) { + if (value) { + alreadyAttached[key] = value; + } + }); + + delete _config.loadErrorFn; + + _loader.onFailure = function (error) { + errors.push(error); + }; + + _loader.onTimeout = function (error) { + errors.push(error); + }; + + args.push(function () { + _config.loadErrorFn = loadErrorFn; + _loader.onFailure = onFailure; + _loader.onTimeout = onTimeout; + + var attached = {}; + + _each(_attached, function (value, key) { + if (value && !alreadyAttached[key]) { + attached[key] = value; + } + }); + + callbackFunction(errors.length ? errors : null, attached); + }); + + return _use.apply(Y, args); + }; +}(Y)); + + +}, 'gallery-2012.03.23-18-00' ,{requires:['oop'], skinnable:false}); diff --git a/build/gallery-lazy-load/gallery-lazy-load-min.js b/build/gallery-lazy-load/gallery-lazy-load-min.js new file mode 100644 index 0000000000..48bd9facf8 --- /dev/null +++ b/build/gallery-lazy-load/gallery-lazy-load-min.js @@ -0,0 +1 @@ +YUI.add("gallery-lazy-load",function(a){(function(b){var i=b.Array,l=b.Env,f=b.Lang,c=l._attached,k=b.config,g=l._loader,h=b.each,j=f.isArray,d=f.isFunction,e=b.use;b.lazyLoad=function(){var p=i(arguments),m={},o=p[p.length-1],s=[],n=k.loadErrorFn,q=g.onFailure,r=g.onTimeout;if(d(o)){p.pop();}else{o=null;}if(j(p[0])){p=p[0];}if(!o){return e.apply(b,p);}h(c,function(u,t){if(u){m[t]=u;}});delete k.loadErrorFn;g.onFailure=function(t){s.push(t);};g.onTimeout=function(t){s.push(t);};p.push(function(){k.loadErrorFn=n;g.onFailure=q;g.onTimeout=r;var t={};h(c,function(v,u){if(v&&!m[u]){t[u]=v;}});o(s.length?s:null,t);});return e.apply(b,p);};}(a));},"gallery-2012.03.23-18-00",{requires:["oop"],skinnable:false}); \ No newline at end of file diff --git a/build/gallery-lazy-load/gallery-lazy-load.js b/build/gallery-lazy-load/gallery-lazy-load.js new file mode 100644 index 0000000000..e64abd26fc --- /dev/null +++ b/build/gallery-lazy-load/gallery-lazy-load.js @@ -0,0 +1,79 @@ +YUI.add('gallery-lazy-load', function(Y) { + +(function (Y) { + 'use strict'; + + var _Array = Y.Array, + _Env = Y.Env, + _Lang = Y.Lang, + + _attached = _Env._attached, + _config = Y.config, + _loader = _Env._loader, + + _each = Y.each, + _isArray = _Lang.isArray, + _isFunction = _Lang.isFunction, + _use = Y.use; + + Y.lazyLoad = function () { + var args = _Array(arguments), + alreadyAttached = {}, + callbackFunction = args[args.length - 1], + errors = [], + loadErrorFn = _config.loadErrorFn, + onFailure = _loader.onFailure, + onTimeout = _loader.onTimeout; + + if (_isFunction(callbackFunction)) { + args.pop(); + } else { + callbackFunction = null; + } + + if (_isArray(args[0])) { + args = args[0]; + } + + if (!callbackFunction) { + return _use.apply(Y, args); + } + + _each(_attached, function (value, key) { + if (value) { + alreadyAttached[key] = value; + } + }); + + delete _config.loadErrorFn; + + _loader.onFailure = function (error) { + errors.push(error); + }; + + _loader.onTimeout = function (error) { + errors.push(error); + }; + + args.push(function () { + _config.loadErrorFn = loadErrorFn; + _loader.onFailure = onFailure; + _loader.onTimeout = onTimeout; + + var attached = {}; + + _each(_attached, function (value, key) { + if (value && !alreadyAttached[key]) { + attached[key] = value; + } + }); + + callbackFunction(errors.length ? errors : null, attached); + }); + + return _use.apply(Y, args); + }; +}(Y)); + + +}, 'gallery-2012.03.23-18-00' ,{requires:['oop'], skinnable:false}); diff --git a/build/gallery-linkedlist/gallery-linkedlist-debug.js b/build/gallery-linkedlist/gallery-linkedlist-debug.js new file mode 100644 index 0000000000..f294c41097 --- /dev/null +++ b/build/gallery-linkedlist/gallery-linkedlist-debug.js @@ -0,0 +1,675 @@ +YUI.add('gallery-linkedlist', function(Y) { + +"use strict"; + +/********************************************************************** + * Item stored by LinkedList. + * + * @class LinkedListItem + */ + +/** + * @method constructor + * @param value {Mixed} value to store + * @private + */ + +function LinkedListItem( + /* object */ value) +{ + this.value = value; + this._prev = this._next = null; +} + +LinkedListItem.prototype = +{ + /** + * @return {LinkedListItem} previous item or null + */ + prev: function() + { + return this._prev; + }, + + /** + * @return {LinkedListItem} next item or null + */ + next: function() + { + return this._next; + } +}; + +Y.LinkedListItem = LinkedListItem; +/********************************************************************** + * Iterator for LinkedList. Stable except when the next item is removed by + * calling list.remove() instead of iter.removeNext(). When items are + * inserted into an empty list, the pointer remains at the end, not the + * beginning. + * + * @class LinkedListIterator + */ + +/** + * @method constructor + * @param list {LinkedList} + * @private + */ + +function LinkedListIterator( + /* LinkedList */ list) +{ + this._list = list; + this.moveToBeginning(); +} + +LinkedListIterator.prototype = +{ + /** + * @return {Boolean} true if at the beginning + */ + atBeginning: function() + { + return (!this._next || (!this._at_end && !this._next._prev)); + }, + + /** + * @return {Boolean} true if at the end + */ + atEnd: function() + { + return (!this._next || this._at_end); + }, + + /** + * Move to the beginning of the list. + */ + moveToBeginning: function() + { + this._next = this._list._head; + this._at_end = !this._next; + }, + + /** + * Move to the end of the list. + */ + moveToEnd: function() + { + this._next = this._list._tail; + this._at_end = true; + }, + + /** + * @return {Mixed} next value in the list or undefined if at the end + */ + next: function() + { + if (this._at_end) + { + return; + } + + var result = this._next; + if (this._next && this._next._next) + { + this._next = this._next._next; + } + else + { + this._at_end = true; + } + + if (result) + { + return result.value; + } + }, + + /** + * @return {Mixed} previous value in the list or undefined if at the beginning + */ + prev: function() + { + var result; + if (this._at_end) + { + this._at_end = false; + result = this._next; + } + else if (this._next) + { + result = this._next._prev; + if (result) + { + this._next = result; + } + } + + if (result) + { + return result.value; + } + }, + + /** + * Insert the given value at the iteration position. The inserted item + * will be returned by next(). + * + * @param value {Mixed} value to insert + * @return {LinkedListItem} inserted item + */ + insert: function( + /* object */ value) + { + if (this._at_end || !this._next) + { + this._next = this._list.append(value); + } + else + { + this._next = this._list.insertBefore(value, this._next); + } + + return this._next; + }, + + /** + * Remove the previous item from the list. + * + * @return {LinkedListItem} removed item or undefined if at the end + */ + removePrev: function() + { + var result; + if (this._at_end) + { + result = this._next; + if (this._next) + { + this._next = this._next._prev; + } + } + else if (this._next) + { + result = this._next._prev; + } + + if (result) + { + this._list.remove(result); + return result; + } + }, + + /** + * Remove the next item from the list. + * + * @return {LinkedListItem} removed item or undefined if at the end + */ + removeNext: function() + { + var result; + if (this._next && !this._at_end) + { + result = this._next; + if (this._next && this._next._next) + { + this._next = this._next._next; + } + else + { + this._next = this._next ? this._next._prev : null; + this._at_end = true; + } + } + + if (result) + { + this._list.remove(result); + return result; + } + } +}; +/********************************************************************** + *

Doubly linked list for storing items. Supports iteration via + * LinkedListIterator (returned by this.iterator()) or Y.each(). Also + * supports all the other operations defined in gallery-funcprog.

+ * + *

Direct indexing into the list is not supported, as a reminder that it + * is an expensive operation. Instead, use find() with a function that + * checks the index.

+ * + * @module gallery-linkedlist + * @class LinkedList + * @constructor + * @param list {Mixed} (Optional) any scalar or iterable list + */ + +function LinkedList(list) +{ + this._head = this._tail = null; + + if (arguments.length > 1) + { + list = Y.Array(arguments); + } + else if (!Y.Lang.isUndefined(list) && !(list instanceof LinkedList) && !Y.Array.test(list)) + { + list = Y.Array(list); + } + + if (!Y.Lang.isUndefined(list)) + { + Y.each(list, function(value) + { + this.append(value); + }, + this); + } +} + +function wrap(value) +{ + if (value instanceof LinkedListItem) + { + this.remove(value); + } + else + { + value = new LinkedListItem(value); + } + + return value; +} + +LinkedList.prototype = +{ + /** + * @return {Boolean} true if the list is empty + */ + isEmpty: function() + { + return (!this._head && !this._tail); + }, + + /** + * Warning: This requires traversing the list! Use isEmpty() whenever + * possible. + * + * @return {Number} the number of items in the list + */ + size: function() + { + var count = 0, + item = this._head; + + while (item) + { + count++; + item = item._next; + } + + return count; + }, + + /** + * @return {LinkedListIterator} + */ + iterator: function() + { + return new LinkedListIterator(this); + }, + + /** + * Creates a new, empty LinkedList. + * + * @return {LinkedList} + */ + newInstance: function() + { + return new LinkedList(); + }, + + /** + * @return {LinkedListItem} the first item in the list, or null if the list is empty + */ + head: function() + { + return this._head; + }, + + /** + * @return {LinkedListItem} the last item in the list, or null if the list is empty + */ + tail: function() + { + return this._tail; + }, + + /** + * @param needle {Mixed} the item to search for + * @return {Number} first index of the needle, or -1 if not found + */ + indexOf: function(needle) + { + var iter = this.iterator(), i = 0; + while (!iter.atEnd()) + { + if (iter.next() === needle) + { + return i; + } + i++; + } + + return -1; + }, + + /** + * @param needle {Mixed} the item to search for + * @return {Number} last index of the needle, or -1 if not found + */ + lastIndexOf: function(needle) + { + var iter = this.iterator(), i = this.size(); + iter.moveToEnd(); + while (!iter.atBeginning()) + { + i--; + if (iter.prev() === needle) + { + return i; + } + } + + return -1; + }, + + /** + * Clear the list. + */ + clear: function() + { + this._head = this._tail = null; + }, + + /** + * @param value {Mixed} value to insert + * @param item {LinkedListItem} existing item + * @return {LinkedListItem} inserted item + */ + insertBefore: function( + /* object */ value, + /* item */ item) + { + value = wrap.call(this, value); + + value._prev = item._prev; + value._next = item; + + if (item._prev) + { + item._prev._next = value; + } + else + { + this._head = value; + } + item._prev = value; + + return value; + }, + + /** + * @param item {LinkedListItem} existing item + * @param value {Mixed} value to insert + * @return {LinkedListItem} inserted item + */ + insertAfter: function( + /* item */ item, + /* object */ value) + { + value = wrap.call(this, value); + + value._prev = item; + value._next = item._next; + + if (item._next) + { + item._next._prev = value; + } + else + { + this._tail = value; + } + item._next = value; + + return value; + }, + + /** + * @param value {Mixed} value to prepend + * @return {LinkedListItem} prepended item + */ + prepend: function( + /* object */ value) + { + value = wrap.call(this, value); + + if (this.isEmpty()) + { + this._head = this._tail = value; + } + else + { + this.insertBefore(value, this._head); + } + + return value; + }, + + /** + * @param value {Mixed} value to append + * @return {LinkedListItem} appended item + */ + append: function( + /* object */ value) + { + value = wrap.call(this, value); + + if (this.isEmpty()) + { + this._head = this._tail = value; + } + else + { + this.insertAfter(this._tail, value); + } + + return value; + }, + + /** + * Remove the item from the list. + */ + remove: function( + /* item */ item) + { + if (item._prev) + { + item._prev._next = item._next; + } + else if (item === this._head) + { + this._head = item._next; + if (item._next) + { + item._next._prev = null; + } + } + + if (item._next) + { + item._next._prev = item._prev; + } + else if (item === this._tail) + { + this._tail = item._prev; + if (item._prev) + { + item._prev._next = null; + } + } + + item._prev = item._next = null; + }, + + /** + * Reverses the items in place. + */ + reverse: function() + { + var list = new LinkedList(); + var iter = this.iterator(); + while (!iter.atEnd()) + { + var item = iter.removeNext(); + list.prepend(item); + } + + this._head = list._head; + this._tail = list._tail; + }, + + /** + * @return {Array} + */ + toArray: function() + { + var result = [], + item = this._head; + + while (item) + { + result.push(item.value); + item = item._next; + } + + return result; + } +}; + +Y.mix(LinkedList, Y.Iterable, false, null, 4); + + /** + * Executes the supplied function on each item in the list. The + * function receives the value, the index, and the list itself as + * parameters (in that order). + * + * @method each + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + */ + + /** + * Executes the supplied function on each item in the list. Iteration + * stops if the supplied function does not return a truthy value. The + * function receives the value, the index, and the list itself as + * parameters (in that order). + * + * @method every + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Boolean} true if every item in the array returns true from the supplied function, false otherwise + */ + + /** + * Executes the supplied function on each item in the list. Returns a + * new list containing the items for which the supplied function + * returned a truthy value. The function receives the value, the + * index, and the object itself as parameters (in that order). + * + * @method filter + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Object} list of items for which the supplied function returned a truthy value (empty if it never returned a truthy value) + */ + + /** + * Executes the supplied function on each item in the list, searching + * for the first item that matches the supplied function. The function + * receives the value, the index, and the object itself as parameters + * (in that order). + * + * @method find + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Mixed} the first item for which the supplied function returns true, or null if it never returns true + */ + + /** + * Executes the supplied function on each item in the list and returns + * a new list with the results. The function receives the value, the + * index, and the object itself as parameters (in that order). + * + * @method map + * @param f {String} the function to invoke + * @param c {Object} optional context object + * @return {Object} list of all return values + */ + + /** + * Partitions an list into two new list, one with the items for which + * the supplied function returns true, and one with the items for which + * the function returns false. The function receives the value, the + * index, and the object itself as parameters (in that order). + * + * @method partition + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Object} object with two properties: matches and rejects. Each is a list containing the items that were selected or rejected by the test function (or an empty object if none). + */ + + /** + * Executes the supplied function on each item in the list, folding the + * list into a single value. The function receives the value returned + * by the previous iteration (or the initial value if this is the first + * iteration), the value being iterated, the index, and the list itself + * as parameters (in that order). The function must return the updated + * value. + * + * @method reduce + * @param init {Mixed} the initial value + * @param f {String} the function to invoke + * @param c {Object} optional context object + * @return {Mixed} final result from iteratively applying the given function to each item in the list + */ + + /** + * Executes the supplied function on each item in the list. Returns a + * new list containing the items for which the supplied function + * returned a falsey value. The function receives the value, the + * index, and the object itself as parameters (in that order). + * + * @method reject + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Object} array or object of items for which the supplied function returned a falsey value (empty if it never returned a falsey value) + */ + + /** + * Executes the supplied function on each item in the list. Iteration + * stops if the supplied function returns a truthy value. The function + * receives the value, the index, and the list itself as parameters + * (in that order). + * + * @method some + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Boolean} true if the function returns a truthy value on any of the items in the array, false otherwise + */ + +Y.LinkedList = LinkedList; + + +}, 'gallery-2012.03.23-18-00' ,{requires:['gallery-iterable-extras'], optional:['gallery-funcprog']}); diff --git a/build/gallery-linkedlist/gallery-linkedlist-min.js b/build/gallery-linkedlist/gallery-linkedlist-min.js new file mode 100644 index 0000000000..ee7bc497f2 --- /dev/null +++ b/build/gallery-linkedlist/gallery-linkedlist-min.js @@ -0,0 +1 @@ +YUI.add("gallery-linkedlist",function(e){function c(f){this.value=f;this._prev=this._next=null;}c.prototype={prev:function(){return this._prev;},next:function(){return this._next;}};e.LinkedListItem=c;function a(f){this._list=f;this.moveToBeginning();}a.prototype={atBeginning:function(){return(!this._next||(!this._at_end&&!this._next._prev));},atEnd:function(){return(!this._next||this._at_end);},moveToBeginning:function(){this._next=this._list._head;this._at_end=!this._next;},moveToEnd:function(){this._next=this._list._tail;this._at_end=true;},next:function(){if(this._at_end){return;}var f=this._next;if(this._next&&this._next._next){this._next=this._next._next;}else{this._at_end=true;}if(f){return f.value;}},prev:function(){var f;if(this._at_end){this._at_end=false;f=this._next;}else{if(this._next){f=this._next._prev;if(f){this._next=f;}}}if(f){return f.value;}},insert:function(f){if(this._at_end||!this._next){this._next=this._list.append(f);}else{this._next=this._list.insertBefore(f,this._next);}return this._next;},removePrev:function(){var f;if(this._at_end){f=this._next;if(this._next){this._next=this._next._prev;}}else{if(this._next){f=this._next._prev;}}if(f){this._list.remove(f);return f;}},removeNext:function(){var f;if(this._next&&!this._at_end){f=this._next;if(this._next&&this._next._next){this._next=this._next._next;}else{this._next=this._next?this._next._prev:null;this._at_end=true;}}if(f){this._list.remove(f);return f;}}};function d(f){this._head=this._tail=null;if(arguments.length>1){f=e.Array(arguments);}else{if(!e.Lang.isUndefined(f)&&!(f instanceof d)&&!e.Array.test(f)){f=e.Array(f);}}if(!e.Lang.isUndefined(f)){e.each(f,function(g){this.append(g);},this);}}function b(f){if(f instanceof c){this.remove(f);}else{f=new c(f);}return f;}d.prototype={isEmpty:function(){return(!this._head&&!this._tail);},size:function(){var g=0,f=this._head;while(f){g++;f=f._next;}return g;},iterator:function(){return new a(this);},newInstance:function(){return new d();},head:function(){return this._head;},tail:function(){return this._tail;},indexOf:function(h){var f=this.iterator(),g=0;while(!f.atEnd()){if(f.next()===h){return g;}g++;}return -1;},lastIndexOf:function(h){var f=this.iterator(),g=this.size();f.moveToEnd();while(!f.atBeginning()){g--;if(f.prev()===h){return g;}}return -1;},clear:function(){this._head=this._tail=null;},insertBefore:function(g,f){g=b.call(this,g);g._prev=f._prev;g._next=f;if(f._prev){f._prev._next=g;}else{this._head=g;}f._prev=g;return g;},insertAfter:function(f,g){g=b.call(this,g);g._prev=f;g._next=f._next;if(f._next){f._next._prev=g;}else{this._tail=g;}f._next=g;return g;},prepend:function(f){f=b.call(this,f);if(this.isEmpty()){this._head=this._tail=f;}else{this.insertBefore(f,this._head);}return f;},append:function(f){f=b.call(this,f);if(this.isEmpty()){this._head=this._tail=f;}else{this.insertAfter(this._tail,f);}return f;},remove:function(f){if(f._prev){f._prev._next=f._next;}else{if(f===this._head){this._head=f._next;if(f._next){f._next._prev=null;}}}if(f._next){f._next._prev=f._prev;}else{if(f===this._tail){this._tail=f._prev;if(f._prev){f._prev._next=null;}}}f._prev=f._next=null;},reverse:function(){var h=new d();var f=this.iterator();while(!f.atEnd()){var g=f.removeNext();h.prepend(g);}this._head=h._head;this._tail=h._tail;},toArray:function(){var f=[],g=this._head;while(g){f.push(g.value);g=g._next;}return f;}};e.mix(d,e.Iterable,false,null,4);e.LinkedList=d;},"gallery-2012.03.23-18-00",{requires:["gallery-iterable-extras"],optional:["gallery-funcprog"]}); \ No newline at end of file diff --git a/build/gallery-linkedlist/gallery-linkedlist.js b/build/gallery-linkedlist/gallery-linkedlist.js new file mode 100644 index 0000000000..f294c41097 --- /dev/null +++ b/build/gallery-linkedlist/gallery-linkedlist.js @@ -0,0 +1,675 @@ +YUI.add('gallery-linkedlist', function(Y) { + +"use strict"; + +/********************************************************************** + * Item stored by LinkedList. + * + * @class LinkedListItem + */ + +/** + * @method constructor + * @param value {Mixed} value to store + * @private + */ + +function LinkedListItem( + /* object */ value) +{ + this.value = value; + this._prev = this._next = null; +} + +LinkedListItem.prototype = +{ + /** + * @return {LinkedListItem} previous item or null + */ + prev: function() + { + return this._prev; + }, + + /** + * @return {LinkedListItem} next item or null + */ + next: function() + { + return this._next; + } +}; + +Y.LinkedListItem = LinkedListItem; +/********************************************************************** + * Iterator for LinkedList. Stable except when the next item is removed by + * calling list.remove() instead of iter.removeNext(). When items are + * inserted into an empty list, the pointer remains at the end, not the + * beginning. + * + * @class LinkedListIterator + */ + +/** + * @method constructor + * @param list {LinkedList} + * @private + */ + +function LinkedListIterator( + /* LinkedList */ list) +{ + this._list = list; + this.moveToBeginning(); +} + +LinkedListIterator.prototype = +{ + /** + * @return {Boolean} true if at the beginning + */ + atBeginning: function() + { + return (!this._next || (!this._at_end && !this._next._prev)); + }, + + /** + * @return {Boolean} true if at the end + */ + atEnd: function() + { + return (!this._next || this._at_end); + }, + + /** + * Move to the beginning of the list. + */ + moveToBeginning: function() + { + this._next = this._list._head; + this._at_end = !this._next; + }, + + /** + * Move to the end of the list. + */ + moveToEnd: function() + { + this._next = this._list._tail; + this._at_end = true; + }, + + /** + * @return {Mixed} next value in the list or undefined if at the end + */ + next: function() + { + if (this._at_end) + { + return; + } + + var result = this._next; + if (this._next && this._next._next) + { + this._next = this._next._next; + } + else + { + this._at_end = true; + } + + if (result) + { + return result.value; + } + }, + + /** + * @return {Mixed} previous value in the list or undefined if at the beginning + */ + prev: function() + { + var result; + if (this._at_end) + { + this._at_end = false; + result = this._next; + } + else if (this._next) + { + result = this._next._prev; + if (result) + { + this._next = result; + } + } + + if (result) + { + return result.value; + } + }, + + /** + * Insert the given value at the iteration position. The inserted item + * will be returned by next(). + * + * @param value {Mixed} value to insert + * @return {LinkedListItem} inserted item + */ + insert: function( + /* object */ value) + { + if (this._at_end || !this._next) + { + this._next = this._list.append(value); + } + else + { + this._next = this._list.insertBefore(value, this._next); + } + + return this._next; + }, + + /** + * Remove the previous item from the list. + * + * @return {LinkedListItem} removed item or undefined if at the end + */ + removePrev: function() + { + var result; + if (this._at_end) + { + result = this._next; + if (this._next) + { + this._next = this._next._prev; + } + } + else if (this._next) + { + result = this._next._prev; + } + + if (result) + { + this._list.remove(result); + return result; + } + }, + + /** + * Remove the next item from the list. + * + * @return {LinkedListItem} removed item or undefined if at the end + */ + removeNext: function() + { + var result; + if (this._next && !this._at_end) + { + result = this._next; + if (this._next && this._next._next) + { + this._next = this._next._next; + } + else + { + this._next = this._next ? this._next._prev : null; + this._at_end = true; + } + } + + if (result) + { + this._list.remove(result); + return result; + } + } +}; +/********************************************************************** + *

Doubly linked list for storing items. Supports iteration via + * LinkedListIterator (returned by this.iterator()) or Y.each(). Also + * supports all the other operations defined in gallery-funcprog.

+ * + *

Direct indexing into the list is not supported, as a reminder that it + * is an expensive operation. Instead, use find() with a function that + * checks the index.

+ * + * @module gallery-linkedlist + * @class LinkedList + * @constructor + * @param list {Mixed} (Optional) any scalar or iterable list + */ + +function LinkedList(list) +{ + this._head = this._tail = null; + + if (arguments.length > 1) + { + list = Y.Array(arguments); + } + else if (!Y.Lang.isUndefined(list) && !(list instanceof LinkedList) && !Y.Array.test(list)) + { + list = Y.Array(list); + } + + if (!Y.Lang.isUndefined(list)) + { + Y.each(list, function(value) + { + this.append(value); + }, + this); + } +} + +function wrap(value) +{ + if (value instanceof LinkedListItem) + { + this.remove(value); + } + else + { + value = new LinkedListItem(value); + } + + return value; +} + +LinkedList.prototype = +{ + /** + * @return {Boolean} true if the list is empty + */ + isEmpty: function() + { + return (!this._head && !this._tail); + }, + + /** + * Warning: This requires traversing the list! Use isEmpty() whenever + * possible. + * + * @return {Number} the number of items in the list + */ + size: function() + { + var count = 0, + item = this._head; + + while (item) + { + count++; + item = item._next; + } + + return count; + }, + + /** + * @return {LinkedListIterator} + */ + iterator: function() + { + return new LinkedListIterator(this); + }, + + /** + * Creates a new, empty LinkedList. + * + * @return {LinkedList} + */ + newInstance: function() + { + return new LinkedList(); + }, + + /** + * @return {LinkedListItem} the first item in the list, or null if the list is empty + */ + head: function() + { + return this._head; + }, + + /** + * @return {LinkedListItem} the last item in the list, or null if the list is empty + */ + tail: function() + { + return this._tail; + }, + + /** + * @param needle {Mixed} the item to search for + * @return {Number} first index of the needle, or -1 if not found + */ + indexOf: function(needle) + { + var iter = this.iterator(), i = 0; + while (!iter.atEnd()) + { + if (iter.next() === needle) + { + return i; + } + i++; + } + + return -1; + }, + + /** + * @param needle {Mixed} the item to search for + * @return {Number} last index of the needle, or -1 if not found + */ + lastIndexOf: function(needle) + { + var iter = this.iterator(), i = this.size(); + iter.moveToEnd(); + while (!iter.atBeginning()) + { + i--; + if (iter.prev() === needle) + { + return i; + } + } + + return -1; + }, + + /** + * Clear the list. + */ + clear: function() + { + this._head = this._tail = null; + }, + + /** + * @param value {Mixed} value to insert + * @param item {LinkedListItem} existing item + * @return {LinkedListItem} inserted item + */ + insertBefore: function( + /* object */ value, + /* item */ item) + { + value = wrap.call(this, value); + + value._prev = item._prev; + value._next = item; + + if (item._prev) + { + item._prev._next = value; + } + else + { + this._head = value; + } + item._prev = value; + + return value; + }, + + /** + * @param item {LinkedListItem} existing item + * @param value {Mixed} value to insert + * @return {LinkedListItem} inserted item + */ + insertAfter: function( + /* item */ item, + /* object */ value) + { + value = wrap.call(this, value); + + value._prev = item; + value._next = item._next; + + if (item._next) + { + item._next._prev = value; + } + else + { + this._tail = value; + } + item._next = value; + + return value; + }, + + /** + * @param value {Mixed} value to prepend + * @return {LinkedListItem} prepended item + */ + prepend: function( + /* object */ value) + { + value = wrap.call(this, value); + + if (this.isEmpty()) + { + this._head = this._tail = value; + } + else + { + this.insertBefore(value, this._head); + } + + return value; + }, + + /** + * @param value {Mixed} value to append + * @return {LinkedListItem} appended item + */ + append: function( + /* object */ value) + { + value = wrap.call(this, value); + + if (this.isEmpty()) + { + this._head = this._tail = value; + } + else + { + this.insertAfter(this._tail, value); + } + + return value; + }, + + /** + * Remove the item from the list. + */ + remove: function( + /* item */ item) + { + if (item._prev) + { + item._prev._next = item._next; + } + else if (item === this._head) + { + this._head = item._next; + if (item._next) + { + item._next._prev = null; + } + } + + if (item._next) + { + item._next._prev = item._prev; + } + else if (item === this._tail) + { + this._tail = item._prev; + if (item._prev) + { + item._prev._next = null; + } + } + + item._prev = item._next = null; + }, + + /** + * Reverses the items in place. + */ + reverse: function() + { + var list = new LinkedList(); + var iter = this.iterator(); + while (!iter.atEnd()) + { + var item = iter.removeNext(); + list.prepend(item); + } + + this._head = list._head; + this._tail = list._tail; + }, + + /** + * @return {Array} + */ + toArray: function() + { + var result = [], + item = this._head; + + while (item) + { + result.push(item.value); + item = item._next; + } + + return result; + } +}; + +Y.mix(LinkedList, Y.Iterable, false, null, 4); + + /** + * Executes the supplied function on each item in the list. The + * function receives the value, the index, and the list itself as + * parameters (in that order). + * + * @method each + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + */ + + /** + * Executes the supplied function on each item in the list. Iteration + * stops if the supplied function does not return a truthy value. The + * function receives the value, the index, and the list itself as + * parameters (in that order). + * + * @method every + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Boolean} true if every item in the array returns true from the supplied function, false otherwise + */ + + /** + * Executes the supplied function on each item in the list. Returns a + * new list containing the items for which the supplied function + * returned a truthy value. The function receives the value, the + * index, and the object itself as parameters (in that order). + * + * @method filter + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Object} list of items for which the supplied function returned a truthy value (empty if it never returned a truthy value) + */ + + /** + * Executes the supplied function on each item in the list, searching + * for the first item that matches the supplied function. The function + * receives the value, the index, and the object itself as parameters + * (in that order). + * + * @method find + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Mixed} the first item for which the supplied function returns true, or null if it never returns true + */ + + /** + * Executes the supplied function on each item in the list and returns + * a new list with the results. The function receives the value, the + * index, and the object itself as parameters (in that order). + * + * @method map + * @param f {String} the function to invoke + * @param c {Object} optional context object + * @return {Object} list of all return values + */ + + /** + * Partitions an list into two new list, one with the items for which + * the supplied function returns true, and one with the items for which + * the function returns false. The function receives the value, the + * index, and the object itself as parameters (in that order). + * + * @method partition + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Object} object with two properties: matches and rejects. Each is a list containing the items that were selected or rejected by the test function (or an empty object if none). + */ + + /** + * Executes the supplied function on each item in the list, folding the + * list into a single value. The function receives the value returned + * by the previous iteration (or the initial value if this is the first + * iteration), the value being iterated, the index, and the list itself + * as parameters (in that order). The function must return the updated + * value. + * + * @method reduce + * @param init {Mixed} the initial value + * @param f {String} the function to invoke + * @param c {Object} optional context object + * @return {Mixed} final result from iteratively applying the given function to each item in the list + */ + + /** + * Executes the supplied function on each item in the list. Returns a + * new list containing the items for which the supplied function + * returned a falsey value. The function receives the value, the + * index, and the object itself as parameters (in that order). + * + * @method reject + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Object} array or object of items for which the supplied function returned a falsey value (empty if it never returned a falsey value) + */ + + /** + * Executes the supplied function on each item in the list. Iteration + * stops if the supplied function returns a truthy value. The function + * receives the value, the index, and the list itself as parameters + * (in that order). + * + * @method some + * @param f {Function} the function to execute on each item + * @param c {Object} optional context object + * @return {Boolean} true if the function returns a truthy value on any of the items in the array, false otherwise + */ + +Y.LinkedList = LinkedList; + + +}, 'gallery-2012.03.23-18-00' ,{requires:['gallery-iterable-extras'], optional:['gallery-funcprog']}); diff --git a/build/gallery-md-model/gallery-md-model-debug.js b/build/gallery-md-model/gallery-md-model-debug.js new file mode 100644 index 0000000000..016398e86b --- /dev/null +++ b/build/gallery-md-model/gallery-md-model-debug.js @@ -0,0 +1,1268 @@ +YUI.add('gallery-md-model', function(Y) { + +/** +Record-based data model with APIs for getting, setting, validating, and +syncing attribute values, as well as events for being notified of model changes. + +@module gallery-md-model +**/ + +/** +Record-based data model with APIs for getting, setting, validating, and +syncing attribute values, as well as events for being notified of model changes. + +In most cases, you'll want to create your own subclass of Y.GalleryModel and +customize it to meet your needs. In particular, the sync() and validate() +methods are meant to be overridden by custom implementations. You may also want +to override the parse() method to parse non-generic server responses. + +@class Y.GalleryModel +@constructor +@param [cfg] {Object} Initial configuration attribute plus: +@param [cfg.values] {Object} Sets initial values for the model. + Model will be marked as new and not modified (as if just loaded). +@extends Base +**/ + "use strict"; + + var Lang = Y.Lang, + YArray = Y.Array, + YObject = Y.Object, + EVT_CHANGE = 'change', + EVT_LOADED = 'loaded', + EVT_ERROR = 'error', + EVT_SAVED = 'saved', + EVT_RESET = 'reset', + IS_MODIFIED = 'isModified', + IS_NEW = 'isNew', + DOT = '.', + CHANGE = 'Change'; + + + Y.GalleryModel = Y.Base.create( + 'gallery-md-model', + Y.Base, + [], + { + /** + * Hash of values indexed by field name + * @property _values + * @type Object + * @private + */ + _values: null, + /** + * Hash of values as loaded from the remote source, + * presumed to be the current value there. + * @property _loadedValues + * @type Object + * @private + */ + _loadedValues: null, + /** + * Array of field names that make up the primary key for this record + * @property _primaryKeys + * @type Array + * @private + */ + _primaryKeys: null, + /* + * Y.Base lifecycle method + */ + initializer: function (cfg) { + this._values = {}; + this._loadedValues = {}; + /** + * Fired whenever a data value is changed. + * @event change + * @param {String} ev.name Name of the field changed + * @param {Any} ev.newVal New value of the field. + * @param {Any} ev.prevVal Previous value of the field. + * @param {String|null} ev.src Source of the change event, if any. + */ + this.publish(EVT_CHANGE, { + defaultFn: this._defSetValue + }); + /** + * Fired when new data has been received from the remote source. + * It will be fired even on a save operation if the response contains values. + * The parsed values can be altered on the before (on) listener. + * @event loaded + * @param {Object} ev.response Response data as received from the remote source + * @param {Object} ev.parsed Data as returned from the parse method. + */ + this.publish(EVT_LOADED, { + defaultFn:this._defDataLoaded + }); + /** + * Fired when the data has been saved to the remote source + * The event cannot be prevented. + * @event saved + */ + this.publish(EVT_SAVED, { + preventable: false + }); + cfg = cfg || {}; + if (Lang.isObject(cfg.values)) { + this.setValues(cfg.values, 'init'); + this._set(IS_MODIFIED, false); + this._set(IS_NEW, true); + this._loadedValues = Y.clone(this._values); + } + }, + /** + * Destroys this model instance and removes it from its containing lists, if + * any. + + * If there are any arguments then this method also delegates to the + * sync() method to delete the model from the persistence layer, which is an + * asynchronous action and thus requires at least a callback + * otherwise, it returns immediately. + + * @method destroy + * @param [options] {Object} Options passed on to the sync method, if required. + * @param [callback] {function} function to be called when the sync operation finishes. + * @param callback.err {string|null} Error message, if any or null. + * @param callback.response {Any} The server response as received by sync(), + * @chainable + */ + destroy: function (options, callback) { + if (typeof options === 'function') { + callback = options; + options = {}; + } else if (!options) { + options = {}; + } + var self = this, + finish = function (err) { + if (!err) { + YArray.each(self.lists.concat(), function (list) { + list.remove(self, options); + }); + + Y.GalleryModel.superclass.destroy.call(self); + } + + callback && callback.apply(null, arguments); + }; + + if (callback || options) { + this.sync('delete', options, finish); + } else { + finish(); + } + + return this; + }, + /** + * Returns the value of the field named + * @method getValue + * @param name {string} Name of the field to return. + * @return {Any} the value of the field requested. + */ + getValue: function (name) { + return this._values[name]; + }, + /** + * Returns a hash with all values using the field names as keys. + * @method getValues + * @return {Object} a hash with all the fields with the field names as keys. + */ + getValues: function() { + return Y.clone(this._values); + }, + /** + * Sets the value of the named field. + * Fires the change event if the value is different from the current one. + * Primary key fields cannot be changed unless still undefined. + * @method setValue + * @param name {string} Name of the field to be set + * @param value {Any} Value to be assigned to the field + * @param [src] {Any} Source of the change in the value. + */ + setValue: function (name, value, src) { + var prevVal = this._values[name]; + if (prevVal !== value && (this._primaryKeys.indexOf(name) === -1 || Lang.isUndefined(prevVal))) { + this.fire(EVT_CHANGE, { + name:name, + newVal:value, + prevVal:prevVal, + src: src + }); + } + }, + /** + * Default function for the change event, sets the value and marks the model as modified. + * @method _defSetValue + * @param ev {EventFacade} (see change event) + * @private + */ + _defSetValue: function (ev) { + var self = this; + if (ev.name) { + self._values[ev.name] = ev.newVal; + self._set(IS_MODIFIED, true); + } else { + YObject.each(ev.newVals, function (value, name) { + self.setValue(name, value, ev.src); + }); + } + }, + /** + * Sets a series of values. It simply loops over the hash of values provided calling setValue on each. + * @method setValues + * @param values {Object} hash of values to change + * @param [src] {Any} Source of the changes + */ + setValues: function (values, src) { + var self = this, + prevVals = {}; + + YObject.each(values, function (value, name) { + prevVals[name] = self.getValue(name); + }); + this.fire(EVT_CHANGE, { + newVals:values, + prevVals:prevVals, + src: src + }); + }, + /** + * Returns a hash indexed by field name, of all the values in the model that have changed since the last time + * they were synchornized with the remote source. Each entry has a prevVal and newVal entry. + * @method getChangedValues + * @return {Object} Has of all entries changed since last synched, each entry has a newVal and prevVal property contaning original and changed values. + */ + getChangedValues: function() { + var changed = {}, + prev, + loaded = this._loadedValues; + + YObject.each(this._values, function (value, name) { + prev = loaded[name]; + if (prev !== value) { + changed[name] = {prevVal:prev, newVal: value}; + } + }); + return changed; + }, + /** + * Returns a hash with the values of the primary key fields, indexed by their field names + * @method getPKValues + * @return {Object} Hash with the primary key values, indexed by their field names + */ + getPKValues: function () { + var pkValues = {}, + self = this; + YArray.each(self.get('primaryKeys'), function (name) { + pkValues[name] = self._values[name]; + }); + return pkValues; + }, + /** + Returns an HTML-escaped version of the value of the specified string + attribute. The value is escaped using Y.Escape.html(). + + @method getAsHTML + @param {String} name Attribute name or object property path. + @return {String} HTML-escaped attribute value. + **/ + getAsHTML: function (name) { + var value = this.getValue(name); + return Y.Escape.html(Lang.isValue(value) ? String(value) : ''); + }, + + /** + * Returns a URL-encoded version of the value of the specified field, + * or a full URL with name=value sets for all fields if no name is given. + * The names and values are encoded using the native encodeURIComponent() + * function. + + * @method getAsURL + * @param [name] {String} Field name. + * @return {String} URL-encoded field value if name is given or URL encoded set of name=value pairs for all fields. + */ + getAsURL: function (name) { + var value = this.getValue(name), + url = []; + if (name) { + return encodeURIComponent(Lang.isValue(value) ? String(value) : ''); + } else { + YObject.each(value, function (value, name) { + if (Lang.isValue(value)) { + url.push(encodeURIComponent(name) + '=' + encodeURIComponent(value)); + } + }); + return url.join('&'); + } + }, + + /** + * Default function for the loaded event. Does the actual setting of the values just loaded. + * @method _defDataLoaded + * @param ev {EventFacade} see loaded event + * @private + */ + _defDataLoaded: function (ev) { + var self = this; + self.setValues(ev.parsed, ev.src); + self._set(IS_MODIFIED, false); + self._set(IS_NEW, false); + self._loadedValues = Y.clone(self._values); + }, + /** + Loads this model from the server. + + This method delegates to the sync() method to perform the actual load + operation, which is an asynchronous action. Specify a _callback_ function to + be notified of success or failure. + + A successful load operation will fire a loaded event, while an unsuccessful + load operation will fire an error event with the src value "load". + + @method load + @param options {Object} Options to be passed to sync(). + Usually these will be or will include the keys used by the remote source + to locate the data to be loaded. + They will be passed on unmodified to the sync method. + It is up to sync to determine what they mean. + @param [callback] {callback} Called when the sync operation finishes. Callback will receive: + @param callback.err {string|null} Error message, if any or null. + @param callback.response {Any} The server response as received by sync(), + @chainable + **/ + load: function (options, callback) { + var self = this; + + if (typeof options === 'function') { + callback = options; + options = {}; + } else if (!options) { + options = {}; + } + + self.sync('read', options, function (err, response) { + var facade = { + options : options, + response: response, + src: 'load' + }; + + if (err) { + facade.error = err; + + self.fire(EVT_ERROR, facade); + } else { + self._values = {}; + + facade.parsed = self.parse(response); + self.fire(EVT_LOADED, facade); + } + + callback && callback.apply(null, arguments); + }); + + return self; + }, + + /** + Called to parse the _response_ when a response is received from the server. + This method receives a server _response_ and is expected to return a + value hash. + + The default implementation assumes that _response_ is either an attribute + hash or a JSON string that can be parsed into an attribute hash. If + _response_ is a JSON string and either Y.JSON or the native JSON object + are available, it will be parsed automatically. If a parse error occurs, an + error event will be fired and the model will not be updated. + + You may override this method to implement custom parsing logic if necessary. + + @method parse + @param {Any} response Server response. + @return {Object} Values hash. + **/ + parse: function (response) { + if (typeof response === 'string') { + try { + return Y.JSON.parse(response); + } catch (ex) { + this.fire(EVT_ERROR, { + error : ex, + response: response, + src : 'parse' + }); + + return null; + } + } + + return response; + }, + + + + /** + Saves this model to the server. + + This method delegates to the sync() method to perform the actual save + operation, which is an asynchronous action. Specify a _callback_ function to + be notified of success or failure. + + A successful save operation will fire a saved event, while an unsuccessful + load operation will fire an error event with the src value "save". + + If the save operation succeeds and the parse method returns non-empty values + within the response a loaded event will also be fired to read those values. + + @method save + @param {Object} [options] Options to be passed to sync() and to set() + when setting synced attributes. It's up to the custom sync implementation + to determine what options it supports or requires, if any. + @param {Function} [callback] Called when the sync operation finishes. + @param callback.err {string|null} Error message, if any or null. + @param callback.response {Any} The server response as received by sync(), + @chainable + **/ + save: function (options, callback) { + var self = this; + + if (typeof options === 'function') { + callback = options; + options = {}; + } else if (!options) { + options = {}; + } + + self._validate(self.getValues(), function (err) { + if (err) { + callback && callback.call(null, err); + return; + } + + self.sync(self.get(IS_NEW) ? 'create' : 'update', options, function (err, response) { + var facade = { + options : options, + response: response, + src: 'save' + }; + + if (err) { + facade.error = err; + + self.fire(EVT_ERROR, facade); + } else { + facade.parsed = self.parse(response); + + self._set(IS_MODIFIED, false); + self._set(IS_NEW, false); + self._loadedValues = Y.clone(self._values); + self.fire(EVT_SAVED, facade); + if (facade.parsed) { + self.fire(EVT_LOADED, facade); + } + } + + callback && callback.apply(null, arguments); + }); + }); + + return self; + }, + /** + * Restores the values when last loaded, saved or created. + * @method reset + */ + reset: function() { + this._values = Y.clone(this._loadedValues); + this.fire(EVT_RESET); + return this; + }, + /** + Override this method to provide a custom persistence implementation for this + model. The default just calls the callback without actually doing anything. + + This method is called internally by load(), save(), and destroy(). + + @method sync + @param {String} action Sync action to perform. May be one of the following: + + * create: Store a newly-created model for the first time. + * read : Load an existing model. + * update: Update an existing model. + * delete: Delete an existing model. + + @param {Object} [options] Sync options. It's up to the custom sync + implementation to determine what options it supports or requires, if any. + @param {Function} [callback] Called when the sync operation finishes. + @param {Error|null} callback.err If an error occurred, this parameter will + contain the error. If the sync operation succeeded, _err_ will be + falsy. + @param {Any} [callback.response] The server's response. This value will + be passed to the parse() method, which is expected to parse it and + return an attribute hash. + **/ + sync: function (action, options, callback) { + + if (typeof callback === 'function') { + callback(); + } + }, + /** + Override this method to provide custom validation logic for this model. + + While attribute-specific validators can be used to validate individual + attributes, this method gives you a hook to validate a hash of all + attributes before the model is saved. This method is called automatically + before save() takes any action. If validation fails, the save() call + will be aborted. + + In your validation method, call the provided callback function with no + arguments to indicate success. To indicate failure, pass a single argument, + which may contain an error message, an array of error messages, or any other + value. This value will be passed along to the error event. + + @example + + model.validate = function (attrs, callback) { + if (attrs.pie !== true) { + // No pie?! Invalid! + callback('Must provide pie.'); + return; + } + + // Success! + callback(); + }; + + @method validate + @param {Object} attrs Attribute hash containing all model attributes to + be validated. + @param {Function} callback Validation callback. Call this function when your + validation logic finishes. To trigger a validation failure, pass any + value as the first argument to the callback (ideally a meaningful + validation error of some kind). + + @param {Any} [callback.err] Validation error. Don't provide this + argument if validation succeeds. If validation fails, set this to an + error message or some other meaningful value. It will be passed + along to the resulting error event. + **/ + validate: function (attrs, callback) { + callback && callback(); + }, + /** + Calls the public, overridable validate() method and fires an error event + if validation fails. + + @method _validate + @param {Object} attributes Attribute hash. + @param {Function} callback Validation callback. + @param {Any} [callback.err] Value on failure, non-value on success. + @protected + **/ + _validate: function (attributes, callback) { + var self = this; + + self.validate(attributes, function (err) { + if (Lang.isValue(err)) { + // Validation failed. Fire an error. + self.fire(EVT_ERROR, { + attributes: attributes, + error : err, + src : 'validate' + }); + + callback(err); + return; + } + + callback(); + }); + + }, + /** + Returns a copy of this model's attributes that can be passed to + Y.JSON.stringify() or used for other nefarious purposes. + + The clientId attribute is not included in the returned object. + + If you've specified a custom attribute name in the idAttribute property, + the default id attribute will not be included in the returned object. + + @method toJSON + @return {Object} Copy of this model's attributes. + **/ + toJSON: function () { + return this.getValue(); + }, + _isModifiedGetter: function (value, name) { + name = name.split(DOT); + if (name.length > 1) { + name = name[1]; + var ret = {}; + ret[name] = this._values[name] !== this._loadedValues[name]; + return ret; + } else { + return value; + } + + }, + _isNewGetter: function (value, name) { + name = name.split(DOT); + if (name.length > 1) { + name = name[1]; + var ret = {}; + ret[name] = !this._loadedValues.hasOwnProperty(name); + return ret; + } else { + return value; + } + }, + _primaryKeysSetter: function (value) { + if (this._primaryKeys && this._primaryKeys.length) { + return Y.Attribute.INVALID_VALUE; + } + value = new YArray(value); + this._primaryKeys = value; + return value; + }, + _primaryKeysGetter: function (value, name) { + name = name.split(DOT); + if (name.length > 1) { + name = name[1]; + var ret = {}; + ret[name] = value.indexOf(name) !== -1; + return ret; + } else { + return (value || []).concat(); // makes sure to return a copy, not the original. + } + } + }, + { + ATTRS: { + /** + * Indicates whether any of the fields has been changed since created or loaded. + * Field names can be given as sub-attributes to indicate if any particular field has beeen changed. + * model.get('isModified.name') returns true if the field _name_ has been modified. + * Note: contrary to common practice in Attributes with sub-attributes, + * requesting the state of the record does not + * return an object with the state of each individual field keyed by field name, + * but the state of the record as a whole, which is far more useful. + * @attribute isModified + * @type Boolean + * @readonly + * @default false + */ + isModified: { + readOnly: true, + value:false, + validator:Lang.isBoolean, + getter: '_isModifiedGetter' + }, + /** + * Indicates that the model is new and has not been modified since creation. + * Field names can be given as sub-attributes to indicate if any particular field is new. + * model.get('isNew.name') returns true if the field _name_ is new. + * Note: contrary to common practice in Attributes with sub-attributes, + * requesting the state of the record does not + * return an object with the state of each individual field keyed by field name, + * but the state of the record as a whole, which is far more useful. + * @attribute isNew + * @type Boolean + * @readonly + * @default true + */ + isNew: { + readOnly: true, + value:true, + validator:Lang.isBoolean, + getter: '_isNewGetter' + }, + /** + * List of fields making the primary key of this model. + * Primary Key fields cannot be modified once initially loaded. + * It can be set as an array of field names or, if the key is made of a single field, a string with the name of that field. + * It will always be returned as an array. + * Field names can be given as a sub-attribute to ask whether a particular field is a primary key, thus: + * model.get('primaryKeys.name') returns true of the field name is a primary key. + * It can only be set once. + * @attribute primaryKeys + * @writeonce + * @type array + * @default [] + */ + primaryKeys: { + setter:'_primaryKeysSetter', + getter:'_primaryKeysGetter', + lazyAdd: false, + value: [] + } + } + + } + ); + + /** + * An extension for Y.GalleryModel that provides a single level of undo for each field. + * @class Y.GalleryModelSimpleUndo + */ + Y.GalleryModelSimpleUndo = function () {}; + + Y.GalleryModelSimpleUndo.prototype = { + initializer: function () { + this._lastChange = {}; + this._preserve = (this._preserve || []).concat('_lastChange'); + this.after(EVT_CHANGE, this._trackChange); + this.after([EVT_LOADED,EVT_SAVED,EVT_RESET], this._resetUndo); + }, + /** + * Event listener for the after value change event, it tracks changes for each field. + * It retains only the last change for each field. + * @method _trackChange + * @param ev {EventFacade} As provided by the change event + * @private + */ + _trackChange: function (ev) { + if (ev.name && ev.src !== 'undo') { + this._lastChange[ev.name] = ev.prevVal; + } + }, + /** + * After load or save operations, it drops any changes it might have tracked. + * @method _resetUndo + * @private + */ + _resetUndo: function () { + this._lastChange = {}; + }, + /** + * Reverts one level of change for a specific field or all fields + * @method undo + * @param [name] {String} If provided it will undo that particular field, + * otherwise, it undoes the whole record. + */ + undo: function (name) { + var self = this; + if (name) { + if (self._lastChange[name] !== undefined) { + self.setValue(name, self._lastChange[name], 'undo'); + delete self._lastChange[name]; + } + } else { + self.setValues(this._lastChange, 'undo'); + self._lastChange = {}; + } + } + }; + + /** + * Provides multiple levels of undo in strict chronological order + * whatever the field was at each stage. + * Changes done on multiple fields via setValues + * will also be undone in one step. + * @class Y.GalleryModelChronologicalUndo + */ + Y.GalleryModelChronologicalUndo = function () {}; + + Y.GalleryModelChronologicalUndo.prototype = { + initializer: function () { + this._changes = []; + this._preserve = (this._preserve || []).concat('_changes'); + this.after(EVT_CHANGE, this._trackChange); + this.after([EVT_LOADED,EVT_SAVED,EVT_RESET], this._resetUndo); + }, + /** + * Event listener for the after value change event, it tracks changes for each field. + * It keeps a stack of each change. + * @method _trackChange + * @param ev {EventFacade} As provided by the change event + * @private + */ + _trackChange: function (ev) { + if (ev.src !== 'undo') { + this._changes.push(ev.details); + } + }, + /** + * After load or save operations, it drops any changes it might have tracked. + * @method _resetUndo + * @private + */ + _resetUndo: function () { + this._changes = []; + }, + /** + * Reverts one level of field changes. + * @method undo + */ + undo: function () { + var ev = this._changes.pop(); + if (ev) { + if (ev.name) { + this.setValue(ev.name, ev.prevVal, 'undo'); + } else { + this.setValues(ev.prevVals, 'undo'); + } + } + if (this._changes.length === 0) { + this._set(IS_MODIFIED, false); + } + } + }; + + /** + * Allows GalleryModel to handle a set of records using the Flyweight pattern. + * It exposes one record at a time from a shelf of records. + * Exposed records can be selected by setting the _index_ attribute. + * @class Y.GalleryModelMultiRecord + */ + + var INDEX = 'index', + MR = function () {}; + + MR.prototype = { + initializer: function () { + this._shelves = []; + this._currentIndex = 0; + this.on(EVT_LOADED, this._batchLoad); + }, + /** + * Index of the shelf for the record being exposed. + * Use index attribute to check/set the index value. + * @property _currentIndex + * @type integer + * @default 0 + * @private + */ + _currentIndex: 0, + /** + * Storage for the records when not exposed. + * @property _shelves + * @type Array + * @private + */ + _shelves: null, + /** + * Saves the exposed record into the shelves at the position given by _currentIndex + * @method _shelve + * @private + */ + _shelve: function(index) { + if (index === undefined) { + index = this._currentIndex; + } + var self = this, + current = { + _values: self._values, + _loadedValues: self._loadedValues, + isNew: self.get(IS_NEW), + isModified: self.get(IS_MODIFIED) + }; + YArray.each(self._preserve, function (name) { + current[name] = self[name]; + }); + self._shelves[index] = current; + + }, + /** + * Retrives and exposes the record from the shelf at the position given by _currentIndex + * @method _fetch + * @private + */ + _fetch: function (index) { + if (index === undefined) { + index = this._currentIndex; + } + var self = this, + current = self._shelves[index]; + + self._values = current._values; + self._loadedValues = current._loadedValues; + self.__setStateVal(IS_NEW, current.isNew); + self.__setStateVal(IS_MODIFIED, current.isModified); + YArray.each(self._preserve, function (name) { + self[name] = current[name]; + }); + + }, + /** + * Initializes an exposed record + * @method _initNew + * @private + */ + _initNew: function () { + this._values = {}; + this._loadedValues = {}; + this._set(IS_NEW, true); + this._set(IS_MODIFIED, false); + }, + /** + * Adds a new record at the index position given or at the end. + * The new record becomes the current. + * @method add + * @param values {Object} set of values to set + * @param [index] {Integer} position to add the values at or at the end if not provided. + */ + add: function(values, index) { + if (this.get(IS_MODIFIED) || !this.get(IS_NEW)) { + this._shelve(); + } + if (arguments.length === 2) { + this._shelves.splice(index, 1); + } else { + index = this._shelves.length; + } + this._currentIndex = index; + this._initNew(); + this.setValues(values, 'add'); + }, + /** + * Executes the given function for each record in the set. + * Returning exactly false from the function spares shelving the record. + * If function does not modify the record, returning false will improve performance. + * @method each + * @param fn {function} function to execute, it will be provided with: + * @param fn.index {integer} index of the record exposed + */ + each: function(fn) { + var i, l, self = this; + this._shelve(); + for (i = 0, l = this._shelves.length; i < l; i += 1) { + this._currentIndex = i; + this._fetch(i); + if (fn.call(self, i) !== false) { + this._shelve(i); + } + } + }, + /** + * Executes the given function for each record in the set. + * It is faster than using each and then checking for isModified + * Returning exactly false from the function spares shelving the record. + * If function does not modify the record, returning false will improve performance. + * @method eachModified + * @param fn {function} function to execute, it will be provided with: + * @param fn.index {integer} index of the record exposed + */ + eachModified:function(fn) { + var i, l, self = this; + this._shelve(); + for (i = 0, l = this._shelves.length; i < l; i += 1) { + if (this._shelves[i][IS_MODIFIED]) { + this._currentIndex = i; + this._fetch(i); + if (fn.call(self, i) !== false) { + this._shelve(i); + } + } + } + }, + /** + * Calls _save_ on each record modified. + * This is not the best saving strategy for saving batches of records, + * but it is the easiest and safest. Implementors are encouraged to + * design their own. + * @method saveAllModified + */ + saveAllModified: function () { + this.eachModified(this.save); + }, + /** + * This is a documentation entry only, this method does not define load. + * + * This extension captures the _loaded_ event so that if a load + * returns an array of records, they will be added to the shelves. + * Existing records are kept, call _empty_ if they should be discarded. + * See method load of Y.GalleryModel for further info. + * @method load + */ + /** + * Listener for the loaded event, checks if the parsed response is an array + * and saves it into the shelves. + * @method _batchLoad + * @param ev {EventFacade} facade produced by load. + * @private + */ + _batchLoad: function (ev) { + var self = this; + if (ev.src === 'load' && ev.Lang.isArray(ev.parsed)) { + ev.halt(); + YArray.each(ev.parsed, function (values) { + self.add(values); + }); + } + }, + /** + * Returns the number of records stored + * @method size + * @return {Integer} number of records in the shelves + */ + size: function() { + return this._shelves.length; + }, + /** + * Empties the shelves of any records as well as the exposed record + * @method empty + */ + empty: function () { + this._shelves = []; + this._currentIndex = 0; + this.reset(); + }, + /** + * Setter for the _index_ attribute. + * Validates and copies the current index value into _currentIndex. + * @method _indexSetter + * @param value {integer} new value for the index + * @return {integer|INVALID_VALUE} new value for the index or INVALID_VALUE if invalid. + * @private + */ + _indexSetter: function (value) { + if (Lang.isNumber(value) && value >= 0 && value < this._shelves.length) { + this._shelve(this._currentIndex); + this._currentIndex = value = parseInt(value,10); + this._fetch(value); + return value; + } else { + return Y.Attribute.INVALID_VALUE; + } + }, + /** + * Getter for the _index_ attribute + * Returns the value from _currentIndex + * @method _indexGetter + * @return {integer} value of the index + * @private + */ + _indexGetter: function (value) { + return this._currentIndex; + } + + }; + + MR.ATTRS = { + /** + * Index of the record exposed. + * @attribute index + * @type Integer + * @default 0 + */ + index: { + value: 0, + setter:'_indexSetter', + getter:'_indexGetter' + } + }; + + Y.GalleryModelMultiRecord = MR; + + /** + * Extension to sort records stored in GalleryModel, extended with GalleryModelMultiRecord + * @class Y.GalleryModelSortedMultiRecord + */ + var SFIELD = 'sortField', + SDIR = 'sortDir', + ASC = 'asc', + DESC = 'desc', + SMR = function () {}; + + SMR.prototype = { + /** + * Compare function used in sorting. + * @method _compare + * @param a {object} shelf to compare + * @param b {object} shelf to compare + * @return {integer} -1, 0 or 1 as required by Array.sort + * @private + */ + _compare: null, + /** + * Initializer lifecycle method. + * Ensures proper defaults, sets the compare method and + * sets listeners for relevant events + * @method initializer + * @protected + */ + initializer: function () { + if (this.get(SFIELD) === undefined) { + this._set(SFIELD, this.get('primaryKeys')[0]); + } + this._setCompare(); + this.after([SFIELD + CHANGE, SDIR + CHANGE], this._sort); + this.after('change', this._afterChange); + }, + /** + * Sets the compare function to be used in sorting the records + * based on the sortField and sortDir and stores it into this._compare + * @method _setCompare + * @private + */ + _setCompare: function () { + var sortField = this.get(SFIELD), + sortAsc = this.get(SDIR) === ASC?1:-1, + compareValue = (Lang.isFunction(sortField)? + sortField: + function(values) { + return values[sortField]; + } + ); + this._compare = function(a, b) { + var aValue = compareValue(a._values), + bValue = compareValue(b._values); + + return (aValue < bValue ? -1 : (aValue > bValue ? 1 : 0)) * sortAsc; + }; + }, + /** + * Sorts the shelves whenever the sortField or sortDir is changes + * @method _sort + * @private + */ + _sort: function() { + this._setCompare(); + this._shelve(); + this._shelves.sort(this._compare); + this._fetch(); + }, + /** + * Listens to value changes and if the name of the field is that of the sortField + * or if sortField is a function, it will relocate the record to its proper sort order + * @method _afterChange + * @param ev {EventFacade} Event façade as produced by the change event + * @private + */ + _afterChange: function (ev) { + var fieldName = ev.name, + sField = this.get(SFIELD), + index, + currentIndex = this._currentIndex, + shelves = this._shelves, + currentShelf; + + if (fieldName && ev.src !== 'add' && (Lang.isFunction(sField) || fieldName === sField)) { + // The shelf has to be emptied otherwise _findIndex may match itself. + currentShelf = shelves.splice(currentIndex,1)[0]; + index = this._findIndex(currentShelf._values); + shelves.splice(index,0,currentShelf); + this._currentIndex = index; + } + }, + /** + * Finds the correct index position of a record within the shelves + * according to the current sortField and sortDir + * @method _findIndex + * @param values {Object} values of the record to be located + * @return {Integer} location for the record + * @private + */ + _findIndex: function (values) { + var shelves = this._shelves, + low = 0, + high = shelves.length, + index = 0, + cmp = this._compare, + vals = {_values: values}; + + while (low < high) { + index = (high + low) >> 1; + switch(cmp(vals, shelves[index])) { + case 1: + low = index + 1; + break; + case -1: + high = index; + break; + default: + low = high = index; + } + + } + return low; + + }, + /** + * Adds a new record at its proper position according to the sort configuration. + * It overrides GalleryModelMultiRecord's own add method, ignoring the index position requested, if any. + * The new record becomes the current. + * @method add + * @param values {Object} set of values to set + */ + add: function(values) { + var shelves = this._shelves, + index = 0; + + index = this._findIndex(values); + this._currentIndex = index; + shelves.splice(index, 0, {}); + this._initNew(); + this.setValues(values, 'add'); + this._shelve(index); + }, + /** + * Locates a record by value. The record will be located by the field + * given in the sortField attribute. It will return the index of the + * record in the shelves or null if not found. + * By default it will expose that record. + * If sortField contains a function, it will return null and do nothing. + * Since sort fields need not be unique, find may return any of the records + * with the same value for that field. + * @method find + * @param value {Any} value to be found + * @param [move] {Boolean} exposes the record found, defaults to true + * @return {integer | null} index of the record found or null if not found. + * Be sure to differentiate a return of 0, a valid index, from null, a failed search. + */ + find: function (value, move) { + var sfield = this.get(SFIELD), + index, + values = {}; + if (Lang.isFunction(sfield)) { + return null; + } + values[sfield] = value; + index = this._findIndex(values); + if (this._shelves[index]._values[sfield] !== value) { + return null; + } + if (move || arguments.length < 2) { + this.set(INDEX, index); + } + return index; + } + }; + SMR.ATTRS = { + /** + * Name of the field to sort by or function to build the value used for comparisson. + * If a function, it will receive a reference to the record to be sorted; + * it should return the value to be used for comparisson. Functions are + * used when sorting on multiple keys, which the function should return + * concatenated, or when any of the fields needs some pre-processing. + * @attribute sortField + * @type String | Function + * @default first primary key field + */ + sortField: { + validator: function (value){ + return Lang.isString(value) || Lang.isFunction(value); + } + }, + /** + * Sort direction either "asc" for ascending or "desc" for descending + * @attribute sortDir + * @type String + * @default "asc" + */ + sortDir: { + validator: function (value) { + return value === DESC || value === ASC; + }, + value: ASC + } + }; + Y.GalleryModelSortedMultiRecord = SMR; + + + + +}, 'gallery-2012.03.23-18-00' ,{requires:['base'], skinnable:false}); diff --git a/build/gallery-md-model/gallery-md-model-min.js b/build/gallery-md-model/gallery-md-model-min.js new file mode 100644 index 0000000000..ac7108c7ba --- /dev/null +++ b/build/gallery-md-model/gallery-md-model-min.js @@ -0,0 +1,2 @@ +YUI.add("gallery-md-model",function(c){var f=c.Lang,j=c.Array,e=c.Object,k="change",n="loaded",b="error",h="saved",g="reset",r="isModified",t="isNew",p=".",m="Change";c.GalleryModel=c.Base.create("gallery-md-model",c.Base,[],{_values:null,_loadedValues:null,_primaryKeys:null,initializer:function(u){this._values={};this._loadedValues={};this.publish(k,{defaultFn:this._defSetValue});this.publish(n,{defaultFn:this._defDataLoaded});this.publish(h,{preventable:false});u=u||{};if(f.isObject(u.values)){this.setValues(u.values,"init");this._set(r,false);this._set(t,true);this._loadedValues=c.clone(this._values);}},destroy:function(v,x){if(typeof v==="function"){x=v;v={};}else{if(!v){v={};}}var u=this,w=function(y){if(!y){j.each(u.lists.concat(),function(z){z.remove(u,v);});c.GalleryModel.superclass.destroy.call(u);}x&&x.apply(null,arguments);};if(x||v){this.sync("delete",v,w);}else{w();}return this;},getValue:function(u){return this._values[u];},getValues:function(){return c.clone(this._values);},setValue:function(u,v,w){var x=this._values[u];if(x!==v&&(this._primaryKeys.indexOf(u)===-1||f.isUndefined(x))){this.fire(k,{name:u,newVal:v,prevVal:x,src:w});}},_defSetValue:function(v){var u=this;if(v.name){u._values[v.name]=v.newVal;u._set(r,true);}else{e.each(v.newVals,function(x,w){u.setValue(w,x,v.src);});}},setValues:function(w,x){var v=this,u={};e.each(w,function(z,y){u[y]=v.getValue(y);});this.fire(k,{newVals:w,prevVals:u,src:x});},getChangedValues:function(){var w={},v,u=this._loadedValues;e.each(this._values,function(y,x){v=u[x];if(v!==y){w[x]={prevVal:v,newVal:y};}});return w;},getPKValues:function(){var v={},u=this;j.each(u.get("primaryKeys"),function(w){v[w]=u._values[w];});return v;},getAsHTML:function(u){var v=this.getValue(u);return c.Escape.html(f.isValue(v)?String(v):"");},getAsURL:function(v){var w=this.getValue(v),u=[];if(v){return encodeURIComponent(f.isValue(w)?String(w):"");}else{e.each(w,function(y,x){if(f.isValue(y)){u.push(encodeURIComponent(x)+"="+encodeURIComponent(y));}});return u.join("&");}},_defDataLoaded:function(v){var u=this;u.setValues(v.parsed,v.src);u._set(r,false);u._set(t,false);u._loadedValues=c.clone(u._values);},load:function(v,w){var u=this;if(typeof v==="function"){w=v;v={};}else{if(!v){v={};}}u.sync("read",v,function(z,x){var y={options:v,response:x,src:"load"};if(z){y.error=z;u.fire(b,y);}else{u._values={};y.parsed=u.parse(x);u.fire(n,y);}w&&w.apply(null,arguments);});return u;},parse:function(u){if(typeof u==="string"){try{return c.JSON.parse(u);}catch(v){this.fire(b,{error:v,response:u,src:"parse"});return null;}}return u;},save:function(v,w){var u=this;if(typeof v==="function"){w=v;v={};}else{if(!v){v={};}}u._validate(u.getValues(),function(x){if(x){w&&w.call(null,x);return;}u.sync(u.get(t)?"create":"update",v,function(A,y){var z={options:v,response:y,src:"save"};if(A){z.error=A;u.fire(b,z);}else{z.parsed=u.parse(y);u._set(r,false);u._set(t,false);u._loadedValues=c.clone(u._values);u.fire(h,z);if(z.parsed){u.fire(n,z);}}w&&w.apply(null,arguments);});});return u;},reset:function(){this._values=c.clone(this._loadedValues);this.fire(g);return this;},sync:function(v,u,w){if(typeof w==="function"){w();}},validate:function(u,v){v&&v();},_validate:function(v,w){var u=this;u.validate(v,function(x){if(f.isValue(x)){u.fire(b,{attributes:v,error:x,src:"validate"});w(x);return;}w();});},toJSON:function(){return this.getValue();},_isModifiedGetter:function(w,v){v=v.split(p);if(v.length>1){v=v[1];var u={};u[v]=this._values[v]!==this._loadedValues[v];return u;}else{return w;}},_isNewGetter:function(w,v){v=v.split(p);if(v.length>1){v=v[1];var u={};u[v]=!this._loadedValues.hasOwnProperty(v);return u;}else{return w;}},_primaryKeysSetter:function(u){if(this._primaryKeys&&this._primaryKeys.length){return c.Attribute.INVALID_VALUE;}u=new j(u);this._primaryKeys=u;return u;},_primaryKeysGetter:function(w,v){v=v.split(p);if(v.length>1){v=v[1];var u={};u[v]=w.indexOf(v)!==-1;return u;}else{return(w||[]).concat();}}},{ATTRS:{isModified:{readOnly:true,value:false,validator:f.isBoolean,getter:"_isModifiedGetter"},isNew:{readOnly:true,value:true,validator:f.isBoolean,getter:"_isNewGetter"},primaryKeys:{setter:"_primaryKeysSetter",getter:"_primaryKeysGetter",lazyAdd:false,value:[]}}});c.GalleryModelSimpleUndo=function(){};c.GalleryModelSimpleUndo.prototype={initializer:function(){this._lastChange={};this._preserve=(this._preserve||[]).concat("_lastChange");this.after(k,this._trackChange);this.after([n,h,g],this._resetUndo);},_trackChange:function(u){if(u.name&&u.src!=="undo"){this._lastChange[u.name]=u.prevVal;}},_resetUndo:function(){this._lastChange={};},undo:function(v){var u=this;if(v){if(u._lastChange[v]!==undefined){u.setValue(v,u._lastChange[v],"undo");delete u._lastChange[v];}}else{u.setValues(this._lastChange,"undo");u._lastChange={};}}};c.GalleryModelChronologicalUndo=function(){};c.GalleryModelChronologicalUndo.prototype={initializer:function(){this._changes=[];this._preserve=(this._preserve||[]).concat("_changes");this.after(k,this._trackChange);this.after([n,h,g],this._resetUndo);},_trackChange:function(u){if(u.src!=="undo"){this._changes.push(u.details);}},_resetUndo:function(){this._changes=[];},undo:function(){var u=this._changes.pop();if(u){if(u.name){this.setValue(u.name,u.prevVal,"undo");}else{this.setValues(u.prevVals,"undo");}}if(this._changes.length===0){this._set(r,false);}}};var s="index",l=function(){};l.prototype={initializer:function(){this._shelves=[];this._currentIndex=0;this.on(n,this._batchLoad);},_currentIndex:0,_shelves:null,_shelve:function(v){if(v===undefined){v=this._currentIndex;}var u=this,w={_values:u._values,_loadedValues:u._loadedValues,isNew:u.get(t),isModified:u.get(r)};j.each(u._preserve,function(x){w[x]=u[x];});u._shelves[v]=w;},_fetch:function(v){if(v===undefined){v=this._currentIndex;}var u=this,w=u._shelves[v];u._values=w._values;u._loadedValues=w._loadedValues;u.__setStateVal(t,w.isNew);u.__setStateVal(r,w.isModified);j.each(u._preserve,function(x){u[x]=w[x]; +});},_initNew:function(){this._values={};this._loadedValues={};this._set(t,true);this._set(r,false);},add:function(u,v){if(this.get(r)||!this.get(t)){this._shelve();}if(arguments.length===2){this._shelves.splice(v,1);}else{v=this._shelves.length;}this._currentIndex=v;this._initNew();this.setValues(u,"add");},each:function(x){var w,u,v=this;this._shelve();for(w=0,u=this._shelves.length;w=0&&uz?1:0))*w;};},_sort:function(){this._setCompare();this._shelve();this._shelves.sort(this._compare);this._fetch();},_afterChange:function(x){var A=x.name,u=this.get(o),w,v=this._currentIndex,z=this._shelves,y;if(A&&x.src!=="add"&&(f.isFunction(u)||A===u)){y=z.splice(v,1)[0];w=this._findIndex(y._values);z.splice(w,0,y);this._currentIndex=w;}},_findIndex:function(v){var A=this._shelves,u=0,z=A.length,w=0,x=this._compare,y={_values:v};while(u>1;switch(x(y,A[w])){case 1:u=w+1;break;case -1:z=w;break;default:u=z=w;}}return u;},add:function(u){var w=this._shelves,v=0;v=this._findIndex(u);this._currentIndex=v;w.splice(v,0,{});this._initNew();this.setValues(u,"add");this._shelve(v);},find:function(y,u){var x=this.get(o),w,v={};if(f.isFunction(x)){return null;}v[x]=y;w=this._findIndex(v);if(this._shelves[w]._values[x]!==y){return null;}if(u||arguments.length<2){this.set(s,w);}return w;}};i.ATTRS={sortField:{validator:function(u){return f.isString(u)||f.isFunction(u);}},sortDir:{validator:function(u){return u===d||u===q;},value:q}};c.GalleryModelSortedMultiRecord=i;},"gallery-2012.03.23-18-00",{requires:["base"],skinnable:false}); \ No newline at end of file diff --git a/build/gallery-md-model/gallery-md-model.js b/build/gallery-md-model/gallery-md-model.js new file mode 100644 index 0000000000..016398e86b --- /dev/null +++ b/build/gallery-md-model/gallery-md-model.js @@ -0,0 +1,1268 @@ +YUI.add('gallery-md-model', function(Y) { + +/** +Record-based data model with APIs for getting, setting, validating, and +syncing attribute values, as well as events for being notified of model changes. + +@module gallery-md-model +**/ + +/** +Record-based data model with APIs for getting, setting, validating, and +syncing attribute values, as well as events for being notified of model changes. + +In most cases, you'll want to create your own subclass of Y.GalleryModel and +customize it to meet your needs. In particular, the sync() and validate() +methods are meant to be overridden by custom implementations. You may also want +to override the parse() method to parse non-generic server responses. + +@class Y.GalleryModel +@constructor +@param [cfg] {Object} Initial configuration attribute plus: +@param [cfg.values] {Object} Sets initial values for the model. + Model will be marked as new and not modified (as if just loaded). +@extends Base +**/ + "use strict"; + + var Lang = Y.Lang, + YArray = Y.Array, + YObject = Y.Object, + EVT_CHANGE = 'change', + EVT_LOADED = 'loaded', + EVT_ERROR = 'error', + EVT_SAVED = 'saved', + EVT_RESET = 'reset', + IS_MODIFIED = 'isModified', + IS_NEW = 'isNew', + DOT = '.', + CHANGE = 'Change'; + + + Y.GalleryModel = Y.Base.create( + 'gallery-md-model', + Y.Base, + [], + { + /** + * Hash of values indexed by field name + * @property _values + * @type Object + * @private + */ + _values: null, + /** + * Hash of values as loaded from the remote source, + * presumed to be the current value there. + * @property _loadedValues + * @type Object + * @private + */ + _loadedValues: null, + /** + * Array of field names that make up the primary key for this record + * @property _primaryKeys + * @type Array + * @private + */ + _primaryKeys: null, + /* + * Y.Base lifecycle method + */ + initializer: function (cfg) { + this._values = {}; + this._loadedValues = {}; + /** + * Fired whenever a data value is changed. + * @event change + * @param {String} ev.name Name of the field changed + * @param {Any} ev.newVal New value of the field. + * @param {Any} ev.prevVal Previous value of the field. + * @param {String|null} ev.src Source of the change event, if any. + */ + this.publish(EVT_CHANGE, { + defaultFn: this._defSetValue + }); + /** + * Fired when new data has been received from the remote source. + * It will be fired even on a save operation if the response contains values. + * The parsed values can be altered on the before (on) listener. + * @event loaded + * @param {Object} ev.response Response data as received from the remote source + * @param {Object} ev.parsed Data as returned from the parse method. + */ + this.publish(EVT_LOADED, { + defaultFn:this._defDataLoaded + }); + /** + * Fired when the data has been saved to the remote source + * The event cannot be prevented. + * @event saved + */ + this.publish(EVT_SAVED, { + preventable: false + }); + cfg = cfg || {}; + if (Lang.isObject(cfg.values)) { + this.setValues(cfg.values, 'init'); + this._set(IS_MODIFIED, false); + this._set(IS_NEW, true); + this._loadedValues = Y.clone(this._values); + } + }, + /** + * Destroys this model instance and removes it from its containing lists, if + * any. + + * If there are any arguments then this method also delegates to the + * sync() method to delete the model from the persistence layer, which is an + * asynchronous action and thus requires at least a callback + * otherwise, it returns immediately. + + * @method destroy + * @param [options] {Object} Options passed on to the sync method, if required. + * @param [callback] {function} function to be called when the sync operation finishes. + * @param callback.err {string|null} Error message, if any or null. + * @param callback.response {Any} The server response as received by sync(), + * @chainable + */ + destroy: function (options, callback) { + if (typeof options === 'function') { + callback = options; + options = {}; + } else if (!options) { + options = {}; + } + var self = this, + finish = function (err) { + if (!err) { + YArray.each(self.lists.concat(), function (list) { + list.remove(self, options); + }); + + Y.GalleryModel.superclass.destroy.call(self); + } + + callback && callback.apply(null, arguments); + }; + + if (callback || options) { + this.sync('delete', options, finish); + } else { + finish(); + } + + return this; + }, + /** + * Returns the value of the field named + * @method getValue + * @param name {string} Name of the field to return. + * @return {Any} the value of the field requested. + */ + getValue: function (name) { + return this._values[name]; + }, + /** + * Returns a hash with all values using the field names as keys. + * @method getValues + * @return {Object} a hash with all the fields with the field names as keys. + */ + getValues: function() { + return Y.clone(this._values); + }, + /** + * Sets the value of the named field. + * Fires the change event if the value is different from the current one. + * Primary key fields cannot be changed unless still undefined. + * @method setValue + * @param name {string} Name of the field to be set + * @param value {Any} Value to be assigned to the field + * @param [src] {Any} Source of the change in the value. + */ + setValue: function (name, value, src) { + var prevVal = this._values[name]; + if (prevVal !== value && (this._primaryKeys.indexOf(name) === -1 || Lang.isUndefined(prevVal))) { + this.fire(EVT_CHANGE, { + name:name, + newVal:value, + prevVal:prevVal, + src: src + }); + } + }, + /** + * Default function for the change event, sets the value and marks the model as modified. + * @method _defSetValue + * @param ev {EventFacade} (see change event) + * @private + */ + _defSetValue: function (ev) { + var self = this; + if (ev.name) { + self._values[ev.name] = ev.newVal; + self._set(IS_MODIFIED, true); + } else { + YObject.each(ev.newVals, function (value, name) { + self.setValue(name, value, ev.src); + }); + } + }, + /** + * Sets a series of values. It simply loops over the hash of values provided calling setValue on each. + * @method setValues + * @param values {Object} hash of values to change + * @param [src] {Any} Source of the changes + */ + setValues: function (values, src) { + var self = this, + prevVals = {}; + + YObject.each(values, function (value, name) { + prevVals[name] = self.getValue(name); + }); + this.fire(EVT_CHANGE, { + newVals:values, + prevVals:prevVals, + src: src + }); + }, + /** + * Returns a hash indexed by field name, of all the values in the model that have changed since the last time + * they were synchornized with the remote source. Each entry has a prevVal and newVal entry. + * @method getChangedValues + * @return {Object} Has of all entries changed since last synched, each entry has a newVal and prevVal property contaning original and changed values. + */ + getChangedValues: function() { + var changed = {}, + prev, + loaded = this._loadedValues; + + YObject.each(this._values, function (value, name) { + prev = loaded[name]; + if (prev !== value) { + changed[name] = {prevVal:prev, newVal: value}; + } + }); + return changed; + }, + /** + * Returns a hash with the values of the primary key fields, indexed by their field names + * @method getPKValues + * @return {Object} Hash with the primary key values, indexed by their field names + */ + getPKValues: function () { + var pkValues = {}, + self = this; + YArray.each(self.get('primaryKeys'), function (name) { + pkValues[name] = self._values[name]; + }); + return pkValues; + }, + /** + Returns an HTML-escaped version of the value of the specified string + attribute. The value is escaped using Y.Escape.html(). + + @method getAsHTML + @param {String} name Attribute name or object property path. + @return {String} HTML-escaped attribute value. + **/ + getAsHTML: function (name) { + var value = this.getValue(name); + return Y.Escape.html(Lang.isValue(value) ? String(value) : ''); + }, + + /** + * Returns a URL-encoded version of the value of the specified field, + * or a full URL with name=value sets for all fields if no name is given. + * The names and values are encoded using the native encodeURIComponent() + * function. + + * @method getAsURL + * @param [name] {String} Field name. + * @return {String} URL-encoded field value if name is given or URL encoded set of name=value pairs for all fields. + */ + getAsURL: function (name) { + var value = this.getValue(name), + url = []; + if (name) { + return encodeURIComponent(Lang.isValue(value) ? String(value) : ''); + } else { + YObject.each(value, function (value, name) { + if (Lang.isValue(value)) { + url.push(encodeURIComponent(name) + '=' + encodeURIComponent(value)); + } + }); + return url.join('&'); + } + }, + + /** + * Default function for the loaded event. Does the actual setting of the values just loaded. + * @method _defDataLoaded + * @param ev {EventFacade} see loaded event + * @private + */ + _defDataLoaded: function (ev) { + var self = this; + self.setValues(ev.parsed, ev.src); + self._set(IS_MODIFIED, false); + self._set(IS_NEW, false); + self._loadedValues = Y.clone(self._values); + }, + /** + Loads this model from the server. + + This method delegates to the sync() method to perform the actual load + operation, which is an asynchronous action. Specify a _callback_ function to + be notified of success or failure. + + A successful load operation will fire a loaded event, while an unsuccessful + load operation will fire an error event with the src value "load". + + @method load + @param options {Object} Options to be passed to sync(). + Usually these will be or will include the keys used by the remote source + to locate the data to be loaded. + They will be passed on unmodified to the sync method. + It is up to sync to determine what they mean. + @param [callback] {callback} Called when the sync operation finishes. Callback will receive: + @param callback.err {string|null} Error message, if any or null. + @param callback.response {Any} The server response as received by sync(), + @chainable + **/ + load: function (options, callback) { + var self = this; + + if (typeof options === 'function') { + callback = options; + options = {}; + } else if (!options) { + options = {}; + } + + self.sync('read', options, function (err, response) { + var facade = { + options : options, + response: response, + src: 'load' + }; + + if (err) { + facade.error = err; + + self.fire(EVT_ERROR, facade); + } else { + self._values = {}; + + facade.parsed = self.parse(response); + self.fire(EVT_LOADED, facade); + } + + callback && callback.apply(null, arguments); + }); + + return self; + }, + + /** + Called to parse the _response_ when a response is received from the server. + This method receives a server _response_ and is expected to return a + value hash. + + The default implementation assumes that _response_ is either an attribute + hash or a JSON string that can be parsed into an attribute hash. If + _response_ is a JSON string and either Y.JSON or the native JSON object + are available, it will be parsed automatically. If a parse error occurs, an + error event will be fired and the model will not be updated. + + You may override this method to implement custom parsing logic if necessary. + + @method parse + @param {Any} response Server response. + @return {Object} Values hash. + **/ + parse: function (response) { + if (typeof response === 'string') { + try { + return Y.JSON.parse(response); + } catch (ex) { + this.fire(EVT_ERROR, { + error : ex, + response: response, + src : 'parse' + }); + + return null; + } + } + + return response; + }, + + + + /** + Saves this model to the server. + + This method delegates to the sync() method to perform the actual save + operation, which is an asynchronous action. Specify a _callback_ function to + be notified of success or failure. + + A successful save operation will fire a saved event, while an unsuccessful + load operation will fire an error event with the src value "save". + + If the save operation succeeds and the parse method returns non-empty values + within the response a loaded event will also be fired to read those values. + + @method save + @param {Object} [options] Options to be passed to sync() and to set() + when setting synced attributes. It's up to the custom sync implementation + to determine what options it supports or requires, if any. + @param {Function} [callback] Called when the sync operation finishes. + @param callback.err {string|null} Error message, if any or null. + @param callback.response {Any} The server response as received by sync(), + @chainable + **/ + save: function (options, callback) { + var self = this; + + if (typeof options === 'function') { + callback = options; + options = {}; + } else if (!options) { + options = {}; + } + + self._validate(self.getValues(), function (err) { + if (err) { + callback && callback.call(null, err); + return; + } + + self.sync(self.get(IS_NEW) ? 'create' : 'update', options, function (err, response) { + var facade = { + options : options, + response: response, + src: 'save' + }; + + if (err) { + facade.error = err; + + self.fire(EVT_ERROR, facade); + } else { + facade.parsed = self.parse(response); + + self._set(IS_MODIFIED, false); + self._set(IS_NEW, false); + self._loadedValues = Y.clone(self._values); + self.fire(EVT_SAVED, facade); + if (facade.parsed) { + self.fire(EVT_LOADED, facade); + } + } + + callback && callback.apply(null, arguments); + }); + }); + + return self; + }, + /** + * Restores the values when last loaded, saved or created. + * @method reset + */ + reset: function() { + this._values = Y.clone(this._loadedValues); + this.fire(EVT_RESET); + return this; + }, + /** + Override this method to provide a custom persistence implementation for this + model. The default just calls the callback without actually doing anything. + + This method is called internally by load(), save(), and destroy(). + + @method sync + @param {String} action Sync action to perform. May be one of the following: + + * create: Store a newly-created model for the first time. + * read : Load an existing model. + * update: Update an existing model. + * delete: Delete an existing model. + + @param {Object} [options] Sync options. It's up to the custom sync + implementation to determine what options it supports or requires, if any. + @param {Function} [callback] Called when the sync operation finishes. + @param {Error|null} callback.err If an error occurred, this parameter will + contain the error. If the sync operation succeeded, _err_ will be + falsy. + @param {Any} [callback.response] The server's response. This value will + be passed to the parse() method, which is expected to parse it and + return an attribute hash. + **/ + sync: function (action, options, callback) { + + if (typeof callback === 'function') { + callback(); + } + }, + /** + Override this method to provide custom validation logic for this model. + + While attribute-specific validators can be used to validate individual + attributes, this method gives you a hook to validate a hash of all + attributes before the model is saved. This method is called automatically + before save() takes any action. If validation fails, the save() call + will be aborted. + + In your validation method, call the provided callback function with no + arguments to indicate success. To indicate failure, pass a single argument, + which may contain an error message, an array of error messages, or any other + value. This value will be passed along to the error event. + + @example + + model.validate = function (attrs, callback) { + if (attrs.pie !== true) { + // No pie?! Invalid! + callback('Must provide pie.'); + return; + } + + // Success! + callback(); + }; + + @method validate + @param {Object} attrs Attribute hash containing all model attributes to + be validated. + @param {Function} callback Validation callback. Call this function when your + validation logic finishes. To trigger a validation failure, pass any + value as the first argument to the callback (ideally a meaningful + validation error of some kind). + + @param {Any} [callback.err] Validation error. Don't provide this + argument if validation succeeds. If validation fails, set this to an + error message or some other meaningful value. It will be passed + along to the resulting error event. + **/ + validate: function (attrs, callback) { + callback && callback(); + }, + /** + Calls the public, overridable validate() method and fires an error event + if validation fails. + + @method _validate + @param {Object} attributes Attribute hash. + @param {Function} callback Validation callback. + @param {Any} [callback.err] Value on failure, non-value on success. + @protected + **/ + _validate: function (attributes, callback) { + var self = this; + + self.validate(attributes, function (err) { + if (Lang.isValue(err)) { + // Validation failed. Fire an error. + self.fire(EVT_ERROR, { + attributes: attributes, + error : err, + src : 'validate' + }); + + callback(err); + return; + } + + callback(); + }); + + }, + /** + Returns a copy of this model's attributes that can be passed to + Y.JSON.stringify() or used for other nefarious purposes. + + The clientId attribute is not included in the returned object. + + If you've specified a custom attribute name in the idAttribute property, + the default id attribute will not be included in the returned object. + + @method toJSON + @return {Object} Copy of this model's attributes. + **/ + toJSON: function () { + return this.getValue(); + }, + _isModifiedGetter: function (value, name) { + name = name.split(DOT); + if (name.length > 1) { + name = name[1]; + var ret = {}; + ret[name] = this._values[name] !== this._loadedValues[name]; + return ret; + } else { + return value; + } + + }, + _isNewGetter: function (value, name) { + name = name.split(DOT); + if (name.length > 1) { + name = name[1]; + var ret = {}; + ret[name] = !this._loadedValues.hasOwnProperty(name); + return ret; + } else { + return value; + } + }, + _primaryKeysSetter: function (value) { + if (this._primaryKeys && this._primaryKeys.length) { + return Y.Attribute.INVALID_VALUE; + } + value = new YArray(value); + this._primaryKeys = value; + return value; + }, + _primaryKeysGetter: function (value, name) { + name = name.split(DOT); + if (name.length > 1) { + name = name[1]; + var ret = {}; + ret[name] = value.indexOf(name) !== -1; + return ret; + } else { + return (value || []).concat(); // makes sure to return a copy, not the original. + } + } + }, + { + ATTRS: { + /** + * Indicates whether any of the fields has been changed since created or loaded. + * Field names can be given as sub-attributes to indicate if any particular field has beeen changed. + * model.get('isModified.name') returns true if the field _name_ has been modified. + * Note: contrary to common practice in Attributes with sub-attributes, + * requesting the state of the record does not + * return an object with the state of each individual field keyed by field name, + * but the state of the record as a whole, which is far more useful. + * @attribute isModified + * @type Boolean + * @readonly + * @default false + */ + isModified: { + readOnly: true, + value:false, + validator:Lang.isBoolean, + getter: '_isModifiedGetter' + }, + /** + * Indicates that the model is new and has not been modified since creation. + * Field names can be given as sub-attributes to indicate if any particular field is new. + * model.get('isNew.name') returns true if the field _name_ is new. + * Note: contrary to common practice in Attributes with sub-attributes, + * requesting the state of the record does not + * return an object with the state of each individual field keyed by field name, + * but the state of the record as a whole, which is far more useful. + * @attribute isNew + * @type Boolean + * @readonly + * @default true + */ + isNew: { + readOnly: true, + value:true, + validator:Lang.isBoolean, + getter: '_isNewGetter' + }, + /** + * List of fields making the primary key of this model. + * Primary Key fields cannot be modified once initially loaded. + * It can be set as an array of field names or, if the key is made of a single field, a string with the name of that field. + * It will always be returned as an array. + * Field names can be given as a sub-attribute to ask whether a particular field is a primary key, thus: + * model.get('primaryKeys.name') returns true of the field name is a primary key. + * It can only be set once. + * @attribute primaryKeys + * @writeonce + * @type array + * @default [] + */ + primaryKeys: { + setter:'_primaryKeysSetter', + getter:'_primaryKeysGetter', + lazyAdd: false, + value: [] + } + } + + } + ); + + /** + * An extension for Y.GalleryModel that provides a single level of undo for each field. + * @class Y.GalleryModelSimpleUndo + */ + Y.GalleryModelSimpleUndo = function () {}; + + Y.GalleryModelSimpleUndo.prototype = { + initializer: function () { + this._lastChange = {}; + this._preserve = (this._preserve || []).concat('_lastChange'); + this.after(EVT_CHANGE, this._trackChange); + this.after([EVT_LOADED,EVT_SAVED,EVT_RESET], this._resetUndo); + }, + /** + * Event listener for the after value change event, it tracks changes for each field. + * It retains only the last change for each field. + * @method _trackChange + * @param ev {EventFacade} As provided by the change event + * @private + */ + _trackChange: function (ev) { + if (ev.name && ev.src !== 'undo') { + this._lastChange[ev.name] = ev.prevVal; + } + }, + /** + * After load or save operations, it drops any changes it might have tracked. + * @method _resetUndo + * @private + */ + _resetUndo: function () { + this._lastChange = {}; + }, + /** + * Reverts one level of change for a specific field or all fields + * @method undo + * @param [name] {String} If provided it will undo that particular field, + * otherwise, it undoes the whole record. + */ + undo: function (name) { + var self = this; + if (name) { + if (self._lastChange[name] !== undefined) { + self.setValue(name, self._lastChange[name], 'undo'); + delete self._lastChange[name]; + } + } else { + self.setValues(this._lastChange, 'undo'); + self._lastChange = {}; + } + } + }; + + /** + * Provides multiple levels of undo in strict chronological order + * whatever the field was at each stage. + * Changes done on multiple fields via setValues + * will also be undone in one step. + * @class Y.GalleryModelChronologicalUndo + */ + Y.GalleryModelChronologicalUndo = function () {}; + + Y.GalleryModelChronologicalUndo.prototype = { + initializer: function () { + this._changes = []; + this._preserve = (this._preserve || []).concat('_changes'); + this.after(EVT_CHANGE, this._trackChange); + this.after([EVT_LOADED,EVT_SAVED,EVT_RESET], this._resetUndo); + }, + /** + * Event listener for the after value change event, it tracks changes for each field. + * It keeps a stack of each change. + * @method _trackChange + * @param ev {EventFacade} As provided by the change event + * @private + */ + _trackChange: function (ev) { + if (ev.src !== 'undo') { + this._changes.push(ev.details); + } + }, + /** + * After load or save operations, it drops any changes it might have tracked. + * @method _resetUndo + * @private + */ + _resetUndo: function () { + this._changes = []; + }, + /** + * Reverts one level of field changes. + * @method undo + */ + undo: function () { + var ev = this._changes.pop(); + if (ev) { + if (ev.name) { + this.setValue(ev.name, ev.prevVal, 'undo'); + } else { + this.setValues(ev.prevVals, 'undo'); + } + } + if (this._changes.length === 0) { + this._set(IS_MODIFIED, false); + } + } + }; + + /** + * Allows GalleryModel to handle a set of records using the Flyweight pattern. + * It exposes one record at a time from a shelf of records. + * Exposed records can be selected by setting the _index_ attribute. + * @class Y.GalleryModelMultiRecord + */ + + var INDEX = 'index', + MR = function () {}; + + MR.prototype = { + initializer: function () { + this._shelves = []; + this._currentIndex = 0; + this.on(EVT_LOADED, this._batchLoad); + }, + /** + * Index of the shelf for the record being exposed. + * Use index attribute to check/set the index value. + * @property _currentIndex + * @type integer + * @default 0 + * @private + */ + _currentIndex: 0, + /** + * Storage for the records when not exposed. + * @property _shelves + * @type Array + * @private + */ + _shelves: null, + /** + * Saves the exposed record into the shelves at the position given by _currentIndex + * @method _shelve + * @private + */ + _shelve: function(index) { + if (index === undefined) { + index = this._currentIndex; + } + var self = this, + current = { + _values: self._values, + _loadedValues: self._loadedValues, + isNew: self.get(IS_NEW), + isModified: self.get(IS_MODIFIED) + }; + YArray.each(self._preserve, function (name) { + current[name] = self[name]; + }); + self._shelves[index] = current; + + }, + /** + * Retrives and exposes the record from the shelf at the position given by _currentIndex + * @method _fetch + * @private + */ + _fetch: function (index) { + if (index === undefined) { + index = this._currentIndex; + } + var self = this, + current = self._shelves[index]; + + self._values = current._values; + self._loadedValues = current._loadedValues; + self.__setStateVal(IS_NEW, current.isNew); + self.__setStateVal(IS_MODIFIED, current.isModified); + YArray.each(self._preserve, function (name) { + self[name] = current[name]; + }); + + }, + /** + * Initializes an exposed record + * @method _initNew + * @private + */ + _initNew: function () { + this._values = {}; + this._loadedValues = {}; + this._set(IS_NEW, true); + this._set(IS_MODIFIED, false); + }, + /** + * Adds a new record at the index position given or at the end. + * The new record becomes the current. + * @method add + * @param values {Object} set of values to set + * @param [index] {Integer} position to add the values at or at the end if not provided. + */ + add: function(values, index) { + if (this.get(IS_MODIFIED) || !this.get(IS_NEW)) { + this._shelve(); + } + if (arguments.length === 2) { + this._shelves.splice(index, 1); + } else { + index = this._shelves.length; + } + this._currentIndex = index; + this._initNew(); + this.setValues(values, 'add'); + }, + /** + * Executes the given function for each record in the set. + * Returning exactly false from the function spares shelving the record. + * If function does not modify the record, returning false will improve performance. + * @method each + * @param fn {function} function to execute, it will be provided with: + * @param fn.index {integer} index of the record exposed + */ + each: function(fn) { + var i, l, self = this; + this._shelve(); + for (i = 0, l = this._shelves.length; i < l; i += 1) { + this._currentIndex = i; + this._fetch(i); + if (fn.call(self, i) !== false) { + this._shelve(i); + } + } + }, + /** + * Executes the given function for each record in the set. + * It is faster than using each and then checking for isModified + * Returning exactly false from the function spares shelving the record. + * If function does not modify the record, returning false will improve performance. + * @method eachModified + * @param fn {function} function to execute, it will be provided with: + * @param fn.index {integer} index of the record exposed + */ + eachModified:function(fn) { + var i, l, self = this; + this._shelve(); + for (i = 0, l = this._shelves.length; i < l; i += 1) { + if (this._shelves[i][IS_MODIFIED]) { + this._currentIndex = i; + this._fetch(i); + if (fn.call(self, i) !== false) { + this._shelve(i); + } + } + } + }, + /** + * Calls _save_ on each record modified. + * This is not the best saving strategy for saving batches of records, + * but it is the easiest and safest. Implementors are encouraged to + * design their own. + * @method saveAllModified + */ + saveAllModified: function () { + this.eachModified(this.save); + }, + /** + * This is a documentation entry only, this method does not define load. + * + * This extension captures the _loaded_ event so that if a load + * returns an array of records, they will be added to the shelves. + * Existing records are kept, call _empty_ if they should be discarded. + * See method load of Y.GalleryModel for further info. + * @method load + */ + /** + * Listener for the loaded event, checks if the parsed response is an array + * and saves it into the shelves. + * @method _batchLoad + * @param ev {EventFacade} facade produced by load. + * @private + */ + _batchLoad: function (ev) { + var self = this; + if (ev.src === 'load' && ev.Lang.isArray(ev.parsed)) { + ev.halt(); + YArray.each(ev.parsed, function (values) { + self.add(values); + }); + } + }, + /** + * Returns the number of records stored + * @method size + * @return {Integer} number of records in the shelves + */ + size: function() { + return this._shelves.length; + }, + /** + * Empties the shelves of any records as well as the exposed record + * @method empty + */ + empty: function () { + this._shelves = []; + this._currentIndex = 0; + this.reset(); + }, + /** + * Setter for the _index_ attribute. + * Validates and copies the current index value into _currentIndex. + * @method _indexSetter + * @param value {integer} new value for the index + * @return {integer|INVALID_VALUE} new value for the index or INVALID_VALUE if invalid. + * @private + */ + _indexSetter: function (value) { + if (Lang.isNumber(value) && value >= 0 && value < this._shelves.length) { + this._shelve(this._currentIndex); + this._currentIndex = value = parseInt(value,10); + this._fetch(value); + return value; + } else { + return Y.Attribute.INVALID_VALUE; + } + }, + /** + * Getter for the _index_ attribute + * Returns the value from _currentIndex + * @method _indexGetter + * @return {integer} value of the index + * @private + */ + _indexGetter: function (value) { + return this._currentIndex; + } + + }; + + MR.ATTRS = { + /** + * Index of the record exposed. + * @attribute index + * @type Integer + * @default 0 + */ + index: { + value: 0, + setter:'_indexSetter', + getter:'_indexGetter' + } + }; + + Y.GalleryModelMultiRecord = MR; + + /** + * Extension to sort records stored in GalleryModel, extended with GalleryModelMultiRecord + * @class Y.GalleryModelSortedMultiRecord + */ + var SFIELD = 'sortField', + SDIR = 'sortDir', + ASC = 'asc', + DESC = 'desc', + SMR = function () {}; + + SMR.prototype = { + /** + * Compare function used in sorting. + * @method _compare + * @param a {object} shelf to compare + * @param b {object} shelf to compare + * @return {integer} -1, 0 or 1 as required by Array.sort + * @private + */ + _compare: null, + /** + * Initializer lifecycle method. + * Ensures proper defaults, sets the compare method and + * sets listeners for relevant events + * @method initializer + * @protected + */ + initializer: function () { + if (this.get(SFIELD) === undefined) { + this._set(SFIELD, this.get('primaryKeys')[0]); + } + this._setCompare(); + this.after([SFIELD + CHANGE, SDIR + CHANGE], this._sort); + this.after('change', this._afterChange); + }, + /** + * Sets the compare function to be used in sorting the records + * based on the sortField and sortDir and stores it into this._compare + * @method _setCompare + * @private + */ + _setCompare: function () { + var sortField = this.get(SFIELD), + sortAsc = this.get(SDIR) === ASC?1:-1, + compareValue = (Lang.isFunction(sortField)? + sortField: + function(values) { + return values[sortField]; + } + ); + this._compare = function(a, b) { + var aValue = compareValue(a._values), + bValue = compareValue(b._values); + + return (aValue < bValue ? -1 : (aValue > bValue ? 1 : 0)) * sortAsc; + }; + }, + /** + * Sorts the shelves whenever the sortField or sortDir is changes + * @method _sort + * @private + */ + _sort: function() { + this._setCompare(); + this._shelve(); + this._shelves.sort(this._compare); + this._fetch(); + }, + /** + * Listens to value changes and if the name of the field is that of the sortField + * or if sortField is a function, it will relocate the record to its proper sort order + * @method _afterChange + * @param ev {EventFacade} Event façade as produced by the change event + * @private + */ + _afterChange: function (ev) { + var fieldName = ev.name, + sField = this.get(SFIELD), + index, + currentIndex = this._currentIndex, + shelves = this._shelves, + currentShelf; + + if (fieldName && ev.src !== 'add' && (Lang.isFunction(sField) || fieldName === sField)) { + // The shelf has to be emptied otherwise _findIndex may match itself. + currentShelf = shelves.splice(currentIndex,1)[0]; + index = this._findIndex(currentShelf._values); + shelves.splice(index,0,currentShelf); + this._currentIndex = index; + } + }, + /** + * Finds the correct index position of a record within the shelves + * according to the current sortField and sortDir + * @method _findIndex + * @param values {Object} values of the record to be located + * @return {Integer} location for the record + * @private + */ + _findIndex: function (values) { + var shelves = this._shelves, + low = 0, + high = shelves.length, + index = 0, + cmp = this._compare, + vals = {_values: values}; + + while (low < high) { + index = (high + low) >> 1; + switch(cmp(vals, shelves[index])) { + case 1: + low = index + 1; + break; + case -1: + high = index; + break; + default: + low = high = index; + } + + } + return low; + + }, + /** + * Adds a new record at its proper position according to the sort configuration. + * It overrides GalleryModelMultiRecord's own add method, ignoring the index position requested, if any. + * The new record becomes the current. + * @method add + * @param values {Object} set of values to set + */ + add: function(values) { + var shelves = this._shelves, + index = 0; + + index = this._findIndex(values); + this._currentIndex = index; + shelves.splice(index, 0, {}); + this._initNew(); + this.setValues(values, 'add'); + this._shelve(index); + }, + /** + * Locates a record by value. The record will be located by the field + * given in the sortField attribute. It will return the index of the + * record in the shelves or null if not found. + * By default it will expose that record. + * If sortField contains a function, it will return null and do nothing. + * Since sort fields need not be unique, find may return any of the records + * with the same value for that field. + * @method find + * @param value {Any} value to be found + * @param [move] {Boolean} exposes the record found, defaults to true + * @return {integer | null} index of the record found or null if not found. + * Be sure to differentiate a return of 0, a valid index, from null, a failed search. + */ + find: function (value, move) { + var sfield = this.get(SFIELD), + index, + values = {}; + if (Lang.isFunction(sfield)) { + return null; + } + values[sfield] = value; + index = this._findIndex(values); + if (this._shelves[index]._values[sfield] !== value) { + return null; + } + if (move || arguments.length < 2) { + this.set(INDEX, index); + } + return index; + } + }; + SMR.ATTRS = { + /** + * Name of the field to sort by or function to build the value used for comparisson. + * If a function, it will receive a reference to the record to be sorted; + * it should return the value to be used for comparisson. Functions are + * used when sorting on multiple keys, which the function should return + * concatenated, or when any of the fields needs some pre-processing. + * @attribute sortField + * @type String | Function + * @default first primary key field + */ + sortField: { + validator: function (value){ + return Lang.isString(value) || Lang.isFunction(value); + } + }, + /** + * Sort direction either "asc" for ascending or "desc" for descending + * @attribute sortDir + * @type String + * @default "asc" + */ + sortDir: { + validator: function (value) { + return value === DESC || value === ASC; + }, + value: ASC + } + }; + Y.GalleryModelSortedMultiRecord = SMR; + + + + +}, 'gallery-2012.03.23-18-00' ,{requires:['base'], skinnable:false}); diff --git a/build/gallery-model-list-difference/gallery-model-list-difference-debug.js b/build/gallery-model-list-difference/gallery-model-list-difference-debug.js new file mode 100644 index 0000000000..8a7a7b9afd --- /dev/null +++ b/build/gallery-model-list-difference/gallery-model-list-difference-debug.js @@ -0,0 +1,73 @@ +YUI.add('gallery-model-list-difference', function(Y) { + +/** + * Creates a model list that is the difference of two or more other model lists. + * @module gallery-model-list-difference + */ +(function (Y) { + 'use strict'; + + var _Array = Y.Array, + _ModelList = Y.ModelList, + + _filter = _Array.filter, + _indexOf = _Array.indexOf, + _invoke = _Array.invoke, + _isString = Y.Lang.isString, + _reduce = _Array.reduce, + _reduceFn, + _unnest = _Array.unnest; + + /** + * Creates a model list that is the difference of two or more other model lists. + * The new model list stays up to date as the source lists change. + * @method difference + * @for ModelList + * @param {Function|Object|String} modelListType Optional. The first argument determines + * the type of model list that is created; it may be a constructor function or a string namespace + * to a constructor function stored on Y. If the first argument is an instance of ModelList, its + * constructor is used. + * @param {Object} modelLists 1-n ModelList objects to difference. Order is important. + * @return {Object} + * @static + */ + Y.ModelList.difference = function () { + var modelList, + modelLists = _unnest(arguments), + modelListType = modelLists.shift(), + + updateModelList = function () { + return modelList.reset(_reduce(_invoke(modelLists, 'toArray'), 0, _reduceFn)); + }; + + if (_isString(modelListType)) { + modelListType = Y.namespace(modelListType); + } else if (modelListType instanceof _ModelList) { + modelLists.unshift(modelListType); + modelListType = modelListType.constructor; + } + + modelList = new modelListType(); + + _invoke(modelLists, 'after', [ + 'add', + 'remove', + 'reset' + ], updateModelList); + + return updateModelList(); + }; + + _reduceFn = function (previousArray, currentArray) { + if (!previousArray) { + return currentArray; + } + + return _filter(previousArray, function (value) { + return _indexOf(currentArray, value) === -1; + }); + }; +}(Y)); + + +}, 'gallery-2012.03.23-18-00' ,{requires:['array-extras', 'array-invoke', 'gallery-array-unnest', 'model-list'], skinnable:false}); diff --git a/build/gallery-model-list-difference/gallery-model-list-difference-min.js b/build/gallery-model-list-difference/gallery-model-list-difference-min.js new file mode 100644 index 0000000000..d7dc781e87 --- /dev/null +++ b/build/gallery-model-list-difference/gallery-model-list-difference-min.js @@ -0,0 +1 @@ +YUI.add("gallery-model-list-difference",function(a){(function(b){var f=b.Array,e=b.ModelList,g=f.filter,j=f.indexOf,i=f.invoke,d=b.Lang.isString,c=f.reduce,h,k=f.unnest;b.ModelList.difference=function(){var m,n=k(arguments),o=n.shift(),l=function(){return m.reset(c(i(n,"toArray"),0,h));};if(d(o)){o=b.namespace(o);}else{if(o instanceof e){n.unshift(o);o=o.constructor;}}m=new o();i(n,"after",["add","remove","reset"],l);return l();};h=function(l,m){if(!l){return m;}return g(l,function(n){return j(m,n)===-1;});};}(a));},"gallery-2012.03.23-18-00",{requires:["array-extras","array-invoke","gallery-array-unnest","model-list"],skinnable:false}); \ No newline at end of file diff --git a/build/gallery-model-list-difference/gallery-model-list-difference.js b/build/gallery-model-list-difference/gallery-model-list-difference.js new file mode 100644 index 0000000000..8a7a7b9afd --- /dev/null +++ b/build/gallery-model-list-difference/gallery-model-list-difference.js @@ -0,0 +1,73 @@ +YUI.add('gallery-model-list-difference', function(Y) { + +/** + * Creates a model list that is the difference of two or more other model lists. + * @module gallery-model-list-difference + */ +(function (Y) { + 'use strict'; + + var _Array = Y.Array, + _ModelList = Y.ModelList, + + _filter = _Array.filter, + _indexOf = _Array.indexOf, + _invoke = _Array.invoke, + _isString = Y.Lang.isString, + _reduce = _Array.reduce, + _reduceFn, + _unnest = _Array.unnest; + + /** + * Creates a model list that is the difference of two or more other model lists. + * The new model list stays up to date as the source lists change. + * @method difference + * @for ModelList + * @param {Function|Object|String} modelListType Optional. The first argument determines + * the type of model list that is created; it may be a constructor function or a string namespace + * to a constructor function stored on Y. If the first argument is an instance of ModelList, its + * constructor is used. + * @param {Object} modelLists 1-n ModelList objects to difference. Order is important. + * @return {Object} + * @static + */ + Y.ModelList.difference = function () { + var modelList, + modelLists = _unnest(arguments), + modelListType = modelLists.shift(), + + updateModelList = function () { + return modelList.reset(_reduce(_invoke(modelLists, 'toArray'), 0, _reduceFn)); + }; + + if (_isString(modelListType)) { + modelListType = Y.namespace(modelListType); + } else if (modelListType instanceof _ModelList) { + modelLists.unshift(modelListType); + modelListType = modelListType.constructor; + } + + modelList = new modelListType(); + + _invoke(modelLists, 'after', [ + 'add', + 'remove', + 'reset' + ], updateModelList); + + return updateModelList(); + }; + + _reduceFn = function (previousArray, currentArray) { + if (!previousArray) { + return currentArray; + } + + return _filter(previousArray, function (value) { + return _indexOf(currentArray, value) === -1; + }); + }; +}(Y)); + + +}, 'gallery-2012.03.23-18-00' ,{requires:['array-extras', 'array-invoke', 'gallery-array-unnest', 'model-list'], skinnable:false}); diff --git a/build/gallery-model-sync-rest/gallery-model-sync-rest-debug.js b/build/gallery-model-sync-rest/gallery-model-sync-rest-debug.js index dc64cc13c7..1a795c946d 100644 --- a/build/gallery-model-sync-rest/gallery-model-sync-rest-debug.js +++ b/build/gallery-model-sync-rest/gallery-model-sync-rest-debug.js @@ -16,43 +16,48 @@ transmit JSON data via RESTful HTTP. In most cases you'll only need to provide a value for `root` when sub-classing Model, and only provide a value for `url` when sub-classing ModelList. -@example - var User = Y.Base.create('user', Y.Model, [Y.ModelSync.REST], { - root : '/user' + root: '/user' }, { - ATTRS : { - name : {} + ATTRS: { + name: {} } }); var Users = Y.Base.create('users', Y.ModelList, [Y.ModelSync.REST], { - model : User, - url : '/user' + model: User, + url : '/user' }); @class ModelSync.REST -@extensionfor Model ModelList +@extensionfor Model +@extensionfor ModelList **/ -var RESTSync, +var Lang = Y.Lang, - Lang = Y.Lang, sub = Lang.sub, isValue = Lang.isValue, isString = Lang.isString, isNumber = Lang.isNumber, isFunction = Lang.isFunction; -// *** RESTSync *** // +// -- RESTSync ----------------------------------------------------------------- -RESTSync = function () {}; + function RESTSync() {} /** Static hash lookup table of RESTful HTTP methods corresponding to CRUD actions. @property HTTP_METHODS @type Object +@default + { + 'create': 'POST', + 'read' : 'GET', + 'update': 'PUT', + 'delete': 'DELETE' + } @static **/ RESTSync.HTTP_METHODS = { @@ -108,9 +113,21 @@ was overridden. **/ RESTSync.EMULATE_HTTP = false; +/** +Properties that shouldn't be turned into ad-hoc attributes when passed to a +Model or ModelList constructor. + +@property _NON_ATTRS_CFG +@type Array +@default ['url'] +@static +@protected +**/ +RESTSync._NON_ATTRS_CFG = ['url']; + RESTSync.prototype = { - // *** Public Properties *** // + // -- Public Properties ---------------------------------------------------- /** A String which represents the root or collection part of the URL space which @@ -123,20 +140,19 @@ RESTSync.prototype = { '/'; if the `root` does not end with a slash, neither will the XHR URLs. @example - var User = Y.Base.create('user', Y.Model, [Y.ModelSync.REST], { - root : '/user/' + root: '/user/' }, { - ATTRS : { - name : {} + ATTRS: { + name: {} } }); - var myUser = new User({ id: '123' }); - myUser.load(); // will GET the User data from: /user/123/ + var myUser = new User({id: '123'}); + myUser.load(); // Will GET the User data from: /user/123/ - var newUser = new User({ name: 'Eric Ferraiuolo' }); - newUser.save(); // will POST the User data to: /user/ + var newUser = new User({name: 'Eric Ferraiuolo'}); + newUser.save(); // Will POST the User data to: /user/ When sub-classing Y.ModelList, usually you'll want to ignore configuring the `root` and instead just set the `url` to a String; but if you just specify a @@ -144,24 +160,24 @@ RESTSync.prototype = { @property root @type String - @default '' + @default "" **/ - root : '', + root: '', /** A Function or String which is used to generate or specify the URL for the XHRs. While, this property can be defined for each Model/ModelList instance, - usually you'll want to use a Function or String pattern instead. + usually you'll want to use a Function or String-pattern instead. If the `url` property is a Function, it should return the String that should - be used as the URL. The Function will be called before each request. + be used as the URL. The Function will be called before each request and will + be passed the sync `action` which is currently being performed. If the `url` property is a String, it will be processed by `Y.Lang.sub()`; this is useful when the URLs for a Model type match a specific pattern and can use simple replacement tokens: @example - '/user/{id}' **Note:** String substitution on the `url` property will only happen for @@ -181,31 +197,28 @@ RESTSync.prototype = { properties like this: @example - var User = Y.Base.create('user', Y.Model, [Y.ModelSync.REST], { - root : '/users', - url : '/user/{id}' + root: '/users', + url : '/user/{id}' }, { - ATTRS : { - name : {} + ATTRS: { + name: {} } }); - var myUser = new User({ id: '123' }); + var myUser = new User({id: '123'}); myUser.load(); // Will GET the User data from: /user/123 - var newUser = new User({ name: 'Eric Ferraiuolo' }); + var newUser = new User({name: 'Eric Ferraiuolo'}); newUser.save(); // Will POST the User data to: /users When sub-classing Y.ModelList, you probably just need to specify a simple String for the `url` property and leave `root` to be the default value. - @property + @property url @type Function|String - @method url - @return {String} URL to for the XHR to the server. **/ - url : function () { + url: function () { var root = this.root, url; @@ -222,14 +235,14 @@ RESTSync.prototype = { return this._joinURL(url); }, - // *** Lifecycle Methods *** // + // -- Lifecycle Methods ---------------------------------------------------- - initializer : function (config) { + initializer: function (config) { config || (config = {}); isValue(config.url) && (this.url = config.url); }, - // *** Public Methods *** // + // -- Public Methods ------------------------------------------------------- /** Communicates with a RESTful HTTP server by sending and receiving JSON data @@ -240,10 +253,10 @@ RESTSync.prototype = { @method sync @param {String} action Sync action to perform. May be one of the following: - * create: Store a newly-created model for the first time. - * delete: Delete an existing model. - * read : Load an existing model. - * update: Update an existing model. + * **create**: Store a newly-created model for the first time. + * **read** : Load an existing model. + * **update**: Update an existing model. + * **delete**: Delete an existing model. @param {Object} [options] Sync options. @param {Object} [options.headers] The HTTP headers to mix with the default @@ -258,10 +271,10 @@ RESTSync.prototype = { be passed to the parse() method, which is expected to parse it and return an attribute hash. **/ - sync : function (action, options, callback) { + sync: function (action, options, callback) { options || (options = {}); - var url = this._getURL(), + var url = this._getURL(action), method = RESTSync.HTTP_METHODS[action], headers = Y.merge(RESTSync.HTTP_HEADERS, options.headers), timeout = options.timeout || RESTSync.HTTP_TIMEOUT, @@ -269,7 +282,7 @@ RESTSync.prototype = { // Prepare the content if we are sending data to the server. if (method === 'POST' || method === 'PUT') { - entity = Y.JSON.stringify(this); + entity = this._serialize(); } else { // Remove header, no content is being sent. delete headers['Content-Type']; @@ -278,6 +291,7 @@ RESTSync.prototype = { // Setup HTTP emulation for older servers if we need it. if (RESTSync.EMULATE_HTTP && (method === 'PUT' || method === 'DELETE')) { + // Pass along original method type in the headers. headers['X-HTTP-Method-Override'] = method; // Fall-back to using POST method type. @@ -286,17 +300,19 @@ RESTSync.prototype = { // Setup and send the XHR. Y.io(url, { - method : method, - headers : headers, - data : entity, - timeout : timeout, - on : { - success : function (txId, res) { + method : method, + headers: headers, + data : entity, + timeout: timeout, + + on: { + success: function (txId, res) { if (isFunction(callback)) { callback(null, res.responseText); } }, - failure : function (txId, res) { + + failure: function (txId, res) { if (isFunction(callback)) { callback({ code: res.status, @@ -308,7 +324,7 @@ RESTSync.prototype = { }); }, - // *** Protected Methods *** // + // -- Protected Methods ---------------------------------------------------- /** Helper method to return the URL to use when making the XHR to the server. @@ -316,19 +332,21 @@ RESTSync.prototype = { This method correctly handles variations of the `url` property/method. @method _getURL - @return {String} the URL for the XHR + @param {String} action Sync action to perform. + @return {String} the URL for the XHR. @protected **/ - _getURL : function () { + _getURL: function (action) { var url = this.url, data; if (isFunction(url)) { - return this.url(); + return this.url(action); } if (this instanceof Y.Model) { data = {}; + Y.Object.each(this.toJSON(), function (v, k) { if (isString(v) || isNumber(v)) { // URL-encode any String or Number values. @@ -351,7 +369,6 @@ RESTSync.prototype = { https://github.com/yui/yui3/blob/master/src/app/js/controller.js @example - model.root = '/foo' model._joinURL('bar'); // => '/foo/bar' model._joinURL('/bar'); // => '/foo/bar' @@ -375,13 +392,33 @@ RESTSync.prototype = { return root && root.charAt(root.length - 1) === '/' ? root + url : root + '/' + url; - } + }, + /** + Serializes `this` model to be used as the HTTP request entity body. By + default this model will be serialized to a JSON string via its `toJSON()` + method. + + You can override this method when the HTTP server expects a different + representation of this model's data that is different from the default JSON + serialization. + + **Note:** A model's `toJSON()` method can also be overridden; if you just + need to modify which attributes are serialized to JSON, that's a better + place to start. + + @method _serialize + @return {String} serialized HTTP request entity body + @protected + **/ + _serialize: function () { + return Y.JSON.stringify(this); + } }; -// *** Namespace *** // +// -- Namespace ---------------------------------------------------------------- Y.namespace('ModelSync').REST = RESTSync; -}, 'gallery-2012.01.04-22-09' ,{requires:['model', 'model-list', 'io-base', 'json-stringify'], skinnable:false}); +}, 'gallery-2012.03.23-18-00' ,{requires:['model', 'model-list', 'io-base', 'json-stringify'], skinnable:false}); diff --git a/build/gallery-model-sync-rest/gallery-model-sync-rest-min.js b/build/gallery-model-sync-rest/gallery-model-sync-rest-min.js index 88c46df9eb..b9b618266a 100644 --- a/build/gallery-model-sync-rest/gallery-model-sync-rest-min.js +++ b/build/gallery-model-sync-rest/gallery-model-sync-rest-min.js @@ -1 +1 @@ -YUI.add("gallery-model-sync-rest",function(h){var e,d=h.Lang,c=d.sub,g=d.isValue,a=d.isString,b=d.isNumber,f=d.isFunction;e=function(){};e.HTTP_METHODS={"create":"POST","read":"GET","update":"PUT","delete":"DELETE"};e.HTTP_HEADERS={"Accept":"application/json","Content-Type":"application/json"};e.EMULATE_HTTP=false;e.prototype={root:"",url:function(){var i=this.root,j;if(this instanceof h.ModelList||this.isNew()){return i;}j=this.getAsURL("id");if(i&&i.charAt(i.length-1)==="/"){j+="/";}return this._joinURL(j);},initializer:function(i){i||(i={});g(i.url)&&(this.url=i.url);},sync:function(m,k,p){k||(k={});var j=this._getURL(),o=e.HTTP_METHODS[m],n=h.merge(e.HTTP_HEADERS,k.headers),l=k.timeout||e.HTTP_TIMEOUT,i;if(o==="POST"||o==="PUT"){i=h.JSON.stringify(this);}else{delete n["Content-Type"];}if(e.EMULATE_HTTP&&(o==="PUT"||o==="DELETE")){n["X-HTTP-Method-Override"]=o;o="POST";}h.io(j,{method:o,headers:n,data:i,timeout:l,on:{success:function(r,q){if(f(p)){p(null,q.responseText);}},failure:function(r,q){if(f(p)){p({code:q.status,msg:q.statusText},q.responseText);}}}});},_getURL:function(){var i=this.url,j;if(f(i)){return this.url();}if(this instanceof h.Model){j={};h.Object.each(this.toJSON(),function(m,l){if(a(m)||b(m)){j[l]=encodeURIComponent(m);}});i=c(i,j);}return i||this.root;},_joinURL:function(j){var i=this.root;if(j.charAt(0)==="/"){j=j.substring(1);}return i&&i.charAt(i.length-1)==="/"?i+j:i+"/"+j;}};h.namespace("ModelSync").REST=e;},"gallery-2012.01.04-22-09",{requires:["model","model-list","io-base","json-stringify"],skinnable:false}); \ No newline at end of file +YUI.add("gallery-model-sync-rest",function(h){var d=h.Lang,c=d.sub,g=d.isValue,a=d.isString,b=d.isNumber,f=d.isFunction;function e(){}e.HTTP_METHODS={"create":"POST","read":"GET","update":"PUT","delete":"DELETE"};e.HTTP_HEADERS={"Accept":"application/json","Content-Type":"application/json"};e.EMULATE_HTTP=false;e._NON_ATTRS_CFG=["url"];e.prototype={root:"",url:function(){var i=this.root,j;if(this instanceof h.ModelList||this.isNew()){return i;}j=this.getAsURL("id");if(i&&i.charAt(i.length-1)==="/"){j+="/";}return this._joinURL(j);},initializer:function(i){i||(i={});g(i.url)&&(this.url=i.url);},sync:function(m,k,p){k||(k={});var j=this._getURL(m),o=e.HTTP_METHODS[m],n=h.merge(e.HTTP_HEADERS,k.headers),l=k.timeout||e.HTTP_TIMEOUT,i;if(o==="POST"||o==="PUT"){i=this._serialize();}else{delete n["Content-Type"];}if(e.EMULATE_HTTP&&(o==="PUT"||o==="DELETE")){n["X-HTTP-Method-Override"]=o;o="POST";}h.io(j,{method:o,headers:n,data:i,timeout:l,on:{success:function(r,q){if(f(p)){p(null,q.responseText);}},failure:function(r,q){if(f(p)){p({code:q.status,msg:q.statusText},q.responseText);}}}});},_getURL:function(k){var i=this.url,j;if(f(i)){return this.url(k);}if(this instanceof h.Model){j={};h.Object.each(this.toJSON(),function(m,l){if(a(m)||b(m)){j[l]=encodeURIComponent(m);}});i=c(i,j);}return i||this.root;},_joinURL:function(j){var i=this.root;if(j.charAt(0)==="/"){j=j.substring(1);}return i&&i.charAt(i.length-1)==="/"?i+j:i+"/"+j;},_serialize:function(){return h.JSON.stringify(this);}};h.namespace("ModelSync").REST=e;},"gallery-2012.03.23-18-00",{requires:["model","model-list","io-base","json-stringify"],skinnable:false}); \ No newline at end of file diff --git a/build/gallery-model-sync-rest/gallery-model-sync-rest.js b/build/gallery-model-sync-rest/gallery-model-sync-rest.js index dc64cc13c7..1a795c946d 100644 --- a/build/gallery-model-sync-rest/gallery-model-sync-rest.js +++ b/build/gallery-model-sync-rest/gallery-model-sync-rest.js @@ -16,43 +16,48 @@ transmit JSON data via RESTful HTTP. In most cases you'll only need to provide a value for `root` when sub-classing Model, and only provide a value for `url` when sub-classing ModelList. -@example - var User = Y.Base.create('user', Y.Model, [Y.ModelSync.REST], { - root : '/user' + root: '/user' }, { - ATTRS : { - name : {} + ATTRS: { + name: {} } }); var Users = Y.Base.create('users', Y.ModelList, [Y.ModelSync.REST], { - model : User, - url : '/user' + model: User, + url : '/user' }); @class ModelSync.REST -@extensionfor Model ModelList +@extensionfor Model +@extensionfor ModelList **/ -var RESTSync, +var Lang = Y.Lang, - Lang = Y.Lang, sub = Lang.sub, isValue = Lang.isValue, isString = Lang.isString, isNumber = Lang.isNumber, isFunction = Lang.isFunction; -// *** RESTSync *** // +// -- RESTSync ----------------------------------------------------------------- -RESTSync = function () {}; + function RESTSync() {} /** Static hash lookup table of RESTful HTTP methods corresponding to CRUD actions. @property HTTP_METHODS @type Object +@default + { + 'create': 'POST', + 'read' : 'GET', + 'update': 'PUT', + 'delete': 'DELETE' + } @static **/ RESTSync.HTTP_METHODS = { @@ -108,9 +113,21 @@ was overridden. **/ RESTSync.EMULATE_HTTP = false; +/** +Properties that shouldn't be turned into ad-hoc attributes when passed to a +Model or ModelList constructor. + +@property _NON_ATTRS_CFG +@type Array +@default ['url'] +@static +@protected +**/ +RESTSync._NON_ATTRS_CFG = ['url']; + RESTSync.prototype = { - // *** Public Properties *** // + // -- Public Properties ---------------------------------------------------- /** A String which represents the root or collection part of the URL space which @@ -123,20 +140,19 @@ RESTSync.prototype = { '/'; if the `root` does not end with a slash, neither will the XHR URLs. @example - var User = Y.Base.create('user', Y.Model, [Y.ModelSync.REST], { - root : '/user/' + root: '/user/' }, { - ATTRS : { - name : {} + ATTRS: { + name: {} } }); - var myUser = new User({ id: '123' }); - myUser.load(); // will GET the User data from: /user/123/ + var myUser = new User({id: '123'}); + myUser.load(); // Will GET the User data from: /user/123/ - var newUser = new User({ name: 'Eric Ferraiuolo' }); - newUser.save(); // will POST the User data to: /user/ + var newUser = new User({name: 'Eric Ferraiuolo'}); + newUser.save(); // Will POST the User data to: /user/ When sub-classing Y.ModelList, usually you'll want to ignore configuring the `root` and instead just set the `url` to a String; but if you just specify a @@ -144,24 +160,24 @@ RESTSync.prototype = { @property root @type String - @default '' + @default "" **/ - root : '', + root: '', /** A Function or String which is used to generate or specify the URL for the XHRs. While, this property can be defined for each Model/ModelList instance, - usually you'll want to use a Function or String pattern instead. + usually you'll want to use a Function or String-pattern instead. If the `url` property is a Function, it should return the String that should - be used as the URL. The Function will be called before each request. + be used as the URL. The Function will be called before each request and will + be passed the sync `action` which is currently being performed. If the `url` property is a String, it will be processed by `Y.Lang.sub()`; this is useful when the URLs for a Model type match a specific pattern and can use simple replacement tokens: @example - '/user/{id}' **Note:** String substitution on the `url` property will only happen for @@ -181,31 +197,28 @@ RESTSync.prototype = { properties like this: @example - var User = Y.Base.create('user', Y.Model, [Y.ModelSync.REST], { - root : '/users', - url : '/user/{id}' + root: '/users', + url : '/user/{id}' }, { - ATTRS : { - name : {} + ATTRS: { + name: {} } }); - var myUser = new User({ id: '123' }); + var myUser = new User({id: '123'}); myUser.load(); // Will GET the User data from: /user/123 - var newUser = new User({ name: 'Eric Ferraiuolo' }); + var newUser = new User({name: 'Eric Ferraiuolo'}); newUser.save(); // Will POST the User data to: /users When sub-classing Y.ModelList, you probably just need to specify a simple String for the `url` property and leave `root` to be the default value. - @property + @property url @type Function|String - @method url - @return {String} URL to for the XHR to the server. **/ - url : function () { + url: function () { var root = this.root, url; @@ -222,14 +235,14 @@ RESTSync.prototype = { return this._joinURL(url); }, - // *** Lifecycle Methods *** // + // -- Lifecycle Methods ---------------------------------------------------- - initializer : function (config) { + initializer: function (config) { config || (config = {}); isValue(config.url) && (this.url = config.url); }, - // *** Public Methods *** // + // -- Public Methods ------------------------------------------------------- /** Communicates with a RESTful HTTP server by sending and receiving JSON data @@ -240,10 +253,10 @@ RESTSync.prototype = { @method sync @param {String} action Sync action to perform. May be one of the following: - * create: Store a newly-created model for the first time. - * delete: Delete an existing model. - * read : Load an existing model. - * update: Update an existing model. + * **create**: Store a newly-created model for the first time. + * **read** : Load an existing model. + * **update**: Update an existing model. + * **delete**: Delete an existing model. @param {Object} [options] Sync options. @param {Object} [options.headers] The HTTP headers to mix with the default @@ -258,10 +271,10 @@ RESTSync.prototype = { be passed to the parse() method, which is expected to parse it and return an attribute hash. **/ - sync : function (action, options, callback) { + sync: function (action, options, callback) { options || (options = {}); - var url = this._getURL(), + var url = this._getURL(action), method = RESTSync.HTTP_METHODS[action], headers = Y.merge(RESTSync.HTTP_HEADERS, options.headers), timeout = options.timeout || RESTSync.HTTP_TIMEOUT, @@ -269,7 +282,7 @@ RESTSync.prototype = { // Prepare the content if we are sending data to the server. if (method === 'POST' || method === 'PUT') { - entity = Y.JSON.stringify(this); + entity = this._serialize(); } else { // Remove header, no content is being sent. delete headers['Content-Type']; @@ -278,6 +291,7 @@ RESTSync.prototype = { // Setup HTTP emulation for older servers if we need it. if (RESTSync.EMULATE_HTTP && (method === 'PUT' || method === 'DELETE')) { + // Pass along original method type in the headers. headers['X-HTTP-Method-Override'] = method; // Fall-back to using POST method type. @@ -286,17 +300,19 @@ RESTSync.prototype = { // Setup and send the XHR. Y.io(url, { - method : method, - headers : headers, - data : entity, - timeout : timeout, - on : { - success : function (txId, res) { + method : method, + headers: headers, + data : entity, + timeout: timeout, + + on: { + success: function (txId, res) { if (isFunction(callback)) { callback(null, res.responseText); } }, - failure : function (txId, res) { + + failure: function (txId, res) { if (isFunction(callback)) { callback({ code: res.status, @@ -308,7 +324,7 @@ RESTSync.prototype = { }); }, - // *** Protected Methods *** // + // -- Protected Methods ---------------------------------------------------- /** Helper method to return the URL to use when making the XHR to the server. @@ -316,19 +332,21 @@ RESTSync.prototype = { This method correctly handles variations of the `url` property/method. @method _getURL - @return {String} the URL for the XHR + @param {String} action Sync action to perform. + @return {String} the URL for the XHR. @protected **/ - _getURL : function () { + _getURL: function (action) { var url = this.url, data; if (isFunction(url)) { - return this.url(); + return this.url(action); } if (this instanceof Y.Model) { data = {}; + Y.Object.each(this.toJSON(), function (v, k) { if (isString(v) || isNumber(v)) { // URL-encode any String or Number values. @@ -351,7 +369,6 @@ RESTSync.prototype = { https://github.com/yui/yui3/blob/master/src/app/js/controller.js @example - model.root = '/foo' model._joinURL('bar'); // => '/foo/bar' model._joinURL('/bar'); // => '/foo/bar' @@ -375,13 +392,33 @@ RESTSync.prototype = { return root && root.charAt(root.length - 1) === '/' ? root + url : root + '/' + url; - } + }, + /** + Serializes `this` model to be used as the HTTP request entity body. By + default this model will be serialized to a JSON string via its `toJSON()` + method. + + You can override this method when the HTTP server expects a different + representation of this model's data that is different from the default JSON + serialization. + + **Note:** A model's `toJSON()` method can also be overridden; if you just + need to modify which attributes are serialized to JSON, that's a better + place to start. + + @method _serialize + @return {String} serialized HTTP request entity body + @protected + **/ + _serialize: function () { + return Y.JSON.stringify(this); + } }; -// *** Namespace *** // +// -- Namespace ---------------------------------------------------------------- Y.namespace('ModelSync').REST = RESTSync; -}, 'gallery-2012.01.04-22-09' ,{requires:['model', 'model-list', 'io-base', 'json-stringify'], skinnable:false}); +}, 'gallery-2012.03.23-18-00' ,{requires:['model', 'model-list', 'io-base', 'json-stringify'], skinnable:false}); diff --git a/build/gallery-model-sync-yql/gallery-model-sync-yql-debug.js b/build/gallery-model-sync-yql/gallery-model-sync-yql-debug.js index edc0b14b61..6ee57b99a7 100644 --- a/build/gallery-model-sync-yql/gallery-model-sync-yql-debug.js +++ b/build/gallery-model-sync-yql/gallery-model-sync-yql-debug.js @@ -14,40 +14,50 @@ YQL. **Note:** that `read` is the only `sync()` action that is supported at this time, you will not be able to `save()` data to YQL. -@example - var Photo = Y.Base.create('photo', Y.Model, [Y.ModelSync.YQL], { - query : 'SELECT * FROM flickr.photos.info WHERE photo_id={id}', - parse : function (results) { + query: 'SELECT * FROM flickr.photos.info WHERE photo_id={id}', + parse: function (results) { return results && results.photo; } }, { - ATTRS : { - title : {}, - description : {} + ATTRS: { + title : {}, + description: {} } }); @class ModelSync.YQL -@extensionfor Model ModelList +@extensionfor Model +@extensionfor ModelList **/ -var YQLSync, +var Lang = Y.Lang, - Lang = Y.Lang, sub = Lang.sub, isValue = Lang.isValue, isFunction = Lang.isFunction, - noop = function () {}; + noop = function () {}; + +// -- YQLSYnc ------------------------------------------------------------------ -// *** YQLSYnc *** // +function YQLSync() {} -YQLSync = function () {}; +/** +Properties that shouldn't be turned into ad-hoc attributes when passed to a +Model or ModelList constructor. + +@property _NON_ATTRS_CFG +@type Array +@default ['query'] +@static +@protected +**/ +YQLSync._NON_ATTRS_CFG = ['query']; YQLSync.prototype = { - // *** Public Properties *** // + // -- Public Properties ---------------------------------------------------- /** A String which is the YQL query. The query will be passed to `buildQuery()` @@ -56,14 +66,13 @@ YQLSync.prototype = { replacement tokens: @example - 'SELECT * FROM flickr.photos.info WHERE photo_id={id}' @property query @type String - @default '' + @default "" **/ - query : '', + query: '', /** A Y.Cache instance to be used to store the YQL query results. You may wish @@ -74,14 +83,14 @@ YQLSync.prototype = { @default undefined **/ - // *** Lifecycle Methods *** // + // -- Lifecycle Methods ---------------------------------------------------- - initializer : function (config) { + initializer: function (config) { config || (config = {}); isValue(config.query) && (this.query = config.query); }, - // *** Public Methods *** // + // -- Public Methods ------------------------------------------------------- /** Returns a processed YQL query by taking the `query` String and substituting @@ -92,10 +101,10 @@ YQLSync.prototype = { @param {Object} [options] Sync options. @return {String} The query to be sent to YQL. **/ - buildQuery : function (options) { + buildQuery: function (options) { options || (options = {}); return sub(this.query, Y.merge(options, - (this instanceof Y.Model) && { id: this.get('id') })); + (this instanceof Y.Model) && {id: this.get('id')})); }, /** @@ -109,7 +118,7 @@ YQLSync.prototype = { @method sync @param {String} action Sync action to perform. May be one of the following: - * read: Load an existing model. + * **read**: Load an existing model. @param {Object} [options] Sync options. @param {callback} [callback] Called when the sync operation finishes. @@ -134,8 +143,8 @@ YQLSync.prototype = { cache = this.cache, results = cache && cache.retrieve(query); + // Return cached results if we got ’em. if (results) { - // return cached results if we got ’em return callback(null, results.response); } @@ -144,9 +153,12 @@ YQLSync.prototype = { callback(r.error, r); } else { results = r.query.results; + + // Cache the results. if (cache && results) { cache.add(query, results); } + callback(null, results); } }); @@ -154,9 +166,9 @@ YQLSync.prototype = { }; -// *** Namespace *** // +// -- Namespace ---------------------------------------------------------------- Y.namespace('ModelSync').YQL = YQLSync; -}, 'gallery-2011.09.07-20-35' ,{requires:['yql'], skinnable:false, optional:['cache', 'cache-offline']}); +}, 'gallery-2012.03.23-18-00' ,{requires:['model', 'yql'], skinnable:false, optional:['cache', 'cache-offline']}); diff --git a/build/gallery-model-sync-yql/gallery-model-sync-yql-min.js b/build/gallery-model-sync-yql/gallery-model-sync-yql-min.js index d6bff34d45..25b03f02e2 100644 --- a/build/gallery-model-sync-yql/gallery-model-sync-yql-min.js +++ b/build/gallery-model-sync-yql/gallery-model-sync-yql-min.js @@ -1 +1 @@ -YUI.add("gallery-model-sync-yql",function(g){var a,d=g.Lang,b=d.sub,f=d.isValue,e=d.isFunction,c=function(){};a=function(){};a.prototype={query:"",initializer:function(h){h||(h={});f(h.query)&&(this.query=h.query);},buildQuery:function(h){h||(h={});return b(this.query,g.merge(h,(this instanceof g.Model)&&{id:this.get("id")}));},sync:function(l,i,m){e(m)||(m=c);if(l!=="read"){return m(null);}var k=this.buildQuery(i),h=this.cache,j=h&&h.retrieve(k);if(j){return m(null,j.response);}g.YQL(k,function(n){if(n.error){m(n.error,n);}else{j=n.query.results;if(h&&j){h.add(k,j);}m(null,j);}});}};g.namespace("ModelSync").YQL=a;},"gallery-2011.09.07-20-35",{requires:["yql"],skinnable:false,optional:["cache","cache-offline"]}); \ No newline at end of file +YUI.add("gallery-model-sync-yql",function(g){var d=g.Lang,b=d.sub,f=d.isValue,e=d.isFunction,c=function(){};function a(){}a._NON_ATTRS_CFG=["query"];a.prototype={query:"",initializer:function(h){h||(h={});f(h.query)&&(this.query=h.query);},buildQuery:function(h){h||(h={});return b(this.query,g.merge(h,(this instanceof g.Model)&&{id:this.get("id")}));},sync:function(l,i,m){e(m)||(m=c);if(l!=="read"){return m(null);}var k=this.buildQuery(i),h=this.cache,j=h&&h.retrieve(k);if(j){return m(null,j.response);}g.YQL(k,function(n){if(n.error){m(n.error,n);}else{j=n.query.results;if(h&&j){h.add(k,j);}m(null,j);}});}};g.namespace("ModelSync").YQL=a;},"gallery-2012.03.23-18-00",{requires:["model","yql"],skinnable:false,optional:["cache","cache-offline"]}); \ No newline at end of file diff --git a/build/gallery-model-sync-yql/gallery-model-sync-yql.js b/build/gallery-model-sync-yql/gallery-model-sync-yql.js index edc0b14b61..6ee57b99a7 100644 --- a/build/gallery-model-sync-yql/gallery-model-sync-yql.js +++ b/build/gallery-model-sync-yql/gallery-model-sync-yql.js @@ -14,40 +14,50 @@ YQL. **Note:** that `read` is the only `sync()` action that is supported at this time, you will not be able to `save()` data to YQL. -@example - var Photo = Y.Base.create('photo', Y.Model, [Y.ModelSync.YQL], { - query : 'SELECT * FROM flickr.photos.info WHERE photo_id={id}', - parse : function (results) { + query: 'SELECT * FROM flickr.photos.info WHERE photo_id={id}', + parse: function (results) { return results && results.photo; } }, { - ATTRS : { - title : {}, - description : {} + ATTRS: { + title : {}, + description: {} } }); @class ModelSync.YQL -@extensionfor Model ModelList +@extensionfor Model +@extensionfor ModelList **/ -var YQLSync, +var Lang = Y.Lang, - Lang = Y.Lang, sub = Lang.sub, isValue = Lang.isValue, isFunction = Lang.isFunction, - noop = function () {}; + noop = function () {}; + +// -- YQLSYnc ------------------------------------------------------------------ -// *** YQLSYnc *** // +function YQLSync() {} -YQLSync = function () {}; +/** +Properties that shouldn't be turned into ad-hoc attributes when passed to a +Model or ModelList constructor. + +@property _NON_ATTRS_CFG +@type Array +@default ['query'] +@static +@protected +**/ +YQLSync._NON_ATTRS_CFG = ['query']; YQLSync.prototype = { - // *** Public Properties *** // + // -- Public Properties ---------------------------------------------------- /** A String which is the YQL query. The query will be passed to `buildQuery()` @@ -56,14 +66,13 @@ YQLSync.prototype = { replacement tokens: @example - 'SELECT * FROM flickr.photos.info WHERE photo_id={id}' @property query @type String - @default '' + @default "" **/ - query : '', + query: '', /** A Y.Cache instance to be used to store the YQL query results. You may wish @@ -74,14 +83,14 @@ YQLSync.prototype = { @default undefined **/ - // *** Lifecycle Methods *** // + // -- Lifecycle Methods ---------------------------------------------------- - initializer : function (config) { + initializer: function (config) { config || (config = {}); isValue(config.query) && (this.query = config.query); }, - // *** Public Methods *** // + // -- Public Methods ------------------------------------------------------- /** Returns a processed YQL query by taking the `query` String and substituting @@ -92,10 +101,10 @@ YQLSync.prototype = { @param {Object} [options] Sync options. @return {String} The query to be sent to YQL. **/ - buildQuery : function (options) { + buildQuery: function (options) { options || (options = {}); return sub(this.query, Y.merge(options, - (this instanceof Y.Model) && { id: this.get('id') })); + (this instanceof Y.Model) && {id: this.get('id')})); }, /** @@ -109,7 +118,7 @@ YQLSync.prototype = { @method sync @param {String} action Sync action to perform. May be one of the following: - * read: Load an existing model. + * **read**: Load an existing model. @param {Object} [options] Sync options. @param {callback} [callback] Called when the sync operation finishes. @@ -134,8 +143,8 @@ YQLSync.prototype = { cache = this.cache, results = cache && cache.retrieve(query); + // Return cached results if we got ’em. if (results) { - // return cached results if we got ’em return callback(null, results.response); } @@ -144,9 +153,12 @@ YQLSync.prototype = { callback(r.error, r); } else { results = r.query.results; + + // Cache the results. if (cache && results) { cache.add(query, results); } + callback(null, results); } }); @@ -154,9 +166,9 @@ YQLSync.prototype = { }; -// *** Namespace *** // +// -- Namespace ---------------------------------------------------------------- Y.namespace('ModelSync').YQL = YQLSync; -}, 'gallery-2011.09.07-20-35' ,{requires:['yql'], skinnable:false, optional:['cache', 'cache-offline']}); +}, 'gallery-2012.03.23-18-00' ,{requires:['model', 'yql'], skinnable:false, optional:['cache', 'cache-offline']}); diff --git a/build/gallery-mru-cache/gallery-mru-cache-debug.js b/build/gallery-mru-cache/gallery-mru-cache-debug.js new file mode 100644 index 0000000000..0231943653 --- /dev/null +++ b/build/gallery-mru-cache/gallery-mru-cache-debug.js @@ -0,0 +1,196 @@ +YUI.add('gallery-mru-cache', function(Y) { + +/********************************************************************** + *

Cache which drops items based on "most recently used." Items are + * dropped when a user-defined criterion is exceeded, e.g., total size or + * number of items.

+ * + *

The items are stored in a map of {data,mru_item_ref}. The MRU items + * are stored in a doubly linked list (which stores the map keys) to allow + * easy re-ordering and dropping of items. Every cache hit moves the + * associated MRU item to the front of the list.

+ * + * @module gallery-mru-cache + * @class MRUCache + * @constructor + * @param config {Object} + *
+ *
metric
+ *
(Required) Function which computes the metric for an item. It receives the value as an argument and must return a positive number.
+ *
limit
+ *
(Required) Maximum allowed value of the metric. Items are dropped off the end of the MRU list until the metric is less than or equal to the limit.
+ *
meta
+ *
Function which attaches meta data to an item when it is added to the cache. It receives the value as an argument.
+ *
stats
+ *
Pass true if you want to collect basic statistics. Pass a function if you want to control what information is stored for each key. The function receives the key, the value, and the stat object.
+ *
+ */ + +function MRUCache(config) +{ + this._metric_fn = config.metric; + this._limit = config.limit; + this._meta = config.meta; + this._stats = config.stats ? initStats() : null; + + if (Y.Lang.isFunction(config.stats)) + { + this._stats_key_meta = config.stats; + } + + this.clear(); +} + +function initStats() +{ + return { gets: 0, keys: {} }; +} + +function initKeyStats(keys, key) +{ + if (!keys[key]) + { + keys[key] = { puts: 0, gets: 0 }; + } +} + +MRUCache.prototype = +{ + /** + * Retrieve a value. + * + * @param key {String} the key of the object to retrieve + * @return {Mixed} the stored object, or undefined if the slot is empty + */ + get: function( + /* string */ key) + { + var obj = this._store[key]; + if (obj) + { + this._mru.prepend(obj.mru); + + if (this._stats) + { + this._stats.gets++; + + initKeyStats(this._stats.keys, key); + this._stats.keys[key].gets++; + } + + return obj.data; + } + }, + + /** + * Store a value. + * + * @param key {String} the key of the value + * @param value {Object} the value to store + * @return {boolean} false if the key has already been used + */ + put: function( + /* string */ key, + /* obj/fn */ value) + { + var exists = !Y.Lang.isUndefined(this._store[key]); + if (exists) + { + return false; + } + + var obj = + { + data: value, + mru: this._mru.prepend(key) + }; + + if (this._meta) + { + obj.meta = this._meta(value); + } + + this._store[key] = obj; + + this._metric += this._metric_fn(value); + while (this._metric > this._limit) + { + this.remove(this._mru.tail().value); + } + + if (this._stats) + { + initKeyStats(this._stats.keys, key); + this._stats.keys[key].puts++; + + if (this._stats_key_meta) + { + this._stats_key_meta(key, value, this._stats.keys[key]); + } + } + + return true; + }, + + /** + * Store a value. + * + * @param key {String} the key of the value + * @param value {Object} the value to store + * @return {Mixed} the original value that was in the slot, or undefined if the slot is empty + */ + replace: function( + /* string */ key, + /* obj/fn */ value) + { + var orig = this.remove(key); + this.put(key, value); + return orig; + }, + + /** + * Remove an value. + * + * @param key {String} the key of the value + * @return {mixed} the value that was removed, or undefined if the slot was empty + */ + remove: function( + /* string */ key) + { + var orig = this._store[key]; + delete this._store[key]; + if (orig) + { + this._mru.remove(orig.mru); + this._metric -= this._metric_fn(orig.data); + return orig.data; + } + }, + + /** + * Remove all values. + */ + clear: function() + { + this._store = {}; + this._mru = new Y.LinkedList(); + this._metric = 0; + }, + + /** + * This resets all the values. + * + * @return {Object} the current stats + */ + dumpStats: function() + { + var stats = this._stats; + this._stats = initStats(); + return stats; + } +}; + +Y.MRUCache = MRUCache; + + +}, 'gallery-2012.03.23-18-00' ,{requires:['gallery-linkedlist']}); diff --git a/build/gallery-mru-cache/gallery-mru-cache-min.js b/build/gallery-mru-cache/gallery-mru-cache-min.js new file mode 100644 index 0000000000..fb289bd730 --- /dev/null +++ b/build/gallery-mru-cache/gallery-mru-cache-min.js @@ -0,0 +1 @@ +YUI.add("gallery-mru-cache",function(d){function b(e){this._metric_fn=e.metric;this._limit=e.limit;this._meta=e.meta;this._stats=e.stats?a():null;if(d.Lang.isFunction(e.stats)){this._stats_key_meta=e.stats;}this.clear();}function a(){return{gets:0,keys:{}};}function c(f,e){if(!f[e]){f[e]={puts:0,gets:0};}}b.prototype={get:function(e){var f=this._store[e];if(f){this._mru.prepend(f.mru);if(this._stats){this._stats.gets++;c(this._stats.keys,e);this._stats.keys[e].gets++;}return f.data;}},put:function(e,g){var f=!d.Lang.isUndefined(this._store[e]);if(f){return false;}var h={data:g,mru:this._mru.prepend(e)};if(this._meta){h.meta=this._meta(g);}this._store[e]=h;this._metric+=this._metric_fn(g);while(this._metric>this._limit){this.remove(this._mru.tail().value);}if(this._stats){c(this._stats.keys,e);this._stats.keys[e].puts++;if(this._stats_key_meta){this._stats_key_meta(e,g,this._stats.keys[e]);}}return true;},replace:function(e,f){var g=this.remove(e);this.put(e,f);return g;},remove:function(e){var f=this._store[e];delete this._store[e];if(f){this._mru.remove(f.mru);this._metric-=this._metric_fn(f.data);return f.data;}},clear:function(){this._store={};this._mru=new d.LinkedList();this._metric=0;},dumpStats:function(){var e=this._stats;this._stats=a();return e;}};d.MRUCache=b;},"gallery-2012.03.23-18-00",{requires:["gallery-linkedlist"]}); \ No newline at end of file diff --git a/build/gallery-mru-cache/gallery-mru-cache.js b/build/gallery-mru-cache/gallery-mru-cache.js new file mode 100644 index 0000000000..0231943653 --- /dev/null +++ b/build/gallery-mru-cache/gallery-mru-cache.js @@ -0,0 +1,196 @@ +YUI.add('gallery-mru-cache', function(Y) { + +/********************************************************************** + *

Cache which drops items based on "most recently used." Items are + * dropped when a user-defined criterion is exceeded, e.g., total size or + * number of items.

+ * + *

The items are stored in a map of {data,mru_item_ref}. The MRU items + * are stored in a doubly linked list (which stores the map keys) to allow + * easy re-ordering and dropping of items. Every cache hit moves the + * associated MRU item to the front of the list.

+ * + * @module gallery-mru-cache + * @class MRUCache + * @constructor + * @param config {Object} + *
+ *
metric
+ *
(Required) Function which computes the metric for an item. It receives the value as an argument and must return a positive number.
+ *
limit
+ *
(Required) Maximum allowed value of the metric. Items are dropped off the end of the MRU list until the metric is less than or equal to the limit.
+ *
meta
+ *
Function which attaches meta data to an item when it is added to the cache. It receives the value as an argument.
+ *
stats
+ *
Pass true if you want to collect basic statistics. Pass a function if you want to control what information is stored for each key. The function receives the key, the value, and the stat object.
+ *
+ */ + +function MRUCache(config) +{ + this._metric_fn = config.metric; + this._limit = config.limit; + this._meta = config.meta; + this._stats = config.stats ? initStats() : null; + + if (Y.Lang.isFunction(config.stats)) + { + this._stats_key_meta = config.stats; + } + + this.clear(); +} + +function initStats() +{ + return { gets: 0, keys: {} }; +} + +function initKeyStats(keys, key) +{ + if (!keys[key]) + { + keys[key] = { puts: 0, gets: 0 }; + } +} + +MRUCache.prototype = +{ + /** + * Retrieve a value. + * + * @param key {String} the key of the object to retrieve + * @return {Mixed} the stored object, or undefined if the slot is empty + */ + get: function( + /* string */ key) + { + var obj = this._store[key]; + if (obj) + { + this._mru.prepend(obj.mru); + + if (this._stats) + { + this._stats.gets++; + + initKeyStats(this._stats.keys, key); + this._stats.keys[key].gets++; + } + + return obj.data; + } + }, + + /** + * Store a value. + * + * @param key {String} the key of the value + * @param value {Object} the value to store + * @return {boolean} false if the key has already been used + */ + put: function( + /* string */ key, + /* obj/fn */ value) + { + var exists = !Y.Lang.isUndefined(this._store[key]); + if (exists) + { + return false; + } + + var obj = + { + data: value, + mru: this._mru.prepend(key) + }; + + if (this._meta) + { + obj.meta = this._meta(value); + } + + this._store[key] = obj; + + this._metric += this._metric_fn(value); + while (this._metric > this._limit) + { + this.remove(this._mru.tail().value); + } + + if (this._stats) + { + initKeyStats(this._stats.keys, key); + this._stats.keys[key].puts++; + + if (this._stats_key_meta) + { + this._stats_key_meta(key, value, this._stats.keys[key]); + } + } + + return true; + }, + + /** + * Store a value. + * + * @param key {String} the key of the value + * @param value {Object} the value to store + * @return {Mixed} the original value that was in the slot, or undefined if the slot is empty + */ + replace: function( + /* string */ key, + /* obj/fn */ value) + { + var orig = this.remove(key); + this.put(key, value); + return orig; + }, + + /** + * Remove an value. + * + * @param key {String} the key of the value + * @return {mixed} the value that was removed, or undefined if the slot was empty + */ + remove: function( + /* string */ key) + { + var orig = this._store[key]; + delete this._store[key]; + if (orig) + { + this._mru.remove(orig.mru); + this._metric -= this._metric_fn(orig.data); + return orig.data; + } + }, + + /** + * Remove all values. + */ + clear: function() + { + this._store = {}; + this._mru = new Y.LinkedList(); + this._metric = 0; + }, + + /** + * This resets all the values. + * + * @return {Object} the current stats + */ + dumpStats: function() + { + var stats = this._stats; + this._stats = initStats(); + return stats; + } +}; + +Y.MRUCache = MRUCache; + + +}, 'gallery-2012.03.23-18-00' ,{requires:['gallery-linkedlist']}); diff --git a/build/gallery-node-optimizations/gallery-node-optimizations-debug.js b/build/gallery-node-optimizations/gallery-node-optimizations-debug.js index a27984071e..8f90b1ba55 100644 --- a/build/gallery-node-optimizations/gallery-node-optimizations-debug.js +++ b/build/gallery-node-optimizations/gallery-node-optimizations-debug.js @@ -14,6 +14,24 @@ var tag_class_name_re = /^([a-z]*)\.([-_a-z0-9]+)$/i; var class_name_re = /^\.([-_a-z0-9]+)$/i; var tag_name_re = /^[a-z]+$/i; +/** + * Useful when constructing regular expressions that match CSS classes. + * + * @property Y.Node.class_re_prefix + * @type {String} + * @value "(?:^|\\s)(?:" + */ +Y.Node.class_re_prefix = '(?:^|\\s)(?:'; + +/** + * Useful when constructing regular expressions that match CSS classes. + * + * @property Y.Node.class_re_suffix + * @type {String} + * @value ")(?:\\s|$)" + */ +Y.Node.class_re_suffix = ')(?:\\s|$)'; + /********************************************************************** *

Patch to speed up search for a single class name or single tag name. * To use a regular expression, call getAncestorByClassName().

@@ -35,13 +53,13 @@ Y.Node.prototype.ancestor = function( var m = class_name_re.exec(fn); if (m && m.length) { - Y.log('ancestor() calling getAncestorByClassName() with ' + m[1], 'info', 'Node'); + Y.log('ancestor() calling getAncestorByClassName() with ' + m[1], 'debug', 'Node'); return this.getAncestorByClassName(m[1], test_self); } if (tag_name_re.test(fn)) { - Y.log('ancestor() calling getAncestorByTagName() with ' + fn, 'info', 'Node'); + Y.log('ancestor() calling getAncestorByTagName() with ' + fn, 'debug', 'Node'); return this.getAncestorByTagName(fn, test_self); } } @@ -129,16 +147,22 @@ Y.Node.prototype.one = function( { if (Y.Lang.isString(selector)) { + if (selector == '*') + { + Y.log('one() returning children[0]', 'debug', 'Node'); + return Y.one(Y.Node.getDOMNode(this).children[0]); + } + var m = tag_class_name_re.exec(selector); if (m && m.length) { - Y.log('one() calling getElementsByClassName() with ' + m[2] + ',' + m[1], 'info', 'Node'); + Y.log('one() calling getElementsByClassName() with ' + m[2] + ',' + m[1], 'debug', 'Node'); return this.getFirstElementByClassName(m[2], m[1]); } if (tag_name_re.test(selector)) { - Y.log('one() calling getElementsByTagName() with ' + selector, 'info', 'Node'); + Y.log('one() calling getElementsByTagName() with ' + selector, 'debug', 'Node'); return this.getElementsByTagName(selector).item(0); } } @@ -165,13 +189,13 @@ Y.Node.prototype.all = function( var m = tag_class_name_re.exec(selector); if (m && m.length) { - Y.log('all() calling getElementsByClassName() with ' + m[2] + ',' + m[1], 'info', 'Node'); + Y.log('all() calling getElementsByClassName() with ' + m[2] + ',' + m[1], 'debug', 'Node'); return this.getElementsByClassName(m[2], m[1]); } if (tag_name_re.test(selector)) { - Y.log('all() calling getElementsByTagName() with ' + selector, 'info', 'Node'); + Y.log('all() calling getElementsByTagName() with ' + selector, 'debug', 'Node'); return this.getElementsByTagName(selector); } } @@ -193,14 +217,13 @@ Y.Node.prototype.getElementsByClassName = function( /* string */ class_name, /* string */ tag_name) { - var descendants = this.getElementsByTagName(tag_name || '*'); + var descendants = Y.Node.getDOMNode(this).getElementsByTagName(tag_name || '*'); - var list = new Y.NodeList(); - var count = descendants.size(); - for (var i=0; iSearches for one descendant by class name. Unlike Y.one(), this - * function accepts a regular expression.

+ * function accepts a regular expression.

* * @method getFirstElementByClassName * @param class_name {String|Regexp} class to search for @@ -223,15 +246,43 @@ Y.Node.prototype.getFirstElementByClassName = function( /* string */ class_name, /* string */ tag_name) { - var descendants = this.getElementsByTagName(tag_name || '*'); + if (!tag_name || tag_name == '*' || tag_name == 'div') + { + // breadth first search - var count = descendants.size(); - for (var i=0; iPatch to speed up search for a single class name or single tag name. * To use a regular expression, call getAncestorByClassName().

@@ -127,6 +145,11 @@ Y.Node.prototype.one = function( { if (Y.Lang.isString(selector)) { + if (selector == '*') + { + return Y.one(Y.Node.getDOMNode(this).children[0]); + } + var m = tag_class_name_re.exec(selector); if (m && m.length) { @@ -187,14 +210,13 @@ Y.Node.prototype.getElementsByClassName = function( /* string */ class_name, /* string */ tag_name) { - var descendants = this.getElementsByTagName(tag_name || '*'); + var descendants = Y.Node.getDOMNode(this).getElementsByTagName(tag_name || '*'); - var list = new Y.NodeList(); - var count = descendants.size(); - for (var i=0; iSearches for one descendant by class name. Unlike Y.one(), this - * function accepts a regular expression.

+ * function accepts a regular expression.

* * @method getFirstElementByClassName * @param class_name {String|Regexp} class to search for @@ -217,15 +239,43 @@ Y.Node.prototype.getFirstElementByClassName = function( /* string */ class_name, /* string */ tag_name) { - var descendants = this.getElementsByTagName(tag_name || '*'); + if (!tag_name || tag_name == '*' || tag_name == 'div') + { + // breadth first search - var count = descendants.size(); - for (var i=0; i @@ -18,8 +22,8 @@ http://developer.yahoo.net/yui/license.txt *
  • totalRecords : n (int or Paginator.VALUE_UNLIMITED)
  • * * - * @module gallery-paginator * @class Paginator + * @extends Widget * @constructor * @param config {Object} Object literal to set instance and ui component * configuration. @@ -85,7 +89,7 @@ Y.mix(Paginator, { * * @method Paginator.isNumeric * @param v {Number|String} value to be checked for number or numeric string - * @returns {Boolean} true if the input is coercable into a finite number + * @return {Boolean} true if the input is coercable into a finite number * @static */ isNumeric : function (v) { @@ -865,7 +869,6 @@ http://developer.yahoo.net/yui/license.txt /** * Generates an input field for setting the current page. * - * @module gallery-paginator * @class Paginator.ui.CurrentPageInput * @constructor * @param p {Pagintor} Paginator instance to attach to @@ -990,7 +993,6 @@ http://developer.yahoo.net/yui/license.txt * ui Component to generate the textual report of current pagination status. * E.g. "Now viewing page 1 of 13". * - * @module gallery-paginator * @class Paginator.ui.CurrentPageReport * @constructor * @param p {Pagintor} Paginator instance to attach to @@ -1147,7 +1149,6 @@ http://developer.yahoo.net/yui/license.txt /** * ui Component to generate the link to jump to the first page. * - * @module gallery-paginator * @class Paginator.ui.FirstPageLink * @constructor * @param p {Pagintor} Paginator instance to attach to @@ -1314,7 +1315,6 @@ http://developer.yahoo.net/yui/license.txt /** * ui Component to display a menu for selecting the range of items to display. * - * @module gallery-paginator * @class Paginator.ui.ItemRangeDropdown * @constructor * @param p {Pagintor} Paginator instance to attach to @@ -1459,7 +1459,6 @@ http://developer.yahoo.net/yui/license.txt /** * ui Component to generate the link to jump to the last page. * - * @module gallery-paginator * @class Paginator.ui.LastPageLink * @constructor * @param p {Pagintor} Paginator instance to attach to @@ -1472,8 +1471,8 @@ Paginator.ui.LastPageLink = function (p) { p.after('rowsPerPageChange',this.update,this); p.after('totalRecordsChange',this.update,this); - p.after('lastPageLinkClassChange', this.rebuild, this); - p.after('lastPageLinkLabelChange', this.rebuild, this); + p.after('lastPageLinkClassChange', this.rebuild, this); + p.after('lastPageLinkLabelChange', this.rebuild, this); }; /** @@ -1658,7 +1657,6 @@ http://developer.yahoo.net/yui/license.txt /** * ui Component to generate the link to jump to the next page. * - * @module gallery-paginator * @class Paginator.ui.NextPageLink * @constructor * @param p {Pagintor} Paginator instance to attach to @@ -1671,8 +1669,8 @@ Paginator.ui.NextPageLink = function (p) { p.after('rowsPerPageChange', this.update,this); p.after('totalRecordsChange', this.update,this); - p.after('nextPageLinkClassChange', this.rebuild, this); - p.after('nextPageLinkLabelChange', this.rebuild, this); + p.after('nextPageLinkClassChange', this.rebuild, this); + p.after('nextPageLinkLabelChange', this.rebuild, this); }; /** @@ -1828,7 +1826,6 @@ http://developer.yahoo.net/yui/license.txt /** * ui Component to generate the page links * - * @module gallery-paginator * @class Paginator.ui.PageLinks * @constructor * @param p {Pagintor} Paginator instance to attach to @@ -2077,7 +2074,6 @@ http://developer.yahoo.net/yui/license.txt /** * ui Component to generate the link to jump to the previous page. * - * @module gallery-paginator * @class Paginator.ui.PreviousPageLink * @constructor * @param p {Pagintor} Paginator instance to attach to @@ -2222,7 +2218,6 @@ http://developer.yahoo.net/yui/license.txt /** * ui Component to generate the rows-per-page dropdown * - * @module gallery-paginator * @class Paginator.ui.RowsPerPageDropdown * @constructor * @param p {Pagintor} Paginator instance to attach to @@ -2413,7 +2408,6 @@ Paginator.ui.RowsPerPageDropdown.prototype = { /********************************************************************** * Adds per-page error notification to Paginator.ui.PageLinks. * - * @module gallery-paginator * @class Paginator.ui.ValidationPageLinks * @constructor * @param p {Pagintor} Paginator instance to attach to @@ -2424,7 +2418,7 @@ Paginator.ui.ValidationPageLinks = function( { Paginator.ui.ValidationPageLinks.superclass.constructor.call(this, p); - p.after('pageStatusChange', this.rebuild, this); + p.after('pageStatusChange', this.rebuild, this); }; var vpl_status_prefix = 'yui3-has'; @@ -2433,7 +2427,7 @@ var vpl_status_prefix = 'yui3-has'; * Array of status strings for each page. If the status value for a page * is not empty, it is used to build a CSS class for the page: * yui3-has<status> - * + * * @attribute pageStatus */ Paginator.ATTRS.pageStatus = @@ -2442,8 +2436,8 @@ Paginator.ATTRS.pageStatus = validator: Y.Lang.isArray }; -Y.extend(Paginator.ui.ValidationPageLinks, Paginator.ui.PageLinks, -{ +Y.extend(Paginator.ui.ValidationPageLinks, Paginator.ui.PageLinks, +{ update: function(e) { if (e && e.prevVal === e.newVal) @@ -2482,8 +2476,8 @@ Y.extend(Paginator.ui.ValidationPageLinks, Paginator.ui.PageLinks, this.container.set('innerHTML', content); } } - + }); -}, 'gallery-2011.04.13-22-38' ,{requires:['widget','event-key','substitute'], skinnable:true}); +}, 'gallery-2012.03.23-18-00' ,{requires:['widget','event-key','substitute'], skinnable:true}); diff --git a/build/gallery-paginator/gallery-paginator-min.js b/build/gallery-paginator/gallery-paginator-min.js index 5181c98411..3338576c95 100644 --- a/build/gallery-paginator/gallery-paginator-min.js +++ b/build/gallery-paginator/gallery-paginator-min.js @@ -2,4 +2,4 @@ YUI.add("gallery-paginator",function(b){function c(d){c.superclass.constructor.c if(e.page&&e.recordOffset===undefined){e.recordOffset=(e.page-1)*(e.rowsPerPage||this.get("rowsPerPage"));}this._batch=true;this._pageChanged=false;for(var d in e){if(e.hasOwnProperty(d)&&this._configs.hasOwnProperty(d)){this.set(d,e[d]);}}this._batch=false;if(this._pageChanged){this._pageChanged=false;this._firePageChange(this.getState(this._state));}}},_syncRecordOffset:function(h){var d=h.newValue,g,f;if(h.prevValue!==d){if(d!==c.VALUE_UNLIMITED){g=this.get("rowsPerPage");if(g&&this.get("recordOffset")>=d){f=this.getState({totalRecords:h.prevValue,recordOffset:this.get("recordOffset")});this.set("recordOffset",f.before.recordOffset);this._firePageChange(f);}}}},_handleStateChange:function(f){if(f.prevValue!==f.newValue){var g=this._state||{},d;g[f.type.replace(/Change$/,"")]=f.prevValue;d=this.getState(g);if(d.page!==d.before.page){if(this._batch){this._pageChanged=true;}else{this._firePageChange(d);}}}},_firePageChange:function(d){if(b.Lang.isObject(d)){var e=d.before;delete d.before;this.fire("pageChange",{type:"pageChange",prevValue:d.page,newValue:e.page,prevState:d,newState:e});}}});b.Paginator=c;c.ui.CurrentPageInput=function(d){this.paginator=d;d.on("destroy",this.destroy,this);d.after("recordOffsetChange",this.update,this);d.after("rowsPerPageChange",this.update,this);d.after("totalRecordsChange",this.update,this);d.after("pageInputClassChange",this.update,this);};c.ATTRS.pageInputClass={value:b.ClassNameManager.getClassName(c.NAME,"page-input"),validator:b.Lang.isString};c.ATTRS.pageInputTemplate={value:"{currentPage} of {totalPages}",validator:b.Lang.isString};c.ui.CurrentPageInput.prototype={destroy:function(){this.span.remove(true);this.span=null;this.input=null;this.page_count=null;},render:function(d){this.span=b.Node.create(''+b.substitute(this.paginator.get("pageInputTemplate"),{currentPage:'',totalPages:''})+"");this.span.set("className",this.paginator.get("pageInputClass"));this.input=this.span.one("input");this.input.on("change",this._onChange,this);this.input.on("key",this._onReturnKey,"down:13",this);this.page_count=this.span.one("span.yui-page-count");this.update();return this.span;},update:function(d){if(d&&d.prevVal===d.newVal){return;}this.span.set("className",this.paginator.get("pageInputClass"));this.input.set("value",this.paginator.getCurrentPage());this.page_count.set("innerHTML",this.paginator.getTotalPages());},_onChange:function(d){this.paginator.setPage(parseInt(this.input.get("value"),10));},_onReturnKey:function(d){d.halt(true);this.paginator.setPage(parseInt(this.input.get("value"),10));}};c.ui.CurrentPageReport=function(d){this.paginator=d;d.on("destroy",this.destroy,this);d.after("recordOffsetChange",this.update,this);d.after("rowsPerPageChange",this.update,this);d.after("totalRecordsChange",this.update,this);d.after("pageReportClassChange",this.update,this);d.after("pageReportTemplateChange",this.update,this);};c.ATTRS.pageReportClass={value:b.ClassNameManager.getClassName(c.NAME,"current"),validator:b.Lang.isString};c.ATTRS.pageReportTemplate={value:"({currentPage} of {totalPages})",validator:b.Lang.isString};c.ATTRS.pageReportValueGenerator={value:function(f){var e=f.getCurrentPage(),d=f.getPageRecords();return{"currentPage":d?e:0,"totalPages":f.getTotalPages(),"startIndex":d?d[0]:0,"endIndex":d?d[1]:0,"startRecord":d?d[0]+1:0,"endRecord":d?d[1]+1:0,"totalRecords":f.get("totalRecords")};},validator:b.Lang.isFunction};c.ui.CurrentPageReport.sprintf=function(e,d){return e.replace(/\{([\w\s\-]+)\}/g,function(f,g){return(g in d)?d[g]:"";});};c.ui.CurrentPageReport.prototype={span:null,destroy:function(){this.span.remove(true);this.span=null;},render:function(d){this.span=b.Node.create('');this.span.set("className",this.paginator.get("pageReportClass"));this.update();return this.span;},update:function(d){if(d&&d.prevVal===d.newVal){return;}this.span.set("className",this.paginator.get("pageReportClass"));this.span.set("innerHTML",c.ui.CurrentPageReport.sprintf(this.paginator.get("pageReportTemplate"),this.paginator.get("pageReportValueGenerator")(this.paginator)));}};c.ui.FirstPageLink=function(d){this.paginator=d;d.on("destroy",this.destroy,this);d.after("recordOffsetChange",this.update,this);d.after("rowsPerPageChange",this.update,this);d.after("totalRecordsChange",this.update,this);d.after("firstPageLinkLabelChange",this.rebuild,this);d.after("firstPageLinkClassChange",this.rebuild,this);};c.ATTRS.firstPageLinkLabel={value:"<< first",validator:b.Lang.isString};c.ATTRS.firstPageLinkClass={value:b.ClassNameManager.getClassName(c.NAME,"first"),validator:b.Lang.isString};c.ui.FirstPageLink.prototype={current:null,link:null,span:null,destroy:function(){this.link.remove(true);this.span.remove(true);this.current=this.link=this.span=null;},render:function(e){var f=this.paginator,g=f.get("firstPageLinkClass"),d=f.get("firstPageLinkLabel");this.link=b.Node.create(''+d+"");this.link.set("className",g);this.link.on("click",this.onClick,this);this.span=b.Node.create(''+d+"");this.span.set("className",g);this.current=f.getCurrentPage()>1?this.link:this.span;return this.current;},update:function(f){if(f&&f.prevVal===f.newVal){return;}var d=this.current?this.current.get("parentNode"):null;if(this.paginator.getCurrentPage()>1){if(d&&this.current===this.span){d.replaceChild(this.link,this.current);this.current=this.link;}}else{if(d&&this.current===this.link){d.replaceChild(this.span,this.current);this.current=this.span;}}},rebuild:function(g){if(g&&g.prevVal===g.newVal){return;}var f=this.paginator,h=f.get("firstPageLinkClass"),d=f.get("firstPageLinkLabel");this.link.set("className",h);this.link.set("innerHTML",d);this.span.set("className",h);this.span.set("innerHTML",d);},onClick:function(d){d.halt();this.paginator.setPage(1);}};c.ui.ItemRangeDropdown=function(d){this.paginator=d; d.on("destroy",this.destroy,this);d.after("recordOffsetChange",this.update,this);d.after("rowsPerPageChange",this.update,this);d.after("totalRecordsChange",this.update,this);d.after("itemRangeDropdownClassChange",this.update,this);};c.ATTRS.itemRangeDropdownClass={value:b.ClassNameManager.getClassName(c.NAME,"ir-dropdown"),validator:b.Lang.isString};c.ATTRS.itemRangeDropdownTemplate={value:"{currentRange} of {totalItems}",validator:b.Lang.isString};c.ui.ItemRangeDropdown.prototype={destroy:function(){this.span.remove(true);this.span=null;this.menu=null;this.page_count=null;},render:function(d){this.span=b.Node.create(''+b.substitute(this.paginator.get("itemRangeDropdownTemplate"),{currentRange:'',totalItems:''})+"");this.span.set("className",this.paginator.get("itemRangeDropdownClass"));this.menu=this.span.one("select");this.menu.on("change",this._onChange,this);this.page_count=this.span.one("span.yui-item-count");this.prev_page_count=-1;this.prev_page_size=-1;this.prev_rec_count=-1;this.update();return this.span;},update:function(m){if(m&&m.prevVal===m.newVal){return;}var l=this.paginator.getCurrentPage();var j=this.paginator.getTotalPages();var h=this.paginator.getRowsPerPage();var k=this.paginator.getTotalRecords();if(j!=this.prev_page_count||h!=this.prev_page_size||k!=this.prev_rec_count){var f=b.Node.getDOMNode(this.menu).options;f.length=0;for(var g=1;g<=j;g++){var d=this.paginator.getPageRecords(g);f[g-1]=new Option((d[0]+1)+" - "+(d[1]+1),g);}this.page_count.set("innerHTML",k);this.prev_page_count=j;this.prev_page_size=h;this.prev_rec_count=k;}this.span.set("className",this.paginator.get("itemRangeDropdownClass"));this.menu.set("selectedIndex",l-1);},_onChange:function(d){this.paginator.setPage(parseInt(this.menu.get("value"),10));}};c.ui.LastPageLink=function(d){this.paginator=d;d.on("destroy",this.destroy,this);d.after("recordOffsetChange",this.update,this);d.after("rowsPerPageChange",this.update,this);d.after("totalRecordsChange",this.update,this);d.after("lastPageLinkClassChange",this.rebuild,this);d.after("lastPageLinkLabelChange",this.rebuild,this);};c.ATTRS.lastPageLinkClass={value:b.ClassNameManager.getClassName(c.NAME,"last"),validator:b.Lang.isString};c.ATTRS.lastPageLinkLabel={value:"last >>",validator:b.Lang.isString};c.ui.LastPageLink.prototype={current:null,link:null,span:null,na:null,destroy:function(){this.link.remove(true);this.span.remove(true);this.na.remove(true);this.current=this.link=this.span=this.na=null;},render:function(e){var g=this.paginator,h=g.get("lastPageLinkClass"),d=g.get("lastPageLinkLabel"),f=g.getTotalPages();this.link=b.Node.create(''+d+"");this.link.set("className",h);this.link.on("click",this.onClick,this);this.span=b.Node.create(''+d+"");this.span.set("className",h);this.na=b.Node.create('');switch(f){case c.VALUE_UNLIMITED:this.current=this.na;break;case g.getCurrentPage():this.current=this.span;break;default:this.current=this.link;}return this.current;},update:function(f){if(f&&f.prevVal===f.newVal){return;}var d=this.current?this.current.get("parentNode"):null,g=this.link;if(d){switch(this.paginator.getTotalPages()){case c.VALUE_UNLIMITED:g=this.na;break;case this.paginator.getCurrentPage():g=this.span;break;}if(this.current!==g){d.replaceChild(g,this.current);this.current=g;}}},rebuild:function(g){if(g&&g.prevVal===g.newVal){return;}var f=this.paginator,h=f.get("lastPageLinkClass"),d=f.get("lastPageLinkLabel");this.link.set("className",h);this.link.set("innerHTML",d);this.span.set("className",h);this.span.set("innerHTML",d);},onClick:function(d){d.halt();this.paginator.setPage(this.paginator.getTotalPages());}};c.ui.NextPageLink=function(d){this.paginator=d;d.on("destroy",this.destroy,this);d.after("recordOffsetChange",this.update,this);d.after("rowsPerPageChange",this.update,this);d.after("totalRecordsChange",this.update,this);d.after("nextPageLinkClassChange",this.rebuild,this);d.after("nextPageLinkLabelChange",this.rebuild,this);};c.ATTRS.nextPageLinkClass={value:b.ClassNameManager.getClassName(c.NAME,"next"),validator:b.Lang.isString};c.ATTRS.nextPageLinkLabel={value:"next >",validator:b.Lang.isString};c.ui.NextPageLink.prototype={current:null,link:null,span:null,destroy:function(){this.link.remove(true);this.span.remove(true);this.current=this.link=this.span=null;},render:function(e){var g=this.paginator,h=g.get("nextPageLinkClass"),d=g.get("nextPageLinkLabel"),f=g.getTotalPages();this.link=b.Node.create(''+d+"");this.link.set("className",h);this.link.on("click",this.onClick,this);this.span=b.Node.create(''+d+"");this.span.set("className",h);this.current=g.getCurrentPage()===f?this.span:this.link;return this.current;},update:function(g){if(g&&g.prevVal===g.newVal){return;}var f=this.paginator.getTotalPages(),d=this.current?this.current.get("parentNode"):null;if(this.paginator.getCurrentPage()!==f){if(d&&this.current===this.span){d.replaceChild(this.link,this.current);this.current=this.link;}}else{if(this.current===this.link){if(d){d.replaceChild(this.span,this.current);this.current=this.span;}}}},rebuild:function(g){if(g&&g.prevVal===g.newVal){return;}var f=this.paginator,h=f.get("nextPageLinkClass"),d=f.get("nextPageLinkLabel");this.link.set("className",h);this.link.set("innerHTML",d);this.span.set("className",h);this.span.set("innerHTML",d);},onClick:function(d){d.halt();this.paginator.setPage(this.paginator.getNextPage());}};c.ui.PageLinks=function(d){this.paginator=d;d.on("destroy",this.destroy,this);d.after("recordOffsetChange",this.update,this);d.after("rowsPerPageChange",this.update,this);d.after("totalRecordsChange",this.update,this);d.after("pageLinksContainerClassChange",this.rebuild,this);d.after("pageLinkClassChange",this.rebuild,this); d.after("currentPageClassChange",this.rebuild,this);d.after("pageLinksChange",this.rebuild,this);};c.ATTRS.pageLinksContainerClass={value:b.ClassNameManager.getClassName(c.NAME,"pages"),validator:b.Lang.isString};c.ATTRS.pageLinkClass={value:b.ClassNameManager.getClassName(c.NAME,"page"),validator:b.Lang.isString};c.ATTRS.currentPageClass={value:b.ClassNameManager.getClassName(c.NAME,"current-page"),validator:b.Lang.isString};c.ATTRS.pageLinks={value:10,validator:c.isNumeric};c.ATTRS.pageLabelBuilder={value:function(d,e){return d;},validator:b.Lang.isFunction};c.ui.PageLinks.calculateRange=function(f,g,e){var j=c.VALUE_UNLIMITED,i,d,h;if(!f||e===0||g===0||(g===j&&e===j)){return[0,-1];}if(g!==j){e=e===j?g:Math.min(e,g);}i=Math.max(1,Math.ceil(f-(e/2)));if(g===j){d=i+e-1;}else{d=Math.min(g,i+e-1);}h=e-(d-i+1);i=Math.max(1,i-h);return[i,d];};c.ui.PageLinks.prototype={current:0,container:null,destroy:function(){this.container.remove(true);this.container=null;},render:function(d){this.container=b.Node.create('');this.container.on("click",this.onClick,this);this.update({newVal:null,rebuild:true});return this.container;},update:function(m){if(m&&m.prevVal===m.newVal){return;}var g=this.paginator,l=g.getCurrentPage();if(this.current!==l||!l||m.rebuild){var o=g.get("pageLabelBuilder"),k=c.ui.PageLinks.calculateRange(l,g.getTotalPages(),g.get("pageLinks")),f=k[0],h=k[1],n="",d,j;d=''+o(j,g)+"";}else{n+=d+j+'">'+o(j,g)+"";}}this.container.set("className",g.get("pageLinksContainerClass"));this.container.set("innerHTML",n);}},rebuild:function(d){d.rebuild=true;this.update(d);},onClick:function(f){var d=f.target;if(d&&d.hasClass(this.paginator.get("pageLinkClass"))){f.halt();this.paginator.setPage(parseInt(d.getAttribute("page"),10));}}};c.ui.PreviousPageLink=function(d){this.paginator=d;d.on("destroy",this.destroy,this);d.after("recordOffsetChange",this.update,this);d.after("rowsPerPageChange",this.update,this);d.after("totalRecordsChange",this.update,this);d.after("previousPageLinkLabelChange",this.update,this);d.after("previousPageLinkClassChange",this.update,this);};c.ATTRS.previousPageLinkClass={value:b.ClassNameManager.getClassName(c.NAME,"previous"),validator:b.Lang.isString};c.ATTRS.previousPageLinkLabel={value:"< prev",validator:b.Lang.isString};c.ui.PreviousPageLink.prototype={current:null,link:null,span:null,destroy:function(){this.link.remove(true);this.span.remove(true);this.current=this.link=this.span=null;},render:function(e){var f=this.paginator,g=f.get("previousPageLinkClass"),d=f.get("previousPageLinkLabel");this.link=b.Node.create(''+d+"");this.link.set("className",g);this.link.on("click",this.onClick,this);this.span=b.Node.create(''+d+"");this.span.set("className",g);this.current=f.getCurrentPage()>1?this.link:this.span;return this.current;},update:function(f){if(f&&f.prevVal===f.newVal){return;}var d=this.current?this.current.get("parentNode"):null;if(this.paginator.getCurrentPage()>1){if(d&&this.current===this.span){d.replaceChild(this.link,this.current);this.current=this.link;}}else{if(d&&this.current===this.link){d.replaceChild(this.span,this.current);this.current=this.span;}}},onClick:function(d){d.halt();this.paginator.setPage(this.paginator.getPreviousPage());}};c.ui.RowsPerPageDropdown=function(d){this.paginator=d;d.on("destroy",this.destroy,this);d.after("rowsPerPageChange",this.update,this);d.after("totalRecordsChange",this._handleTotalRecordsChange,this);d.after("rowsPerPageDropdownClassChange",this.rebuild,this);d.after("rowsPerPageDropdownTitleChange",this.rebuild,this);d.after("rowsPerPageOptionsChange",this.rebuild,this);};c.ATTRS.rowsPerPageDropdownClass={value:b.ClassNameManager.getClassName(c.NAME,"rpp-options"),validator:b.Lang.isString};c.ATTRS.rowsPerPageDropdownTitle={value:"Rows per page",validator:b.Lang.isString};c.ATTRS.rowsPerPageOptions={value:[],validator:b.Lang.isArray};c.ui.RowsPerPageDropdown.prototype={select:null,all:null,destroy:function(){this.select.remove(true);this.all=this.select=null;},render:function(d){this.select=b.Node.create('');this.select.on("change",this.onChange,this);this.rebuild();return this.select;},rebuild:function(n){var f=this.paginator,h=this.select,o=f.get("rowsPerPageOptions"),d=b.Node.getDOMNode(h).options,g,m,j,k,l;this.all=null;h.set("className",this.paginator.get("rowsPerPageDropdownClass"));h.set("title",this.paginator.get("rowsPerPageDropdownTitle"));for(k=0,l=o.length;k"));j=b.Lang.isValue(m.value)?m.value:m;g.set("innerHTML",b.Lang.isValue(m.text)?m.text:m);if(b.Lang.isString(j)&&j.toLowerCase()==="all"){this.all=g;g.set("value",f.get("totalRecords"));}else{g.set("value",j);}}while(d.length>o.length){h.get("lastChild").remove();}this.update();},update:function(j){if(j&&j.prevVal===j.newVal){return;}var h=this.paginator.get("rowsPerPage")+"",f=b.Node.getDOMNode(this.select).options,g,d;for(g=0,d=f.length;g @@ -18,8 +22,8 @@ http://developer.yahoo.net/yui/license.txt *
  • totalRecords : n (int or Paginator.VALUE_UNLIMITED)
  • * * - * @module gallery-paginator * @class Paginator + * @extends Widget * @constructor * @param config {Object} Object literal to set instance and ui component * configuration. @@ -85,7 +89,7 @@ Y.mix(Paginator, { * * @method Paginator.isNumeric * @param v {Number|String} value to be checked for number or numeric string - * @returns {Boolean} true if the input is coercable into a finite number + * @return {Boolean} true if the input is coercable into a finite number * @static */ isNumeric : function (v) { @@ -865,7 +869,6 @@ http://developer.yahoo.net/yui/license.txt /** * Generates an input field for setting the current page. * - * @module gallery-paginator * @class Paginator.ui.CurrentPageInput * @constructor * @param p {Pagintor} Paginator instance to attach to @@ -990,7 +993,6 @@ http://developer.yahoo.net/yui/license.txt * ui Component to generate the textual report of current pagination status. * E.g. "Now viewing page 1 of 13". * - * @module gallery-paginator * @class Paginator.ui.CurrentPageReport * @constructor * @param p {Pagintor} Paginator instance to attach to @@ -1147,7 +1149,6 @@ http://developer.yahoo.net/yui/license.txt /** * ui Component to generate the link to jump to the first page. * - * @module gallery-paginator * @class Paginator.ui.FirstPageLink * @constructor * @param p {Pagintor} Paginator instance to attach to @@ -1314,7 +1315,6 @@ http://developer.yahoo.net/yui/license.txt /** * ui Component to display a menu for selecting the range of items to display. * - * @module gallery-paginator * @class Paginator.ui.ItemRangeDropdown * @constructor * @param p {Pagintor} Paginator instance to attach to @@ -1459,7 +1459,6 @@ http://developer.yahoo.net/yui/license.txt /** * ui Component to generate the link to jump to the last page. * - * @module gallery-paginator * @class Paginator.ui.LastPageLink * @constructor * @param p {Pagintor} Paginator instance to attach to @@ -1472,8 +1471,8 @@ Paginator.ui.LastPageLink = function (p) { p.after('rowsPerPageChange',this.update,this); p.after('totalRecordsChange',this.update,this); - p.after('lastPageLinkClassChange', this.rebuild, this); - p.after('lastPageLinkLabelChange', this.rebuild, this); + p.after('lastPageLinkClassChange', this.rebuild, this); + p.after('lastPageLinkLabelChange', this.rebuild, this); }; /** @@ -1658,7 +1657,6 @@ http://developer.yahoo.net/yui/license.txt /** * ui Component to generate the link to jump to the next page. * - * @module gallery-paginator * @class Paginator.ui.NextPageLink * @constructor * @param p {Pagintor} Paginator instance to attach to @@ -1671,8 +1669,8 @@ Paginator.ui.NextPageLink = function (p) { p.after('rowsPerPageChange', this.update,this); p.after('totalRecordsChange', this.update,this); - p.after('nextPageLinkClassChange', this.rebuild, this); - p.after('nextPageLinkLabelChange', this.rebuild, this); + p.after('nextPageLinkClassChange', this.rebuild, this); + p.after('nextPageLinkLabelChange', this.rebuild, this); }; /** @@ -1828,7 +1826,6 @@ http://developer.yahoo.net/yui/license.txt /** * ui Component to generate the page links * - * @module gallery-paginator * @class Paginator.ui.PageLinks * @constructor * @param p {Pagintor} Paginator instance to attach to @@ -2077,7 +2074,6 @@ http://developer.yahoo.net/yui/license.txt /** * ui Component to generate the link to jump to the previous page. * - * @module gallery-paginator * @class Paginator.ui.PreviousPageLink * @constructor * @param p {Pagintor} Paginator instance to attach to @@ -2222,7 +2218,6 @@ http://developer.yahoo.net/yui/license.txt /** * ui Component to generate the rows-per-page dropdown * - * @module gallery-paginator * @class Paginator.ui.RowsPerPageDropdown * @constructor * @param p {Pagintor} Paginator instance to attach to @@ -2413,7 +2408,6 @@ Paginator.ui.RowsPerPageDropdown.prototype = { /********************************************************************** * Adds per-page error notification to Paginator.ui.PageLinks. * - * @module gallery-paginator * @class Paginator.ui.ValidationPageLinks * @constructor * @param p {Pagintor} Paginator instance to attach to @@ -2424,7 +2418,7 @@ Paginator.ui.ValidationPageLinks = function( { Paginator.ui.ValidationPageLinks.superclass.constructor.call(this, p); - p.after('pageStatusChange', this.rebuild, this); + p.after('pageStatusChange', this.rebuild, this); }; var vpl_status_prefix = 'yui3-has'; @@ -2433,7 +2427,7 @@ var vpl_status_prefix = 'yui3-has'; * Array of status strings for each page. If the status value for a page * is not empty, it is used to build a CSS class for the page: * yui3-has<status> - * + * * @attribute pageStatus */ Paginator.ATTRS.pageStatus = @@ -2442,8 +2436,8 @@ Paginator.ATTRS.pageStatus = validator: Y.Lang.isArray }; -Y.extend(Paginator.ui.ValidationPageLinks, Paginator.ui.PageLinks, -{ +Y.extend(Paginator.ui.ValidationPageLinks, Paginator.ui.PageLinks, +{ update: function(e) { if (e && e.prevVal === e.newVal) @@ -2482,8 +2476,8 @@ Y.extend(Paginator.ui.ValidationPageLinks, Paginator.ui.PageLinks, this.container.set('innerHTML', content); } } - + }); -}, 'gallery-2011.04.13-22-38' ,{requires:['widget','event-key','substitute'], skinnable:true}); +}, 'gallery-2012.03.23-18-00' ,{requires:['widget','event-key','substitute'], skinnable:true}); diff --git a/build/gallery-picklist/gallery-picklist-debug.js b/build/gallery-picklist/gallery-picklist-debug.js new file mode 100644 index 0000000000..db7fff1b88 --- /dev/null +++ b/build/gallery-picklist/gallery-picklist-debug.js @@ -0,0 +1,1076 @@ +YUI.add('gallery-picklist', function(Y) { + +/** + * PickList Widget : + * + * Requires: 3.5.0pr2 at least, "event", "button", "cssbutton" + * + * @module gallery-picklist + **/ + var Lang = Y.Lang, + fnReplacer = Lang.sub; + +/** + * @class PickList + * @extends Widget + **/ + function PickList(config) { + PickList.superclass.constructor.apply(this, arguments); + } + + PickList.NAME = "picklist"; + + PickList.ATTRS = { + + /** + The Array to populate the SELECT / OPTIONS on the left-side, herein referred to + as "options" in this widget. + + Elements of this array aren't required to have members { text:'', value:''}, + if the members are different they can be mapped to the expected settings using + the "optionsMap" attribute. + + If the members of this array are non-object single-items, they are assumed to be + the "text" item, and the "value" will also be set to this item. + + @attribute options + @type {Array} + @default [] + **/ + options : { + value : [], + validator : Lang.isArray + }, + + + /** + The Array to populate the SELECT / OPTIONS on the right-side, herein referred + to as "selected" in this widget. For example, if a FORM is opened with default + multi-selected items, they would be provided in this attribute as an array. + + Elements of this array aren't required to have members { text:'', value:''}, + if the members are different they can be mapped to the expected settings using + the "optionsMap" attribute. + + If the members of this array are non-object single-items, they are assumed to be + the "text" item, and the "value" will also be set to this item. + + @attribute selected + @type {Array} + @default [] + **/ + selections : { + value : [], + validator : Lang.isArray + }, + + + /** + An object having members "value, text, title" that defines the mapping + between the provided "options" array data and the expected parameters of + the JavaScript <option> (value, text title). + + @attribute optionsMap + @type {Object} + @default { value:'value', text:'text', title:'title' } + **/ + optionsMap : { + value : { value:'value', text:'text', title:'title' }, + writeOnce : true, + validator: function(o) { + if ( !Lang.isObject(o) ) return false; + if ( o.text && !o.value ) + o.value = 'value'; + else if ( o.value && !o.text ) + o.text = 'text'; + if ( !o.title ) o.title = 'title'; + return true; + } + }, + + + /** + Classname to be applied to the "options" element, usually used to + specify the "width" of the element. + + @attribute selClassName + @type {String} + @default '' + **/ + selClassName : { + value : '', + validator : Lang.isString + }, + + + /** + The DOM ID element of the "template" to be used for defining the OPTIONS, SELECTIONS, + and the BUTTONS. + + Expected replacable placeholder tokens positioned within the "template" are identified as; + + {OPTIONS_CONTAINER} : Left-hand side "Options" placeholder + {ACTION_ALL} : Placeholder for the "Add All" button + {ACTION_ONE} : Placeholder for the "Add One" button + {ACTION_BACK} : Placeholder for the "Remove One" button + {ACTION_ALLBACK} : Placeholder for the "Remove All" button + + @attribute template + @type {String} + @default '' + **/ + template : { + value : '', + validator : Lang.isString + }, + + + /** + Attribute allows specifying the "button" type to be used during construction of the + Widget template. Currently supported are HTML
    Available:
    {OPTIONS_CONTAINER}
    ' + +'{ACTION_ALL}
    {ACTION_ONE}
    {ACTION_BACK}
    {ACTION_ALLBACK}
    ' + +'Selected:
    {SELECTIONS_CONTAINER}
    ', + + /** + * The template to use for constructing the HTML ', + + /** + * The template to use for constructing the HTML elements, + and four BUTTONS to operate on the Widget. + + The method uses the "template" attribute if set for positioning of the above UI elements, + if none is provided a default template is used. + + @method _renderInput + @protected + @return true + **/ + _renderInput : function() { + var cbox = this.get("contentBox"), + tmpl = ( this.get("template") ) ? Y.one( this.get("template") ).getContent() : null; + + var widget_tmpl = tmpl || this.TMPL_control; // use default if none provided ... + + cbox.addClass( this.getClassName() ); + + var btna_html, btno_html, btnb_html, btnab_html, opt_html, sel_html; + // + // Create the HTML SELECT boxes ... + // + opt_html = fnReplacer( this.TMPL_inputs, { + size : this.get('selectSize'), + className : this.getClassName('options') + ' ' + this.get('optClassName') + }); + + sel_html = fnReplacer( this.TMPL_inputs, { + size : this.get('selectSize'), + className : this.getClassName('selected') + ' ' + this.get('selClassName') + }); + + // + // Create the BUTTONS ... + // + if ( this.get('buttonType') === 'htmlbutton' || this.get('buttonType') === 'ybutton' ) { + btna_html = this._createHtmlButton( 'all', 'actionLabelAll' ); + btno_html = this._createHtmlButton( 'one', 'actionLabelOne' ); + btnb_html = this._createHtmlButton( 'rmv', 'actionLabelRmv' ); + btnab_html= this._createHtmlButton( 'rmvall', 'actionLabelRmvAll' ); + } + + if ( this.get('buttonType') === 'link' ) { + btna_html = this._createALinkButton( 'all', 'actionLabelAll' ); + btno_html = this._createALinkButton( 'one', 'actionLabelOne' ); + btnb_html = this._createALinkButton( 'rmv', 'actionLabelRmv' ); + btnab_html= this._createALinkButton( 'rmvall', 'actionLabelRmvAll' ); + } + + if ( this.get('buttonType') === 'cssbutton' ) { + btna_html = this._createCssButton( 'all', null, 'actionLabelAll' ); + btno_html = this._createCssButton( 'one', null, 'actionLabelOne' ); + btnb_html = this._createCssButton( 'rmv', null, 'actionLabelRmv' ); + btnab_html = this._createCssButton( 'rmvall', null, 'actionLabelRmvAll' ); + } + + // + // Populate the template + // + cbox.setContent( fnReplacer( widget_tmpl, { + OPTIONS_CONTAINER : opt_html || '', + SELECTIONS_CONTAINER: sel_html || '', + ACTION_ALLBACK : btnab_html || '', + ACTION_ALL : btna_html || '', + ACTION_ONE : btno_html || '', + ACTION_BACK : btnb_html || '' + }) ); + + // + // Get references to the nodes of the widget after they were created + // + this._optionNode = cbox.one('.'+this.getClassName('options')); + this._selectNode = cbox.one('.'+this.getClassName('selected')); + + if ( this._optionNode && !this.get('optClassName') ) + this._optionNode.setStyle('width', this.get('selectWidth') ); + + if ( this._selectNode && !this.get('selClassName') ) + this._selectNode.setStyle('width', this.get('selectWidth') ); + + this._optionNode.set('selectedIndex',-1); + this._selectNode.set('selectedIndex',-1); + + // If Button widgets are reqd, go through and build them + // now since the placeholders are in markup + + if ( this.get('buttonType') === 'ybutton' ) { + + this._ybuttons.push( this._createYButtonNode( cbox.one('.'+this.getClassName( this.CLASS_ACTION,'all')) ) ); + this._ybuttons.push( this._createYButtonNode( cbox.one('.'+this.getClassName( this.CLASS_ACTION,'one')) ) ); + this._ybuttons.push( this._createYButtonNode( cbox.one('.'+this.getClassName( this.CLASS_ACTION,'rmv')) ) ); + this._ybuttons.push( this._createYButtonNode( cbox.one('.'+this.getClassName( this.CLASS_ACTION,'rmvall')) ) ); + + } + + return true; + }, + + + /** + Method creates an ',TMPL_button:'',TMPL_cssbtn:'',TMPL_alink:'{content}',CLASS_ACTION:"action",_optionNode:null,_selectNode:null,_eventHandles:[],_ybuttons:[],_optOrder:[],initializer:function(e){this._eventHandles=[];this._ybuttons=[];this._optOrder=[];this._optionNode=null;this._selectNode=null;return true;},destructor:function(){d.Array.each(this._ybuttons,function(e){e.destroy();});this._ybuttons.length=0;d.Array.each(this._eventHandles,function(e){e.detach();});this._eventHandles.length=0;this._optOrder.length=0;this._optionNode.destroy();this._selectNode.destroy();return true;},renderUI:function(){this._renderInput();if(this.get("options")){this._loadOptions();}this._optionNode.focus();this.fire("render");},bindUI:function(){if(this.get("buttonType")==="ybutton"){var g=this._ybuttons;if(g[0]){this._eventHandles.push(g[0].on("click",d.bind(this._onButtonAll,this)));}if(g[1]){this._eventHandles.push(g[1].on("click",d.bind(this._onButtonOne,this)));}if(g[2]){this._eventHandles.push(g[2].on("click",d.bind(this._onButtonBack,this)));}if(g[3]){this._eventHandles.push(g[3].on("click",d.bind(this._onButtonAllBack,this)));}}else{var i=this.get("contentBox"),f=i.one("."+this.getClassName(this.CLASS_ACTION,"all")),j=i.one("."+this.getClassName(this.CLASS_ACTION,"one")),e=i.one("."+this.getClassName(this.CLASS_ACTION,"rmv")),h=i.one("."+this.getClassName(this.CLASS_ACTION,"rmvall"));if(f){this._eventHandles.push(f.on("click",d.bind(this._onButtonAll,this)));}if(j){this._eventHandles.push(j.on("click",d.bind(this._onButtonOne,this)));}if(e){this._eventHandles.push(e.on("click",d.bind(this._onButtonBack,this)));}if(h){this._eventHandles.push(h.on("click",d.bind(this._onButtonAllBack,this)));}}this.after("optionsChange",d.bind(this._loadOptions,this,false));this.after("selectionsChange",d.bind(this._loadOptions,this,true));return true;},syncUI:function(){},clearOptions:function(){this._optOrder.length=0;return this._clearOptions(this._optionNode);},clearSelections:function(){return this._clearOptions(this._selectNode);},getSelections:function(){var e=this._selectNode,f=[];e.get("options").each(function(h,g){f.push({value:h.get("value"),text:h.get("text"),title:h.get("title")});});return f;},_renderInput:function(){var k=this.get("contentBox"),m=(this.get("template"))?d.one(this.get("template")).getContent():null;var j=m||this.TMPL_control;k.addClass(this.getClassName());var e,g,i,l,h,f;h=b(this.TMPL_inputs,{size:this.get("selectSize"),className:this.getClassName("options")+" "+this.get("optClassName")});f=b(this.TMPL_inputs,{size:this.get("selectSize"),className:this.getClassName("selected")+" "+this.get("selClassName")});if(this.get("buttonType")==="htmlbutton"||this.get("buttonType")==="ybutton"){e=this._createHtmlButton("all","actionLabelAll");g=this._createHtmlButton("one","actionLabelOne");i=this._createHtmlButton("rmv","actionLabelRmv");l=this._createHtmlButton("rmvall","actionLabelRmvAll");}if(this.get("buttonType")==="link"){e=this._createALinkButton("all","actionLabelAll");g=this._createALinkButton("one","actionLabelOne");i=this._createALinkButton("rmv","actionLabelRmv");l=this._createALinkButton("rmvall","actionLabelRmvAll");}if(this.get("buttonType")==="cssbutton"){e=this._createCssButton("all",null,"actionLabelAll");g=this._createCssButton("one",null,"actionLabelOne");i=this._createCssButton("rmv",null,"actionLabelRmv");l=this._createCssButton("rmvall",null,"actionLabelRmvAll");}k.setContent(b(j,{OPTIONS_CONTAINER:h||"",SELECTIONS_CONTAINER:f||"",ACTION_ALLBACK:l||"",ACTION_ALL:e||"",ACTION_ONE:g||"",ACTION_BACK:i||""}));this._optionNode=k.one("."+this.getClassName("options"));this._selectNode=k.one("."+this.getClassName("selected"));if(this._optionNode&&!this.get("optClassName")){this._optionNode.setStyle("width",this.get("selectWidth"));}if(this._selectNode&&!this.get("selClassName")){this._selectNode.setStyle("width",this.get("selectWidth"));}this._optionNode.set("selectedIndex",-1);this._selectNode.set("selectedIndex",-1);if(this.get("buttonType")==="ybutton"){this._ybuttons.push(this._createYButtonNode(k.one("."+this.getClassName(this.CLASS_ACTION,"all")))); +this._ybuttons.push(this._createYButtonNode(k.one("."+this.getClassName(this.CLASS_ACTION,"one"))));this._ybuttons.push(this._createYButtonNode(k.one("."+this.getClassName(this.CLASS_ACTION,"rmv"))));this._ybuttons.push(this._createYButtonNode(k.one("."+this.getClassName(this.CLASS_ACTION,"rmvall"))));}return true;},_createOption:function(e){var f=b(this.TMPL_option,{optValue:e.value||"",optText:e.text||"",optTitle:e.title||""});return opt_node=d.Node.create(f);},_loadOptions:function(g){g=(g===undefined)?false:g;var f=(g)?this._selectNode:this._optionNode,e=f.getDOMNode(),h=(g)?this.get("selections"):this.get("options");if(h){if(!g){this.clearOptions();}this.clearSelections();}d.Array.each(h,function(j){var i=this._optStdFormat(j),k=this._createOption(i);f.append(k);if(this.get("stackMode")&&this.get("preserveOrder")){this._optOrder.push(i.value||i.text);}},this);this.fire("load",{obj:this,opts:h});return h.length;},_clearOptions:function(e){e=e||this._optionNode;e.getDOMNode().options.length=0;return true;},_optStdFormat:function(g){var f=this.get("optionsMap"),h=null,e=null,i=null;if(c.isObject(g)){h=g[f.value]||null;e=g[f.text]||null;i=g[f.title]||null;}else{h=g;e=g;}h=h||e;return{value:h,text:e,title:i};},_returnOptionPerOrder:function(l){var g=this._optionNode,h=this._selectNode,e=this.get("preserveOrder");var k=l.get("value"),j=l.get("text");var f=k||j||null;if(f&&this.get("preserveOrder")&&this.get("stackMode")){var i=this._optOrder.indexOf(l);}},_createYButtonNode:function(e){var f=new d.Button({srcNode:e});return f;},_createCssButton:function(i,h,f){var g=(h)?this.get(h):"";var e=(f)?this.get(f):"";return b(this.TMPL_cssbtn,{className:this.getClassName(this.CLASS_ACTION,i),subName:i,lcontent:g||"",rcontent:e||""});},_createHtmlButton:function(f,e){return b(this.TMPL_button,{"content":this.get(e),className:this.getClassName(this.CLASS_ACTION,f)});},_createALinkButton:function(f,e){return b(this.TMPL_alink,{"content":this.get(e),className:this.getClassName(this.CLASS_ACTION,f)});},_getSelectedOptions:function(f){var e=[];f.get("options").each(function(h,g){if(h.get("selected")===true){e.push({"index":g,optionObj:h});}});return e;},_selOptionExists:function(h){var e=this._selectNode,g=h.get("value"),f=false;e.get("options").some(function(i){if(i.get("value")===g){f=true;return true;}},this);return f;},_onButtonAll:function(i){var f=this._optionNode,g=this._selectNode,h=f.get("options");h.each(function(e){if(this._selOptionExists(e)){return;}if(this.get("stackMode")){g.append(e);}else{g.append(e.cloneNode(true));}},this);this.fire("clickAll",{evt:i,opts:h});},_findInsertIndex:function(k,g){var e=-1;var j=this._optOrder,h=[];g.get("options").each(function(i){h.push(i.get("value"));});var f=0;},_onButtonOne:function(i){var f=this._optionNode,g=this._selectNode;var h=this._getSelectedOptions(f);d.Array.each(h,function(e){this._findInsertIndex(e,g);e.optionObj.set("selected",false);if(this._selOptionExists(e.optionObj)){return;}if(this.get("stackMode")){g.append(e.optionObj);}else{g.append(e.optionObj.cloneNode(true));}},this);this.fire("clickOne",{evt:i,opts:h});},_onButtonBack:function(i){var f=this._optionNode,h=this._selectNode;var g=this._getSelectedOptions(h);d.Array.each(g,function(e){this._findInsertIndex(e,f);e.optionObj.set("selected",false);if(this.get("stackMode")){f.append(e.optionObj);}else{e.optionObj.remove();}},this);this.fire("clickRemove",{evt:i,opts:g});},_onButtonAllBack:function(h){var f=this._optionNode,g=this._selectNode,i=g.get("options");i.each(function(e){if(this.get("stackMode")){f.append(e);}else{e.remove();}},this);this.fire("clickRemoveAll",{evt:h,opts:i});}});d.PickList=a;},"gallery-2012.03.23-18-00",{requires:["base-build","widget","event","button","cssbutton"],skinnable:false}); \ No newline at end of file diff --git a/build/gallery-picklist/gallery-picklist.js b/build/gallery-picklist/gallery-picklist.js new file mode 100644 index 0000000000..db7fff1b88 --- /dev/null +++ b/build/gallery-picklist/gallery-picklist.js @@ -0,0 +1,1076 @@ +YUI.add('gallery-picklist', function(Y) { + +/** + * PickList Widget : + * + * Requires: 3.5.0pr2 at least, "event", "button", "cssbutton" + * + * @module gallery-picklist + **/ + var Lang = Y.Lang, + fnReplacer = Lang.sub; + +/** + * @class PickList + * @extends Widget + **/ + function PickList(config) { + PickList.superclass.constructor.apply(this, arguments); + } + + PickList.NAME = "picklist"; + + PickList.ATTRS = { + + /** + The Array to populate the SELECT / OPTIONS on the left-side, herein referred to + as "options" in this widget. + + Elements of this array aren't required to have members { text:'', value:''}, + if the members are different they can be mapped to the expected settings using + the "optionsMap" attribute. + + If the members of this array are non-object single-items, they are assumed to be + the "text" item, and the "value" will also be set to this item. + + @attribute options + @type {Array} + @default [] + **/ + options : { + value : [], + validator : Lang.isArray + }, + + + /** + The Array to populate the SELECT / OPTIONS on the right-side, herein referred + to as "selected" in this widget. For example, if a FORM is opened with default + multi-selected items, they would be provided in this attribute as an array. + + Elements of this array aren't required to have members { text:'', value:''}, + if the members are different they can be mapped to the expected settings using + the "optionsMap" attribute. + + If the members of this array are non-object single-items, they are assumed to be + the "text" item, and the "value" will also be set to this item. + + @attribute selected + @type {Array} + @default [] + **/ + selections : { + value : [], + validator : Lang.isArray + }, + + + /** + An object having members "value, text, title" that defines the mapping + between the provided "options" array data and the expected parameters of + the JavaScript <option> (value, text title). + + @attribute optionsMap + @type {Object} + @default { value:'value', text:'text', title:'title' } + **/ + optionsMap : { + value : { value:'value', text:'text', title:'title' }, + writeOnce : true, + validator: function(o) { + if ( !Lang.isObject(o) ) return false; + if ( o.text && !o.value ) + o.value = 'value'; + else if ( o.value && !o.text ) + o.text = 'text'; + if ( !o.title ) o.title = 'title'; + return true; + } + }, + + + /** + Classname to be applied to the "options" element, usually used to + specify the "width" of the element. + + @attribute selClassName + @type {String} + @default '' + **/ + selClassName : { + value : '', + validator : Lang.isString + }, + + + /** + The DOM ID element of the "template" to be used for defining the OPTIONS, SELECTIONS, + and the BUTTONS. + + Expected replacable placeholder tokens positioned within the "template" are identified as; + + {OPTIONS_CONTAINER} : Left-hand side "Options" placeholder + {ACTION_ALL} : Placeholder for the "Add All" button + {ACTION_ONE} : Placeholder for the "Add One" button + {ACTION_BACK} : Placeholder for the "Remove One" button + {ACTION_ALLBACK} : Placeholder for the "Remove All" button + + @attribute template + @type {String} + @default '' + **/ + template : { + value : '', + validator : Lang.isString + }, + + + /** + Attribute allows specifying the "button" type to be used during construction of the + Widget template. Currently supported are HTML