source: Dev/trunk/src/client/dijit/_AttachMixin.js @ 513

Last change on this file since 513 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 7.9 KB
Line 
1define([
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});
Note: See TracBrowser for help on using the repository browser.