UNPKG

dijit

Version:

Dijit provides a complete collection of user interface controls based on Dojo, giving you the power to create web applications that are highly optimized for usability, performance, internationalization, accessibility, but above all deliver an incredible u

239 lines (205 loc) 8.12 kB
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; });