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 | }); |
---|