You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
238 lines
7.9 KiB
238 lines
7.9 KiB
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; |
|
});
|
|
|