[483] | 1 | define([ |
---|
| 2 | "require", |
---|
| 3 | "dojo/_base/array", // array.forEach |
---|
| 4 | "dojo/_base/connect", // remove for 2.0 |
---|
| 5 | "dojo/_base/declare", // declare |
---|
| 6 | "dojo/_base/lang", // lang.getObject |
---|
| 7 | "dojo/mouse", |
---|
| 8 | "dojo/on", |
---|
| 9 | "dojo/touch", |
---|
| 10 | "./_WidgetBase" |
---|
| 11 | ], function(require, array, connect, declare, lang, mouse, on, touch, _WidgetBase){ |
---|
| 12 | |
---|
| 13 | // module: |
---|
| 14 | // dijit/_AttachMixin |
---|
| 15 | |
---|
| 16 | // Map from string name like "mouseenter" to synthetic event like mouse.enter |
---|
| 17 | var synthEvents = lang.delegate(touch, { |
---|
| 18 | "mouseenter": mouse.enter, |
---|
| 19 | "mouseleave": mouse.leave, |
---|
| 20 | "keypress": connect._keypress // remove for 2.0 |
---|
| 21 | }); |
---|
| 22 | |
---|
| 23 | // To be lightweight, _AttachMixin doesn't require() dijit/a11yclick. |
---|
| 24 | // If the subclass has a template using "ondijitclick", it must load dijit/a11yclick itself. |
---|
| 25 | // In that case, the a11yclick variable below will get set to point to that synthetic event. |
---|
| 26 | var a11yclick; |
---|
| 27 | |
---|
| 28 | var _AttachMixin = declare("dijit._AttachMixin", null, { |
---|
| 29 | // summary: |
---|
| 30 | // Mixin for widgets to attach to dom nodes and setup events via |
---|
| 31 | // convenient data-dojo-attach-point and data-dojo-attach-event DOM attributes. |
---|
| 32 | // |
---|
| 33 | // Superclass of _TemplatedMixin, and can also be used standalone when templates are pre-rendered on the |
---|
| 34 | // server. |
---|
| 35 | // |
---|
| 36 | // Does not [yet] handle widgets like ContentPane with this.containerNode set. It should skip |
---|
| 37 | // scanning for data-dojo-attach-point and data-dojo-attach-event inside this.containerNode, but it |
---|
| 38 | // doesn't. |
---|
| 39 | |
---|
| 40 | /*===== |
---|
| 41 | // _attachPoints: [private] String[] |
---|
| 42 | // List of widget attribute names associated with data-dojo-attach-point=... in the |
---|
| 43 | // template, ex: ["containerNode", "labelNode"] |
---|
| 44 | _attachPoints: [], |
---|
| 45 | |
---|
| 46 | // _attachEvents: [private] Handle[] |
---|
| 47 | // List of connections associated with data-dojo-attach-event=... in the |
---|
| 48 | // template |
---|
| 49 | _attachEvents: [], |
---|
| 50 | |
---|
| 51 | // attachScope: [public] Object |
---|
| 52 | // Object to which attach points and events will be scoped. Defaults |
---|
| 53 | // to 'this'. |
---|
| 54 | attachScope: undefined, |
---|
| 55 | |
---|
| 56 | // searchContainerNode: [protected] Boolean |
---|
| 57 | // Search descendants of this.containerNode for data-dojo-attach-point and data-dojo-attach-event. |
---|
| 58 | // Should generally be left false (the default value) both for performance and to avoid failures when |
---|
| 59 | // this.containerNode holds other _AttachMixin instances with their own attach points and events. |
---|
| 60 | searchContainerNode: false, |
---|
| 61 | =====*/ |
---|
| 62 | |
---|
| 63 | constructor: function(/*===== params, srcNodeRef =====*/){ |
---|
| 64 | // summary: |
---|
| 65 | // Create the widget. |
---|
| 66 | // params: Object|null |
---|
| 67 | // Hash of initialization parameters for widget, including scalar values (like title, duration etc.) |
---|
| 68 | // and functions, typically callbacks like onClick. |
---|
| 69 | // The hash can contain any of the widget's properties, excluding read-only properties. |
---|
| 70 | // srcNodeRef: DOMNode|String? |
---|
| 71 | // If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree. |
---|
| 72 | |
---|
| 73 | this._attachPoints = []; |
---|
| 74 | this._attachEvents = []; |
---|
| 75 | }, |
---|
| 76 | |
---|
| 77 | |
---|
| 78 | buildRendering: function(){ |
---|
| 79 | // summary: |
---|
| 80 | // Attach to DOM nodes marked with special attributes. |
---|
| 81 | // tags: |
---|
| 82 | // protected |
---|
| 83 | |
---|
| 84 | this.inherited(arguments); |
---|
| 85 | |
---|
| 86 | // recurse through the node, looking for, and attaching to, our |
---|
| 87 | // attachment points and events, which should be defined on the template node. |
---|
| 88 | this._attachTemplateNodes(this.domNode); |
---|
| 89 | |
---|
| 90 | this._beforeFillContent(); // hook for _WidgetsInTemplateMixin |
---|
| 91 | }, |
---|
| 92 | |
---|
| 93 | _beforeFillContent: function(){ |
---|
| 94 | }, |
---|
| 95 | |
---|
| 96 | _attachTemplateNodes: function(rootNode){ |
---|
| 97 | // summary: |
---|
| 98 | // Iterate through the dom nodes and attach functions and nodes accordingly. |
---|
| 99 | // description: |
---|
| 100 | // Map widget properties and functions to the handlers specified in |
---|
| 101 | // the dom node and it's descendants. This function iterates over all |
---|
| 102 | // nodes and looks for these properties: |
---|
| 103 | // |
---|
| 104 | // - dojoAttachPoint/data-dojo-attach-point |
---|
| 105 | // - dojoAttachEvent/data-dojo-attach-event |
---|
| 106 | // rootNode: DomNode |
---|
| 107 | // The node to search for properties. All descendants will be searched. |
---|
| 108 | // tags: |
---|
| 109 | // private |
---|
| 110 | |
---|
| 111 | // DFS to process all nodes except those inside of this.containerNode |
---|
| 112 | var node = rootNode; |
---|
| 113 | while(true){ |
---|
| 114 | if(node.nodeType == 1 && (this._processTemplateNode(node, function(n,p){ return n.getAttribute(p); }, |
---|
| 115 | this._attach) || this.searchContainerNode) && node.firstChild){ |
---|
| 116 | node = node.firstChild; |
---|
| 117 | }else{ |
---|
| 118 | if(node == rootNode){ return; } |
---|
| 119 | while(!node.nextSibling){ |
---|
| 120 | node = node.parentNode; |
---|
| 121 | if(node == rootNode){ return; } |
---|
| 122 | } |
---|
| 123 | node = node.nextSibling; |
---|
| 124 | } |
---|
| 125 | } |
---|
| 126 | }, |
---|
| 127 | |
---|
| 128 | _processTemplateNode: function(/*DOMNode|Widget*/ baseNode, getAttrFunc, attachFunc){ |
---|
| 129 | // summary: |
---|
| 130 | // Process data-dojo-attach-point and data-dojo-attach-event for given node or widget. |
---|
| 131 | // Returns true if caller should process baseNode's children too. |
---|
| 132 | |
---|
| 133 | var ret = true; |
---|
| 134 | |
---|
| 135 | // Process data-dojo-attach-point |
---|
| 136 | var _attachScope = this.attachScope || this, |
---|
| 137 | attachPoint = getAttrFunc(baseNode, "dojoAttachPoint") || getAttrFunc(baseNode, "data-dojo-attach-point"); |
---|
| 138 | if(attachPoint){ |
---|
| 139 | var point, points = attachPoint.split(/\s*,\s*/); |
---|
| 140 | while((point = points.shift())){ |
---|
| 141 | if(lang.isArray(_attachScope[point])){ |
---|
| 142 | _attachScope[point].push(baseNode); |
---|
| 143 | }else{ |
---|
| 144 | _attachScope[point] = baseNode; |
---|
| 145 | } |
---|
| 146 | ret = (point != "containerNode"); |
---|
| 147 | this._attachPoints.push(point); |
---|
| 148 | } |
---|
| 149 | } |
---|
| 150 | |
---|
| 151 | // Process data-dojo-attach-event |
---|
| 152 | var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent") || getAttrFunc(baseNode, "data-dojo-attach-event"); |
---|
| 153 | if(attachEvent){ |
---|
| 154 | // NOTE: we want to support attributes that have the form |
---|
| 155 | // "domEvent: nativeEvent; ..." |
---|
| 156 | var event, events = attachEvent.split(/\s*,\s*/); |
---|
| 157 | var trim = lang.trim; |
---|
| 158 | while((event = events.shift())){ |
---|
| 159 | if(event){ |
---|
| 160 | var thisFunc = null; |
---|
| 161 | if(event.indexOf(":") != -1){ |
---|
| 162 | // oh, if only JS had tuple assignment |
---|
| 163 | var funcNameArr = event.split(":"); |
---|
| 164 | event = trim(funcNameArr[0]); |
---|
| 165 | thisFunc = trim(funcNameArr[1]); |
---|
| 166 | }else{ |
---|
| 167 | event = trim(event); |
---|
| 168 | } |
---|
| 169 | if(!thisFunc){ |
---|
| 170 | thisFunc = event; |
---|
| 171 | } |
---|
| 172 | |
---|
| 173 | this._attachEvents.push(attachFunc(baseNode, event, lang.hitch(_attachScope, thisFunc))); |
---|
| 174 | } |
---|
| 175 | } |
---|
| 176 | } |
---|
| 177 | |
---|
| 178 | return ret; |
---|
| 179 | }, |
---|
| 180 | |
---|
| 181 | _attach: function(node, type, func){ |
---|
| 182 | // summary: |
---|
| 183 | // Roughly corresponding to dojo/on, this is the default function for processing a |
---|
| 184 | // data-dojo-attach-event. Meant to attach to DOMNodes, not to widgets. |
---|
| 185 | // node: DOMNode |
---|
| 186 | // The node to setup a listener on. |
---|
| 187 | // type: String |
---|
| 188 | // Event name like "click". |
---|
| 189 | // getAttrFunc: Function |
---|
| 190 | // Function to get the specified property for a given DomNode/Widget. |
---|
| 191 | // attachFunc: Function? |
---|
| 192 | // Attaches an event handler from the specified node/widget to specified function. |
---|
| 193 | |
---|
| 194 | // Map special type names like "mouseenter" to synthetic events. |
---|
| 195 | // Subclasses are responsible to require() dijit/a11yclick if they want to use it. |
---|
| 196 | type = type.replace(/^on/, "").toLowerCase(); |
---|
| 197 | if(type == "dijitclick"){ |
---|
| 198 | type = a11yclick || (a11yclick = require("./a11yclick")); |
---|
| 199 | }else{ |
---|
| 200 | type = synthEvents[type] || type; |
---|
| 201 | } |
---|
| 202 | |
---|
| 203 | return on(node, type, func); |
---|
| 204 | }, |
---|
| 205 | |
---|
| 206 | _detachTemplateNodes: function() { |
---|
| 207 | // summary: |
---|
| 208 | // Detach and clean up the attachments made in _attachtempalteNodes. |
---|
| 209 | |
---|
| 210 | // Delete all attach points to prevent IE6 memory leaks. |
---|
| 211 | var _attachScope = this.attachScope || this; |
---|
| 212 | array.forEach(this._attachPoints, function(point){ |
---|
| 213 | delete _attachScope[point]; |
---|
| 214 | }); |
---|
| 215 | this._attachPoints = []; |
---|
| 216 | |
---|
| 217 | // And same for event handlers |
---|
| 218 | array.forEach(this._attachEvents, function(handle){ handle.remove(); }); |
---|
| 219 | this._attachEvents = []; |
---|
| 220 | }, |
---|
| 221 | |
---|
| 222 | destroyRendering: function(){ |
---|
| 223 | this._detachTemplateNodes(); |
---|
| 224 | this.inherited(arguments); |
---|
| 225 | } |
---|
| 226 | }); |
---|
| 227 | |
---|
| 228 | // These arguments can be specified for widgets which are used in templates. |
---|
| 229 | // Since any widget can be specified as sub widgets in template, mix it |
---|
| 230 | // into the base widget class. (This is a hack, but it's effective.). |
---|
| 231 | // Remove for 2.0. Also, hide from API doc parser. |
---|
| 232 | lang.extend(_WidgetBase, /*===== {} || =====*/ { |
---|
| 233 | dojoAttachEvent: "", |
---|
| 234 | dojoAttachPoint: "" |
---|
| 235 | }); |
---|
| 236 | |
---|
| 237 | return _AttachMixin; |
---|
| 238 | }); |
---|