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