You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

239 lines
7.9 KiB
JavaScript

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