]> git.pond.sub.org Git - eow/blobdiff - static/dojo-release-1.1.1/dojo/dnd/Container.js
add Dojo 1.1.1
[eow] / static / dojo-release-1.1.1 / dojo / dnd / Container.js
diff --git a/static/dojo-release-1.1.1/dojo/dnd/Container.js b/static/dojo-release-1.1.1/dojo/dnd/Container.js
new file mode 100644 (file)
index 0000000..92b3211
--- /dev/null
@@ -0,0 +1,311 @@
+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");
+
+/*
+       Container states:
+               ""              - normal state
+               "Over"  - mouse over a container
+       Container item states:
+               ""              - normal state
+               "Over"  - mouse over a container item
+*/
+
+dojo.declare("dojo.dnd.Container", null, {
+       // summary: a Container object, which knows when mouse hovers over it, 
+       //      and know over which element it hovers
+       
+       // object attributes (for markup)
+       skipForm: false,
+       
+       constructor: function(node, params){
+               // summary: a constructor of the Container
+               // node: Node: node or node's id to build the container on
+               // params: Object: a dict of parameters, recognized parameters are:
+               //      creator: Function: 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
+               //      _skipStartup: Boolean: skip startup(), which collects children, for deferred initialization
+               //              (this is used in the markup mode)
+               this.node = dojo.byId(node);
+               if(!params){ params = {}; }
+               this.creator = params.creator || null;
+               this.skipForm = params.skipForm;
+               this.defaultCreator = dojo.dnd._defaultCreator(this.node);
+
+               // 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(){},  // 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];   // Object
+       },
+       setItem: function(/*String*/ key, /*Object*/ 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, which 
+               //      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 this.map){
+                       if(i in e){ continue; }
+                       f.call(o, m[i], i, m);
+               }
+       },
+       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
+       },
+       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 the object to be garbage-collected
+               dojo.forEach(this.events, dojo.disconnect);
+               this.clearItems();
+               this.node = this.parent = this.current;
+       },
+
+       // 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
+               this.parent = this.node;
+               if(this.parent.tagName.toLowerCase() == "table"){
+                       var c = this.parent.getElementsByTagName("tbody");
+                       if(c && c.length){ this.parent = c[0]; }
+               }
+
+               // process specially marked children
+               this.getAllNodes().forEach(function(node){
+                       if(!node.id){ node.id = dojo.dnd.getUniqueId(); }
+                       var type = node.getAttribute("dndType"),
+                               data = node.getAttribute("dndData");
+                       this.setItem(node.id, {
+                               data: data ? data : node.innerHTML,
+                               type: type ? type.split(/\s*,\s*/) : ["text"]
+                       });
+               }, this);
+       },
+
+       // 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(item, hint){
+               // summary: adds all necessary data to the output of the user-supplied creator function
+               var t = (this.creator ? this.creator : this.defaultCreator)(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
+               var n = dojo.doc.createElement(tag);
+               n.innerHTML = text;
+               return n;
+       };
+};
+
+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.doc.createElement("tr");
+       var td = dojo.doc.createElement("td");
+       td.innerHTML = text;
+       tr.appendChild(td);
+       return tr;      // Node
+};
+
+dojo.dnd._createSpan = function(text){
+       // summary: creates a SPAN element with given text as its innerHTML
+       // text: String: a text for SPAN
+       var n = dojo.doc.createElement("span");
+       n.innerHTML = text;
+       return n;       // Node
+};
+
+// dojo.dnd._defaultCreatorNodes: Object: a dicitionary, which 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 container node, and returns an appropriate creator function
+       // node: Node: a container node
+       var tag = node.tagName.toLowerCase();
+       var c = tag == "table" ? dojo.dnd._createTrTd : dojo.dnd._createNode(dojo.dnd._defaultCreatorNodes[tag]);
+       return function(item, hint){    // Function
+               var isObj = dojo.isObject(item) && item;
+               var data = (isObj && item.data) ? item.data : item;
+               var type = (isObj && item.type) ? item.type : ["text"];
+               var t = String(data), n = (hint == "avatar" ? dojo.dnd._createSpan : c)(t);
+               n.id = dojo.dnd.getUniqueId();
+               return {node: n, data: data, type: type};
+       };
+};
+
+}