]> git.wh0rd.org - tt-rss.git/blobdiff - lib/dojo/dnd/Container.js
build custom layer of Dojo to speed up loading of tt-rss (refs #293)
[tt-rss.git] / lib / dojo / dnd / Container.js
index 01e4ce2d9206969441da3d97c9e7abda3f117a21..6efc6f6fb40e76746d0ebb89ca33e2af48d4cd17 100644 (file)
 */
 
 
-if(!dojo._hasResource["dojo.dnd.Container"]){
-dojo._hasResource["dojo.dnd.Container"]=true;
+if(!dojo._hasResource["dojo.dnd.Container"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo.dnd.Container"] = true;
 dojo.provide("dojo.dnd.Container");
+
 dojo.require("dojo.dnd.common");
 dojo.require("dojo.parser");
-dojo.declare("dojo.dnd.Container",null,{skipForm:false,constructor:function(_1,_2){
-this.node=dojo.byId(_1);
-if(!_2){
-_2={};
-}
-this.creator=_2.creator||null;
-this.skipForm=_2.skipForm;
-this.parent=_2.dropParent&&dojo.byId(_2.dropParent);
-this.map={};
-this.current=null;
-this.containerState="";
-dojo.addClass(this.node,"dojoDndContainer");
-if(!(_2&&_2._skipStartup)){
-this.startup();
-}
-this.events=[dojo.connect(this.node,"onmouseover",this,"onMouseOver"),dojo.connect(this.node,"onmouseout",this,"onMouseOut"),dojo.connect(this.node,"ondragstart",this,"onSelectStart"),dojo.connect(this.node,"onselectstart",this,"onSelectStart")];
-},creator:function(){
-},getItem:function(_3){
-return this.map[_3];
-},setItem:function(_4,_5){
-this.map[_4]=_5;
-},delItem:function(_6){
-delete this.map[_6];
-},forInItems:function(f,o){
-o=o||dojo.global;
-var m=this.map,e=dojo.dnd._empty;
-for(var i in m){
-if(i in e){
-continue;
-}
-f.call(o,m[i],i,this);
-}
-return o;
-},clearItems:function(){
-this.map={};
-},getAllNodes:function(){
-return dojo.query("> .dojoDndItem",this.parent);
-},sync:function(){
-var _7={};
-this.getAllNodes().forEach(function(_8){
-if(_8.id){
-var _9=this.getItem(_8.id);
-if(_9){
-_7[_8.id]=_9;
-return;
-}
-}else{
-_8.id=dojo.dnd.getUniqueId();
-}
-var _a=_8.getAttribute("dndType"),_b=_8.getAttribute("dndData");
-_7[_8.id]={data:_b||_8.innerHTML,type:_a?_a.split(/\s*,\s*/):["text"]};
-},this);
-this.map=_7;
-return this;
-},insertNodes:function(_c,_d,_e){
-if(!this.parent.firstChild){
-_e=null;
-}else{
-if(_d){
-if(!_e){
-_e=this.parent.firstChild;
-}
-}else{
-if(_e){
-_e=_e.nextSibling;
-}
-}
-}
-if(_e){
-for(var i=0;i<_c.length;++i){
-var t=this._normalizedCreator(_c[i]);
-this.setItem(t.node.id,{data:t.data,type:t.type});
-this.parent.insertBefore(t.node,_e);
-}
-}else{
-for(var i=0;i<_c.length;++i){
-var t=this._normalizedCreator(_c[i]);
-this.setItem(t.node.id,{data:t.data,type:t.type});
-this.parent.appendChild(t.node);
-}
-}
-return this;
-},destroy:function(){
-dojo.forEach(this.events,dojo.disconnect);
-this.clearItems();
-this.node=this.parent=this.current=null;
-},markupFactory:function(_f,_10){
-_f._skipStartup=true;
-return new dojo.dnd.Container(_10,_f);
-},startup:function(){
-if(!this.parent){
-this.parent=this.node;
-if(this.parent.tagName.toLowerCase()=="table"){
-var c=this.parent.getElementsByTagName("tbody");
-if(c&&c.length){
-this.parent=c[0];
-}
-}
-}
-this.defaultCreator=dojo.dnd._defaultCreator(this.parent);
-this.sync();
-},onMouseOver:function(e){
-var n=e.relatedTarget;
-while(n){
-if(n==this.node){
-break;
-}
-try{
-n=n.parentNode;
-}
-catch(x){
-n=null;
-}
-}
-if(!n){
-this._changeState("Container","Over");
-this.onOverEvent();
-}
-n=this._getChildByEvent(e);
-if(this.current==n){
-return;
-}
-if(this.current){
-this._removeItemClass(this.current,"Over");
-}
-if(n){
-this._addItemClass(n,"Over");
-}
-this.current=n;
-},onMouseOut:function(e){
-for(var n=e.relatedTarget;n;){
-if(n==this.node){
-return;
-}
-try{
-n=n.parentNode;
-}
-catch(x){
-n=null;
-}
-}
-if(this.current){
-this._removeItemClass(this.current,"Over");
-this.current=null;
-}
-this._changeState("Container","");
-this.onOutEvent();
-},onSelectStart:function(e){
-if(!this.skipForm||!dojo.dnd.isFormElement(e)){
-dojo.stopEvent(e);
-}
-},onOverEvent:function(){
-},onOutEvent:function(){
-},_changeState:function(_11,_12){
-var _13="dojoDnd"+_11;
-var _14=_11.toLowerCase()+"State";
-dojo.removeClass(this.node,_13+this[_14]);
-dojo.addClass(this.node,_13+_12);
-this[_14]=_12;
-},_addItemClass:function(_15,_16){
-dojo.addClass(_15,"dojoDndItem"+_16);
-},_removeItemClass:function(_17,_18){
-dojo.removeClass(_17,"dojoDndItem"+_18);
-},_getChildByEvent:function(e){
-var _19=e.target;
-if(_19){
-for(var _1a=_19.parentNode;_1a;_19=_1a,_1a=_19.parentNode){
-if(_1a==this.parent&&dojo.hasClass(_19,"dojoDndItem")){
-return _19;
-}
-}
-}
-return null;
-},_normalizedCreator:function(_1b,_1c){
-var t=(this.creator||this.defaultCreator).call(this,_1b,_1c);
-if(!dojo.isArray(t.type)){
-t.type=["text"];
-}
-if(!t.node.id){
-t.node.id=dojo.dnd.getUniqueId();
-}
-dojo.addClass(t.node,"dojoDndItem");
-return t;
-}});
-dojo.dnd._createNode=function(tag){
-if(!tag){
-return dojo.dnd._createSpan;
-}
-return function(_1d){
-return dojo.create(tag,{innerHTML:_1d});
-};
-};
-dojo.dnd._createTrTd=function(_1e){
-var tr=dojo.create("tr");
-dojo.create("td",{innerHTML:_1e},tr);
-return tr;
+
+/*
+       Container states:
+               ""              - normal state
+               "Over"  - mouse over a container
+       Container item states:
+               ""              - normal state
+               "Over"  - mouse over a container item
+*/
+
+/*=====
+dojo.declare("dojo.dnd.__ContainerArgs", [], {
+       creator: function(){
+               // summary:
+               //              a creator function, which takes a data item, and returns an object like that:
+               //              {node: newNode, data: usedData, type: arrayOfStrings}
+       },
+
+       // skipForm: Boolean
+       //              don't start the drag operation, if clicked on form elements
+       skipForm: false,
+
+       // dropParent: Node||String
+       //              node or node's id to use as the parent node for dropped items
+       //              (must be underneath the 'node' parameter in the DOM)
+       dropParent: null,
+
+       // _skipStartup: Boolean
+       //              skip startup(), which collects children, for deferred initialization
+       //              (this is used in the markup mode)
+       _skipStartup: false
+});
+
+dojo.dnd.Item = function(){
+       // summary:
+       //              Represents (one of) the source node(s) being dragged.
+       //              Contains (at least) the "type" and "data" attributes.
+       // type: String[]
+       //              Type(s) of this item, by default this is ["text"]
+       // data: Object
+       //              Logical representation of the object being dragged.
+       //              If the drag object's type is "text" then data is a String,
+       //              if it's another type then data could be a different Object,
+       //              perhaps a name/value hash.
+       
+       this.type = type;
+       this.data = data;
+}
+=====*/
+
+dojo.declare("dojo.dnd.Container", null, {
+       // summary:
+       //              a Container object, which knows when mouse hovers over it, 
+       //              and over which element it hovers
+       
+       // object attributes (for markup)
+       skipForm: false,
+       
+       /*=====
+       // current: DomNode
+       //              The DOM node the mouse is currently hovered over
+       current: null,
+       
+       // map: Hash<String, dojo.dnd.Item>
+       //              Map from an item's id (which is also the DOMNode's id) to
+       //              the dojo.dnd.Item itself.
+       map: {},
+       =====*/
+       
+       constructor: function(node, params){
+               // summary:
+               //              a constructor of the Container
+               // node: Node
+               //              node or node's id to build the container on
+               // params: dojo.dnd.__ContainerArgs
+               //              a dictionary of parameters
+               this.node = dojo.byId(node);
+               if(!params){ params = {}; }
+               this.creator = params.creator || null;
+               this.skipForm = params.skipForm;
+               this.parent = params.dropParent && dojo.byId(params.dropParent);
+               
+               // class-specific variables
+               this.map = {};
+               this.current = null;
+
+               // states
+               this.containerState = "";
+               dojo.addClass(this.node, "dojoDndContainer");
+               
+               // mark up children
+               if(!(params && params._skipStartup)){
+                       this.startup();
+               }
+
+               // set up events
+               this.events = [
+                       dojo.connect(this.node, "onmouseover", this, "onMouseOver"),
+                       dojo.connect(this.node, "onmouseout",  this, "onMouseOut"),
+                       // cancel text selection and text dragging
+                       dojo.connect(this.node, "ondragstart",   this, "onSelectStart"),
+                       dojo.connect(this.node, "onselectstart", this, "onSelectStart")
+               ];
+       },
+       
+       // object attributes (for markup)
+       creator: function(){
+               // summary:
+               //              creator function, dummy at the moment
+       },
+       
+       // abstract access to the map
+       getItem: function(/*String*/ key){
+               // summary:
+               //              returns a data item by its key (id)
+               return this.map[key];   // dojo.dnd.Item
+       },
+       setItem: function(/*String*/ key, /*dojo.dnd.Item*/ data){
+               // summary:
+               //              associates a data item with its key (id)
+               this.map[key] = data;
+       },
+       delItem: function(/*String*/ key){
+               // summary:
+               //              removes a data item from the map by its key (id)
+               delete this.map[key];
+       },
+       forInItems: function(/*Function*/ f, /*Object?*/ o){
+               // summary:
+               //              iterates over a data map skipping members that 
+               //              are present in the empty object (IE and/or 3rd-party libraries).
+               o = o || dojo.global;
+               var m = this.map, e = dojo.dnd._empty;
+               for(var i in m){
+                       if(i in e){ continue; }
+                       f.call(o, m[i], i, this);
+               }
+               return o;       // Object
+       },
+       clearItems: function(){
+               // summary:
+               //              removes all data items from the map
+               this.map = {};
+       },
+       
+       // methods
+       getAllNodes: function(){
+               // summary:
+               //              returns a list (an array) of all valid child nodes
+               return dojo.query("> .dojoDndItem", this.parent);       // NodeList
+       },
+       sync: function(){
+               // summary:
+               //              sync up the node list with the data map
+               var map = {};
+               this.getAllNodes().forEach(function(node){
+                       if(node.id){
+                               var item = this.getItem(node.id);
+                               if(item){
+                                       map[node.id] = item;
+                                       return;
+                               }
+                       }else{
+                               node.id = dojo.dnd.getUniqueId();
+                       }
+                       var type = node.getAttribute("dndType"),
+                               data = node.getAttribute("dndData");
+                       map[node.id] = {
+                               data: data || node.innerHTML,
+                               type: type ? type.split(/\s*,\s*/) : ["text"]
+                       };
+               }, this);
+               this.map = map;
+               return this;    // self
+       },
+       insertNodes: function(data, before, anchor){
+               // summary:
+               //              inserts an array of new nodes before/after an anchor node
+               // data: Array
+               //              a list of data items, which should be processed by the creator function
+               // before: Boolean
+               //              insert before the anchor, if true, and after the anchor otherwise
+               // anchor: Node
+               //              the anchor node to be used as a point of insertion
+               if(!this.parent.firstChild){
+                       anchor = null;
+               }else if(before){
+                       if(!anchor){
+                               anchor = this.parent.firstChild;
+                       }
+               }else{
+                       if(anchor){
+                               anchor = anchor.nextSibling;
+                       }
+               }
+               if(anchor){
+                       for(var i = 0; i < data.length; ++i){
+                               var t = this._normalizedCreator(data[i]);
+                               this.setItem(t.node.id, {data: t.data, type: t.type});
+                               this.parent.insertBefore(t.node, anchor);
+                       }
+               }else{
+                       for(var i = 0; i < data.length; ++i){
+                               var t = this._normalizedCreator(data[i]);
+                               this.setItem(t.node.id, {data: t.data, type: t.type});
+                               this.parent.appendChild(t.node);
+                       }
+               }
+               return this;    // self
+       },
+       destroy: function(){
+               // summary:
+               //              prepares this object to be garbage-collected
+               dojo.forEach(this.events, dojo.disconnect);
+               this.clearItems();
+               this.node = this.parent = this.current = null;
+       },
+
+       // markup methods
+       markupFactory: function(params, node){
+               params._skipStartup = true;
+               return new dojo.dnd.Container(node, params);
+       },
+       startup: function(){
+               // summary:
+               //              collects valid child items and populate the map
+               
+               // set up the real parent node
+               if(!this.parent){
+                       // use the standard algorithm, if not assigned
+                       this.parent = this.node;
+                       if(this.parent.tagName.toLowerCase() == "table"){
+                               var c = this.parent.getElementsByTagName("tbody");
+                               if(c && c.length){ this.parent = c[0]; }
+                       }
+               }
+               this.defaultCreator = dojo.dnd._defaultCreator(this.parent);
+
+               // process specially marked children
+               this.sync();
+       },
+
+       // mouse events
+       onMouseOver: function(e){
+               // summary:
+               //              event processor for onmouseover
+               // e: Event
+               //              mouse event
+               var n = e.relatedTarget;
+               while(n){
+                       if(n == this.node){ break; }
+                       try{
+                               n = n.parentNode;
+                       }catch(x){
+                               n = null;
+                       }
+               }
+               if(!n){
+                       this._changeState("Container", "Over");
+                       this.onOverEvent();
+               }
+               n = this._getChildByEvent(e);
+               if(this.current == n){ return; }
+               if(this.current){ this._removeItemClass(this.current, "Over"); }
+               if(n){ this._addItemClass(n, "Over"); }
+               this.current = n;
+       },
+       onMouseOut: function(e){
+               // summary:
+               //              event processor for onmouseout
+               // e: Event
+               //              mouse event
+               for(var n = e.relatedTarget; n;){
+                       if(n == this.node){ return; }
+                       try{
+                               n = n.parentNode;
+                       }catch(x){
+                               n = null;
+                       }
+               }
+               if(this.current){
+                       this._removeItemClass(this.current, "Over");
+                       this.current = null;
+               }
+               this._changeState("Container", "");
+               this.onOutEvent();
+       },
+       onSelectStart: function(e){
+               // summary:
+               //              event processor for onselectevent and ondragevent
+               // e: Event
+               //              mouse event
+               if(!this.skipForm || !dojo.dnd.isFormElement(e)){
+                       dojo.stopEvent(e);
+               }
+       },
+       
+       // utilities
+       onOverEvent: function(){
+               // summary:
+               //              this function is called once, when mouse is over our container
+       },
+       onOutEvent: function(){
+               // summary:
+               //              this function is called once, when mouse is out of our container
+       },
+       _changeState: function(type, newState){
+               // summary:
+               //              changes a named state to new state value
+               // type: String
+               //              a name of the state to change
+               // newState: String
+               //              new state
+               var prefix = "dojoDnd" + type;
+               var state  = type.toLowerCase() + "State";
+               //dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
+               dojo.removeClass(this.node, prefix + this[state]);
+               dojo.addClass(this.node, prefix + newState);
+               this[state] = newState;
+       },
+       _addItemClass: function(node, type){
+               // summary:
+               //              adds a class with prefix "dojoDndItem"
+               // node: Node
+               //              a node
+               // type: String
+               //              a variable suffix for a class name
+               dojo.addClass(node, "dojoDndItem" + type);
+       },
+       _removeItemClass: function(node, type){
+               // summary:
+               //              removes a class with prefix "dojoDndItem"
+               // node: Node
+               //              a node
+               // type: String
+               //              a variable suffix for a class name
+               dojo.removeClass(node, "dojoDndItem" + type);
+       },
+       _getChildByEvent: function(e){
+               // summary:
+               //              gets a child, which is under the mouse at the moment, or null
+               // e: Event
+               //              a mouse event
+               var node = e.target;
+               if(node){
+                       for(var parent = node.parentNode; parent; node = parent, parent = node.parentNode){
+                               if(parent == this.parent && dojo.hasClass(node, "dojoDndItem")){ return node; }
+                       }
+               }
+               return null;
+       },
+       _normalizedCreator: function(/*dojo.dnd.Item*/ item, /*String*/ hint){
+               // summary:
+               //              adds all necessary data to the output of the user-supplied creator function
+               var t = (this.creator || this.defaultCreator).call(this, item, hint);
+               if(!dojo.isArray(t.type)){ t.type = ["text"]; }
+               if(!t.node.id){ t.node.id = dojo.dnd.getUniqueId(); }
+               dojo.addClass(t.node, "dojoDndItem");
+               return t;
+       }
+});
+
+dojo.dnd._createNode = function(tag){
+       // summary:
+       //              returns a function, which creates an element of given tag 
+       //              (SPAN by default) and sets its innerHTML to given text
+       // tag: String
+       //              a tag name or empty for SPAN
+       if(!tag){ return dojo.dnd._createSpan; }
+       return function(text){  // Function
+               return dojo.create(tag, {innerHTML: text});     // Node
+       };
 };
-dojo.dnd._createSpan=function(_1f){
-return dojo.create("span",{innerHTML:_1f});
+
+dojo.dnd._createTrTd = function(text){
+       // summary:
+       //              creates a TR/TD structure with given text as an innerHTML of TD
+       // text: String
+       //              a text for TD
+       var tr = dojo.create("tr");
+       dojo.create("td", {innerHTML: text}, tr);
+       return tr;      // Node
 };
-dojo.dnd._defaultCreatorNodes={ul:"li",ol:"li",div:"div",p:"div"};
-dojo.dnd._defaultCreator=function(_20){
-var tag=_20.tagName.toLowerCase();
-var c=tag=="tbody"||tag=="thead"?dojo.dnd._createTrTd:dojo.dnd._createNode(dojo.dnd._defaultCreatorNodes[tag]);
-return function(_21,_22){
-var _23=_21&&dojo.isObject(_21),_24,_25,n;
-if(_23&&_21.tagName&&_21.nodeType&&_21.getAttribute){
-_24=_21.getAttribute("dndData")||_21.innerHTML;
-_25=_21.getAttribute("dndType");
-_25=_25?_25.split(/\s*,\s*/):["text"];
-n=_21;
-}else{
-_24=(_23&&_21.data)?_21.data:_21;
-_25=(_23&&_21.type)?_21.type:["text"];
-n=(_22=="avatar"?dojo.dnd._createSpan:c)(String(_24));
-}
-if(!n.id){
-n.id=dojo.dnd.getUniqueId();
-}
-return {node:n,data:_24,type:_25};
+
+dojo.dnd._createSpan = function(text){
+       // summary:
+       //              creates a SPAN element with given text as its innerHTML
+       // text: String
+       //              a text for SPAN
+       return dojo.create("span", {innerHTML: text});  // Node
 };
+
+// dojo.dnd._defaultCreatorNodes: Object
+//             a dictionary that maps container tag names to child tag names
+dojo.dnd._defaultCreatorNodes = {ul: "li", ol: "li", div: "div", p: "div"};
+
+dojo.dnd._defaultCreator = function(node){
+       // summary:
+       //              takes a parent node, and returns an appropriate creator function
+       // node: Node
+       //              a container node
+       var tag = node.tagName.toLowerCase();
+       var c = tag == "tbody" || tag == "thead" ? dojo.dnd._createTrTd :
+                       dojo.dnd._createNode(dojo.dnd._defaultCreatorNodes[tag]);
+       return function(item, hint){    // Function
+               var isObj = item && dojo.isObject(item), data, type, n;
+               if(isObj && item.tagName && item.nodeType && item.getAttribute){
+                       // process a DOM node
+                       data = item.getAttribute("dndData") || item.innerHTML;
+                       type = item.getAttribute("dndType");
+                       type = type ? type.split(/\s*,\s*/) : ["text"];
+                       n = item;       // this node is going to be moved rather than copied
+               }else{
+                       // process a DnD item object or a string
+                       data = (isObj && item.data) ? item.data : item;
+                       type = (isObj && item.type) ? item.type : ["text"];
+                       n = (hint == "avatar" ? dojo.dnd._createSpan : c)(String(data));
+               }
+               if(!n.id){
+                       n.id = dojo.dnd.getUniqueId();
+               }
+               return {node: n, data: data, type: type};
+       };
 };
+
 }