require({cache:{
'dijit/main':function(){
define([
"dojo/_base/kernel"
], function(dojo){
// module:
// dijit/main
/*=====
return {
// summary:
// The dijit package main module.
// Deprecated. Users should access individual modules (ex: dijit/registry) directly.
};
=====*/
return dojo.dijit;
});
},
'dojox/main':function(){
define(["dojo/_base/kernel"], function(dojo) {
// module:
// dojox/main
/*=====
return {
// summary:
// The dojox package main module; dojox package is somewhat unusual in that the main module currently just provides an empty object.
// Apps should require modules from the dojox packages directly, rather than loading this module.
};
=====*/
return dojo.dojox;
});
},
'dojo/require':function(){
define(["./_base/loader"], function(loader){
return {
dynamic:0,
normalize:function(id){return id;},
load:loader.require
};
});
},
'demos/cropper/src/Preview':function(){
// wrapped by build app
define(["dojo","dijit","dojox","dojo/require!dijit/_Widget,dojo/dnd/move,dojox/layout/ResizeHandle"], function(dojo,dijit,dojox){
dojo.provide("demos.cropper.src.Preview");
dojo.require("dijit._Widget");
dojo.require("dojo.dnd.move");
dojo.require("dojox.layout.ResizeHandle");
(function(d, $){
// a simple "wrap" function. availaing in `plugd`
// warning: node must be in the DOM before being wrapped.
var wrap = function(node, withTag){
// summary:
// wrap some node with a tag type.
var n = d.create(withTag);
d.place(n, node, "before");
d.place(node, n, "first");
return n;
},
// some quick aliases for shrinksafe:
abs = "absolute",
pos = "position",
pixels = "px",
_floor = Math.floor;
d.declare("image.Preview", dijit._Widget, {
// summary:
// A Behavioral widget adding a preview pane to any ``
// the size of the draggable/moveable window
glassSize:150,
// the size of the preview window relative to the glassSize
scale:2,
// unsupported atm:
withMouseMove:false,
withDrag:true,
// moveInterval: Int
// Adjust the time the preview redraws from dnd operations (ms)
moveInterval: 50,
// hoverable: Boolean
// Does this image react to hovering to show/hide the dragger
hoverable: false,
// resizeable: Boolean
// Can the glass be resized, and provide zooming of the preview?
resizeable: true,
// opacity: Float
// Opacity value to use in hoverable or non-hoverable cases.
opacity: 0.35,
postCreate: function(){
var gs = this.glassSize,
s = this.scale;
// wrap the target in a div so we can control it with dnd.
var mb = d.marginBox(this.domNode);
this.currentSize = mb;
// wrap the domNode in antoher div so parentDnd works
this.container = wrap(this.domNode, "div");
d.marginBox(this.container, mb);
// create a draggable handle thing over our target
this.picker = d.create('div', {
"class":"imageDragger",
style: {
opacity: this.hoverable ? 0 : this.opacity,
width: gs + pixels,
height: gs + pixels
}
}, this.domNode, "before");
// create the preview node. _positionPicker places it.
this.preview = d.create('div', {
style:{
position:abs,
overflow:"hidden",
width: _floor(gs * s) + pixels,
height: _floor(gs * s) + pixels
}
}, d.body());
// wrap the full image, so we can position it easily relative
// to the outer node. embedded position:rel/absolute/rel/abs here
var n = wrap(d.create('img', {
style:{ position: abs },
src: this.altSrc || this.domNode.src
}, this.preview), "div");
d.style(n, pos, "relative");
d.style(this.domNode, pos, abs);
this.image = d.query('img', this.preview)
// when it is loaded, use those numbers:
.onload(d.hitch(this, "_adjustImage"))[0]; // the [0] is for the array
this._positionPicker();
// setup dnd for the picker:
this.mover = new d.dnd.move.parentConstrainedMoveable(this.picker,
// Safari and IE seem to be pickup up paddings and whatnot
// as container? ugh.
{ area: "content", within: true }
);
if(this.resizeable){
// create the resize handle for the glass:
this._handle = new dojox.layout.ResizeHandle({
// the draggable node is the one to resize.
// maintain aspect to keep inline with preview
targetContainer: this.picker,
fixedAspect: true,
// rescale the image often, activeResize is true
intermediateChanges: true,
activeResize: true,
onResize: d.hitch(this, function(e,f){
this._adjustImage(e,f);
this._whileMoving();
}),
// constrain to box.
constrainMax: true,
maxWidth: mb.w,
maxHeight: mb.h,
// sane default minimums:
minWidth:40,
minHeight:40
}).placeAt(this.picker);
}
// setup dnd behavior
d.subscribe("/dnd/move/start", this, "_startDnd");
d.subscribe("/dnd/move/stop", this, "_stopDnd");
// janky IE src issue fix:
d.isIE && (this.image.src = this.image.src);
// only because our layout is fluid:
this.connect(d.global, "onresize", "_positionPicker");
if(this.hoverable){
this.connect(this.container, "onmouseenter", "_enter");
this.connect(this.container, "onmouseleave", "_leave");
}
setTimeout(dojo.hitch(this, "_positionPicker"), 125);
},
_adjustImage: function(e){
var tc = this.coords,
s = this.scale;
// if we were called from the resizehandle, we probably need to adjust our scale.
if(e && e.type && (e.type == "mouseup" || e.type == "mousemove")){
var xy = d.coords(this.picker);
this.scale = d.coords(this.preview).w / xy.w;
}else if(e && e.type && e.type == "load" && this.imageReady(e)){ /* noop, imageReady fired */ }
d.style(this.image, {
height: _floor(tc.h * s) + pixels,
width: _floor(tc.w * s) + pixels
});
},
_positionPicker: function(e){
// place the preview thinger somewhere relative to the container
// we wrapped around the orig image.
var tc = this.coords = d.coords(this.container, true);
d.style(this.preview,{
left: tc.x + tc.w + 10 + pixels,
top: tc.y + pixels
});
},
_startDnd: function(n){
// listen for dnd Start, determine if we care:
if(!this._interval && n && n.node == this.picker){
// listen to doc onmousemove
this._interval = this.connect(d.doc, "onmousemove", "_whileMoving");
}
},
_stopDnd: function(){
// dnd operations are done, remove the timer
if(this._interval){
this.disconnect(this._interval);
delete this._interval; // remember to delete it ;)
if(this.resizeable && this._lastXY){
// update the max size values of our handle to
// be based on our current position, only allowing
// for the glass to be resized to the offset box
// we're in (eg, @ top 50 with size 50, max next
// size is 250). if top 0, use orig container size.
var tc = this.coords;
this._handle.maxSize = {
h: _floor(tc.h - (this._lastXY.t - tc.t)),
w: _floor(tc.w - (this._lastXY.l - tc.l))
}
}
}
},
_whileMoving: function(){
// while dnd in progress, adjust the backgroundPosition of the preview
var xy = this._lastXY = d.coords(this.picker),
tc = this.coords,
r = this.image.width / tc.w,
x = _floor((xy.l - tc.l) * r),
y = _floor((xy.t - tc.t) * r);
// position the image relative to the picker's position
// in the container.
d.style(this.image, {
top: "-" + y + pixels,
left: "-" + x + pixels
});
},
destroy: function(){
// destroy our domNodes. this is a behavioral widget.
// we can .destroy(true) and leave the image in tact
d.place(this.domNode, this.container, "before");
d.forEach(["preview","picker","container","image"], function(n){
d.destroy(this[n]);
delete this[n];
}, this);
this.inherited(arguments);
},
imageReady: function(){
// stub fired anytime the preview.onload happens
},
_enter: function(e){
// handler for mouseenter event
this._anim && this._anim.stop();
this._anim = d.anim(this.picker, { opacity: this.opacity });
},
_leave: function(e){
// handler for mouseleave event
if(!this._interval && !this._handle._isSizing){
this._anim && this._anim.stop();
this._anim = d.anim(this.picker, { opacity: 0 });
}
}
});
// setup the query "plugin"
d.extend(d.NodeList, {
preview: function(args){
// example: dojo.query(".something").preview();
return this.instantiate(image.Preview, args);
}
});
})(dojo, dojo.query);
});
},
'dijit/_Widget':function(){
define([
"dojo/aspect", // aspect.around
"dojo/_base/config", // config.isDebug
"dojo/_base/connect", // connect.connect
"dojo/_base/declare", // declare
"dojo/has",
"dojo/_base/kernel", // kernel.deprecated
"dojo/_base/lang", // lang.hitch
"dojo/query",
"dojo/ready",
"./registry", // registry.byNode
"./_WidgetBase",
"./_OnDijitClickMixin",
"./_FocusMixin",
"dojo/uacss", // browser sniffing (included for back-compat; subclasses may be using)
"./hccss" // high contrast mode sniffing (included to set CSS classes on
, module ret value unused)
], function(aspect, config, connect, declare, has, kernel, lang, query, ready,
registry, _WidgetBase, _OnDijitClickMixin, _FocusMixin){
// module:
// dijit/_Widget
function connectToDomNode(){
// summary:
// If user connects to a widget method === this function, then they will
// instead actually be connecting the equivalent event on this.domNode
}
// Trap dojo.connect() calls to connectToDomNode methods, and redirect to _Widget.on()
function aroundAdvice(originalConnect){
return function(obj, event, scope, method){
if(obj && typeof event == "string" && obj[event] == connectToDomNode){
return obj.on(event.substring(2).toLowerCase(), lang.hitch(scope, method));
}
return originalConnect.apply(connect, arguments);
};
}
aspect.around(connect, "connect", aroundAdvice);
if(kernel.connect){
aspect.around(kernel, "connect", aroundAdvice);
}
var _Widget = declare("dijit._Widget", [_WidgetBase, _OnDijitClickMixin, _FocusMixin], {
// summary:
// Old base class for widgets. New widgets should extend `dijit/_WidgetBase` instead
// description:
// Old Base class for Dijit widgets.
//
// Extends _WidgetBase, adding support for:
//
// - declaratively/programatically specifying widget initialization parameters like
// onMouseMove="foo" that call foo when this.domNode gets a mousemove event
// - ondijitclick:
// Support new data-dojo-attach-event="ondijitclick: ..." that is triggered by a mouse click or a SPACE/ENTER keypress
// - focus related functions:
// In particular, the onFocus()/onBlur() callbacks. Driven internally by
// dijit/_base/focus.js.
// - deprecated methods
// - onShow(), onHide(), onClose()
//
// Also, by loading code in dijit/_base, turns on:
//
// - browser sniffing (putting browser class like `dj_ie` on `` node)
// - high contrast mode sniffing (add `dijit_a11y` class to `` if machine is in high contrast mode)
////////////////// DEFERRED CONNECTS ///////////////////
onClick: connectToDomNode,
/*=====
onClick: function(event){
// summary:
// Connect to this function to receive notifications of mouse click events.
// event:
// mouse Event
// tags:
// callback
},
=====*/
onDblClick: connectToDomNode,
/*=====
onDblClick: function(event){
// summary:
// Connect to this function to receive notifications of mouse double click events.
// event:
// mouse Event
// tags:
// callback
},
=====*/
onKeyDown: connectToDomNode,
/*=====
onKeyDown: function(event){
// summary:
// Connect to this function to receive notifications of keys being pressed down.
// event:
// key Event
// tags:
// callback
},
=====*/
onKeyPress: connectToDomNode,
/*=====
onKeyPress: function(event){
// summary:
// Connect to this function to receive notifications of printable keys being typed.
// event:
// key Event
// tags:
// callback
},
=====*/
onKeyUp: connectToDomNode,
/*=====
onKeyUp: function(event){
// summary:
// Connect to this function to receive notifications of keys being released.
// event:
// key Event
// tags:
// callback
},
=====*/
onMouseDown: connectToDomNode,
/*=====
onMouseDown: function(event){
// summary:
// Connect to this function to receive notifications of when the mouse button is pressed down.
// event:
// mouse Event
// tags:
// callback
},
=====*/
onMouseMove: connectToDomNode,
/*=====
onMouseMove: function(event){
// summary:
// Connect to this function to receive notifications of when the mouse moves over nodes contained within this widget.
// event:
// mouse Event
// tags:
// callback
},
=====*/
onMouseOut: connectToDomNode,
/*=====
onMouseOut: function(event){
// summary:
// Connect to this function to receive notifications of when the mouse moves off of nodes contained within this widget.
// event:
// mouse Event
// tags:
// callback
},
=====*/
onMouseOver: connectToDomNode,
/*=====
onMouseOver: function(event){
// summary:
// Connect to this function to receive notifications of when the mouse moves onto nodes contained within this widget.
// event:
// mouse Event
// tags:
// callback
},
=====*/
onMouseLeave: connectToDomNode,
/*=====
onMouseLeave: function(event){
// summary:
// Connect to this function to receive notifications of when the mouse moves off of this widget.
// event:
// mouse Event
// tags:
// callback
},
=====*/
onMouseEnter: connectToDomNode,
/*=====
onMouseEnter: function(event){
// summary:
// Connect to this function to receive notifications of when the mouse moves onto this widget.
// event:
// mouse Event
// tags:
// callback
},
=====*/
onMouseUp: connectToDomNode,
/*=====
onMouseUp: function(event){
// summary:
// Connect to this function to receive notifications of when the mouse button is released.
// event:
// mouse Event
// tags:
// callback
},
=====*/
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:
//
// - use srcNodeRef.innerHTML as my contents
// - if this is a behavioral widget then apply behavior to that srcNodeRef
// - otherwise, replace srcNodeRef with my generated DOM tree
// extract parameters like onMouseMove that should connect directly to this.domNode
this._toConnect = {};
for(var name in params){
if(this[name] === connectToDomNode){
this._toConnect[name.replace(/^on/, "").toLowerCase()] = params[name];
delete params[name];
}
}
},
postCreate: function(){
this.inherited(arguments);
// perform connection from this.domNode to user specified handlers (ex: onMouseMove)
for(var name in this._toConnect){
this.on(name, this._toConnect[name]);
}
delete this._toConnect;
},
on: function(/*String|Function*/ type, /*Function*/ func){
if(this[this._onMap(type)] === connectToDomNode){
// Use connect.connect() rather than on() to get handling for "onmouseenter" on non-IE,
// normalization of onkeypress/onkeydown to behave like firefox, etc.
// Also, need to specify context as "this" rather than the default context of the DOMNode
// Remove in 2.0.
return connect.connect(this.domNode, type.toLowerCase(), this, func);
}
return this.inherited(arguments);
},
_setFocusedAttr: function(val){
// Remove this method in 2.0 (or sooner), just here to set _focused == focused, for back compat
// (but since it's a private variable we aren't required to keep supporting it).
this._focused = val;
this._set("focused", val);
},
////////////////// DEPRECATED METHODS ///////////////////
setAttribute: function(/*String*/ attr, /*anything*/ value){
// summary:
// Deprecated. Use set() instead.
// tags:
// deprecated
kernel.deprecated(this.declaredClass+"::setAttribute(attr, value) is deprecated. Use set() instead.", "", "2.0");
this.set(attr, value);
},
attr: function(/*String|Object*/name, /*Object?*/value){
// summary:
// This method is deprecated, use get() or set() directly.
// name:
// The property to get or set. If an object is passed here and not
// a string, its keys are used as names of attributes to be set
// and the value of the object as values to set in the widget.
// value:
// Optional. If provided, attr() operates as a setter. If omitted,
// the current value of the named property is returned.
// tags:
// deprecated
var args = arguments.length;
if(args >= 2 || typeof name === "object"){ // setter
return this.set.apply(this, arguments);
}else{ // getter
return this.get(name);
}
},
getDescendants: function(){
// summary:
// Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
// This method should generally be avoided as it returns widgets declared in templates, which are
// supposed to be internal/hidden, but it's left here for back-compat reasons.
kernel.deprecated(this.declaredClass+"::getDescendants() is deprecated. Use getChildren() instead.", "", "2.0");
return this.containerNode ? query('[widgetId]', this.containerNode).map(registry.byNode) : []; // dijit/_WidgetBase[]
},
////////////////// MISCELLANEOUS METHODS ///////////////////
_onShow: function(){
// summary:
// Internal method called when this widget is made visible.
// See `onShow` for details.
this.onShow();
},
onShow: function(){
// summary:
// Called when this widget becomes the selected pane in a
// `dijit/layout/TabContainer`, `dijit/layout/StackContainer`,
// `dijit/layout/AccordionContainer`, etc.
//
// Also called to indicate display of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
// tags:
// callback
},
onHide: function(){
// summary:
// Called when another widget becomes the selected pane in a
// `dijit/layout/TabContainer`, `dijit/layout/StackContainer`,
// `dijit/layout/AccordionContainer`, etc.
//
// Also called to indicate hide of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
// tags:
// callback
},
onClose: function(){
// summary:
// Called when this widget is being displayed as a popup (ex: a Calendar popped
// up from a DateTextBox), and it is hidden.
// This is called from the dijit.popup code, and should not be called directly.
//
// Also used as a parameter for children of `dijit/layout/StackContainer` or subclasses.
// Callback if a user tries to close the child. Child will be closed if this function returns true.
// tags:
// extension
return true; // Boolean
}
});
// For back-compat, remove in 2.0.
if(has("dijit-legacy-requires")){
ready(0, function(){
var requires = ["dijit/_base"];
require(requires); // use indirection so modules not rolled into a build
});
}
return _Widget;
});
},
'dijit/registry':function(){
define([
"dojo/_base/array", // array.forEach array.map
"dojo/_base/window", // win.body
"./main" // dijit._scopeName
], function(array, win, dijit){
// module:
// dijit/registry
var _widgetTypeCtr = {}, hash = {};
var registry = {
// summary:
// Registry of existing widget on page, plus some utility methods.
// length: Number
// Number of registered widgets
length: 0,
add: function(widget){
// summary:
// Add a widget to the registry. If a duplicate ID is detected, a error is thrown.
// widget: dijit/_WidgetBase
// Any dijit/_WidgetBase subclass.
if(hash[widget.id]){
throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
}
hash[widget.id] = widget;
this.length++;
},
remove: function(/*String*/ id){
// summary:
// Remove a widget from the registry. Does not destroy the widget; simply
// removes the reference.
if(hash[id]){
delete hash[id];
this.length--;
}
},
byId: function(/*String|Widget*/ id){
// summary:
// Find a widget by it's id.
// If passed a widget then just returns the widget.
return typeof id == "string" ? hash[id] : id; // dijit/_WidgetBase
},
byNode: function(/*DOMNode*/ node){
// summary:
// Returns the widget corresponding to the given DOMNode
return hash[node.getAttribute("widgetId")]; // dijit/_WidgetBase
},
toArray: function(){
// summary:
// Convert registry into a true Array
//
// example:
// Work with the widget .domNodes in a real Array
// | array.map(registry.toArray(), function(w){ return w.domNode; });
var ar = [];
for(var id in hash){
ar.push(hash[id]);
}
return ar; // dijit/_WidgetBase[]
},
getUniqueId: function(/*String*/widgetType){
// summary:
// Generates a unique id for a given widgetType
var id;
do{
id = widgetType + "_" +
(widgetType in _widgetTypeCtr ?
++_widgetTypeCtr[widgetType] : _widgetTypeCtr[widgetType] = 0);
}while(hash[id]);
return dijit._scopeName == "dijit" ? id : dijit._scopeName + "_" + id; // String
},
findWidgets: function(root, skipNode){
// summary:
// Search subtree under root returning widgets found.
// Doesn't search for nested widgets (ie, widgets inside other widgets).
// root: DOMNode
// Node to search under.
// skipNode: DOMNode
// If specified, don't search beneath this node (usually containerNode).
var outAry = [];
function getChildrenHelper(root){
for(var node = root.firstChild; node; node = node.nextSibling){
if(node.nodeType == 1){
var widgetId = node.getAttribute("widgetId");
if(widgetId){
var widget = hash[widgetId];
if(widget){ // may be null on page w/multiple dojo's loaded
outAry.push(widget);
}
}else if(node !== skipNode){
getChildrenHelper(node);
}
}
}
}
getChildrenHelper(root);
return outAry;
},
_destroyAll: function(){
// summary:
// Code to destroy all widgets and do other cleanup on page unload
// Clean up focus manager lingering references to widgets and nodes
dijit._curFocus = null;
dijit._prevFocus = null;
dijit._activeStack = [];
// Destroy all the widgets, top down
array.forEach(registry.findWidgets(win.body()), function(widget){
// Avoid double destroy of widgets like Menu that are attached to
// even though they are logically children of other widgets.
if(!widget._destroyed){
if(widget.destroyRecursive){
widget.destroyRecursive();
}else if(widget.destroy){
widget.destroy();
}
}
});
},
getEnclosingWidget: function(/*DOMNode*/ node){
// summary:
// Returns the widget whose DOM tree contains the specified DOMNode, or null if
// the node is not contained within the DOM tree of any widget
while(node){
var id = node.nodeType == 1 && node.getAttribute("widgetId");
if(id){
return hash[id];
}
node = node.parentNode;
}
return null;
},
// In case someone needs to access hash.
// Actually, this is accessed from WidgetSet back-compatibility code
_hash: hash
};
dijit.registry = registry;
return registry;
});
},
'dijit/_WidgetBase':function(){
define([
"require", // require.toUrl
"dojo/_base/array", // array.forEach array.map
"dojo/aspect",
"dojo/_base/config", // config.blankGif
"dojo/_base/connect", // connect.connect
"dojo/_base/declare", // declare
"dojo/dom", // dom.byId
"dojo/dom-attr", // domAttr.set domAttr.remove
"dojo/dom-class", // domClass.add domClass.replace
"dojo/dom-construct", // domConstruct.destroy domConstruct.place
"dojo/dom-geometry", // isBodyLtr
"dojo/dom-style", // domStyle.set, domStyle.get
"dojo/has",
"dojo/_base/kernel",
"dojo/_base/lang", // mixin(), isArray(), etc.
"dojo/on",
"dojo/ready",
"dojo/Stateful", // Stateful
"dojo/topic",
"dojo/_base/window", // win.body()
"./Destroyable",
"dojo/has!dojo-bidi?./_BidiMixin",
"./registry" // registry.getUniqueId(), registry.findWidgets()
], function(require, array, aspect, config, connect, declare,
dom, domAttr, domClass, domConstruct, domGeometry, domStyle, has, kernel,
lang, on, ready, Stateful, topic, win, Destroyable, _BidiMixin, registry){
// module:
// dijit/_WidgetBase
// Flag to make dijit load modules the app didn't explicitly request, for backwards compatibility
has.add("dijit-legacy-requires", !kernel.isAsync);
// Flag to enable support for textdir attribute
has.add("dojo-bidi", false);
// For back-compat, remove in 2.0.
if(has("dijit-legacy-requires")){
ready(0, function(){
var requires = ["dijit/_base/manager"];
require(requires); // use indirection so modules not rolled into a build
});
}
// Nested hash listing attributes for each tag, all strings in lowercase.
// ex: {"div": {"style": true, "tabindex" true}, "form": { ...
var tagAttrs = {};
function getAttrs(obj){
var ret = {};
for(var attr in obj){
ret[attr.toLowerCase()] = true;
}
return ret;
}
function nonEmptyAttrToDom(attr){
// summary:
// Returns a setter function that copies the attribute to this.domNode,
// or removes the attribute from this.domNode, depending on whether the
// value is defined or not.
return function(val){
domAttr[val ? "set" : "remove"](this.domNode, attr, val);
this._set(attr, val);
};
}
function isEqual(a, b){
// summary:
// Function that determines whether two values are identical,
// taking into account that NaN is not normally equal to itself
// in JS.
return a === b || (/* a is NaN */ a !== a && /* b is NaN */ b !== b);
}
var _WidgetBase = declare("dijit._WidgetBase", [Stateful, Destroyable], {
// summary:
// Future base class for all Dijit widgets.
// description:
// Future base class for all Dijit widgets.
// _Widget extends this class adding support for various features needed by desktop.
//
// Provides stubs for widget lifecycle methods for subclasses to extend, like postMixInProperties(), buildRendering(),
// postCreate(), startup(), and destroy(), and also public API methods like set(), get(), and watch().
//
// Widgets can provide custom setters/getters for widget attributes, which are called automatically by set(name, value).
// For an attribute XXX, define methods _setXXXAttr() and/or _getXXXAttr().
//
// _setXXXAttr can also be a string/hash/array mapping from a widget attribute XXX to the widget's DOMNodes:
//
// - DOM node attribute
// | _setFocusAttr: {node: "focusNode", type: "attribute"}
// | _setFocusAttr: "focusNode" (shorthand)
// | _setFocusAttr: "" (shorthand, maps to this.domNode)
// Maps this.focus to this.focusNode.focus, or (last example) this.domNode.focus
//
// - DOM node innerHTML
// | _setTitleAttr: { node: "titleNode", type: "innerHTML" }
// Maps this.title to this.titleNode.innerHTML
//
// - DOM node innerText
// | _setTitleAttr: { node: "titleNode", type: "innerText" }
// Maps this.title to this.titleNode.innerText
//
// - DOM node CSS class
// | _setMyClassAttr: { node: "domNode", type: "class" }
// Maps this.myClass to this.domNode.className
//
// - Toggle DOM node CSS class
// | _setMyClassAttr: { node: "domNode", type: "toggleClass" }
// Toggles myClass on this.domNode by this.myClass
//
// If the value of _setXXXAttr is an array, then each element in the array matches one of the
// formats of the above list.
//
// If the custom setter is null, no action is performed other than saving the new value
// in the widget (in this).
//
// If no custom setter is defined for an attribute, then it will be copied
// to this.focusNode (if the widget defines a focusNode), or this.domNode otherwise.
// That's only done though for attributes that match DOMNode attributes (title,
// alt, aria-labelledby, etc.)
// id: [const] String
// A unique, opaque ID string that can be assigned by users or by the
// system. If the developer passes an ID which is known not to be
// unique, the specified ID is ignored and the system-generated ID is
// used instead.
id: "",
_setIdAttr: "domNode", // to copy to this.domNode even for auto-generated id's
// lang: [const] String
// Rarely used. Overrides the default Dojo locale used to render this widget,
// as defined by the [HTML LANG](http://www.w3.org/TR/html401/struct/dirlang.html#adef-lang) attribute.
// Value must be among the list of locales specified during by the Dojo bootstrap,
// formatted according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt) (like en-us).
lang: "",
// set on domNode even when there's a focus node. but don't set lang="", since that's invalid.
_setLangAttr: nonEmptyAttrToDom("lang"),
// dir: [const] String
// Bi-directional support, as defined by the [HTML DIR](http://www.w3.org/TR/html401/struct/dirlang.html#adef-dir)
// attribute. Either left-to-right "ltr" or right-to-left "rtl". If undefined, widgets renders in page's
// default direction.
dir: "",
// set on domNode even when there's a focus node. but don't set dir="", since that's invalid.
_setDirAttr: nonEmptyAttrToDom("dir"), // to set on domNode even when there's a focus node
// class: String
// HTML class attribute
"class": "",
_setClassAttr: { node: "domNode", type: "class" },
// Override automatic assigning type --> focusNode, it causes exception on IE6-8.
// Instead, type must be specified as ${type} in the template, as part of the original DOM.
_setTypeAttr: null,
// style: String||Object
// HTML style attributes as cssText string or name/value hash
style: "",
// title: String
// HTML title attribute.
//
// For form widgets this specifies a tooltip to display when hovering over
// the widget (just like the native HTML title attribute).
//
// For TitlePane or for when this widget is a child of a TabContainer, AccordionContainer,
// etc., it's used to specify the tab label, accordion pane title, etc. In this case it's
// interpreted as HTML.
title: "",
// tooltip: String
// When this widget's title attribute is used to for a tab label, accordion pane title, etc.,
// this specifies the tooltip to appear when the mouse is hovered over that text.
tooltip: "",
// baseClass: [protected] String
// Root CSS class of the widget (ex: dijitTextBox), used to construct CSS classes to indicate
// widget state.
baseClass: "",
// srcNodeRef: [readonly] DomNode
// pointer to original DOM node
srcNodeRef: null,
// domNode: [readonly] DomNode
// This is our visible representation of the widget! Other DOM
// Nodes may by assigned to other properties, usually through the
// template system's data-dojo-attach-point syntax, but the domNode
// property is the canonical "top level" node in widget UI.
domNode: null,
// containerNode: [readonly] DomNode
// Designates where children of the source DOM node will be placed.
// "Children" in this case refers to both DOM nodes and widgets.
// For example, for myWidget:
//
// |
// | here's a plain DOM node
// | and a widget
// | and another plain DOM node
// |
//
// containerNode would point to:
//
// | here's a plain DOM node
// | and a widget
// | and another plain DOM node
//
// In templated widgets, "containerNode" is set via a
// data-dojo-attach-point assignment.
//
// containerNode must be defined for any widget that accepts innerHTML
// (like ContentPane or BorderContainer or even Button), and conversely
// is null for widgets that don't, like TextBox.
containerNode: null,
// ownerDocument: [const] Document?
// The document this widget belongs to. If not specified to constructor, will default to
// srcNodeRef.ownerDocument, or if no sourceRef specified, then to the document global
ownerDocument: null,
_setOwnerDocumentAttr: function(val){
// this setter is merely to avoid automatically trying to set this.domNode.ownerDocument
this._set("ownerDocument", val);
},
/*=====
// _started: [readonly] Boolean
// startup() has completed.
_started: false,
=====*/
// attributeMap: [protected] Object
// Deprecated. Instead of attributeMap, widget should have a _setXXXAttr attribute
// for each XXX attribute to be mapped to the DOM.
//
// attributeMap sets up a "binding" between attributes (aka properties)
// of the widget and the widget's DOM.
// Changes to widget attributes listed in attributeMap will be
// reflected into the DOM.
//
// For example, calling set('title', 'hello')
// on a TitlePane will automatically cause the TitlePane's DOM to update
// with the new title.
//
// attributeMap is a hash where the key is an attribute of the widget,
// and the value reflects a binding to a:
//
// - DOM node attribute
// | focus: {node: "focusNode", type: "attribute"}
// Maps this.focus to this.focusNode.focus
//
// - DOM node innerHTML
// | title: { node: "titleNode", type: "innerHTML" }
// Maps this.title to this.titleNode.innerHTML
//
// - DOM node innerText
// | title: { node: "titleNode", type: "innerText" }
// Maps this.title to this.titleNode.innerText
//
// - DOM node CSS class
// | myClass: { node: "domNode", type: "class" }
// Maps this.myClass to this.domNode.className
//
// If the value is an array, then each element in the array matches one of the
// formats of the above list.
//
// There are also some shorthands for backwards compatibility:
//
// - string --> { node: string, type: "attribute" }, for example:
//
// | "focusNode" ---> { node: "focusNode", type: "attribute" }
//
// - "" --> { node: "domNode", type: "attribute" }
attributeMap: {},
// _blankGif: [protected] String
// Path to a blank 1x1 image.
// Used by `` nodes in templates that really get their image via CSS background-image.
_blankGif: config.blankGif || require.toUrl("dojo/resources/blank.gif"),
// textDir: String
// Bi-directional support, the main variable which is responsible for the direction of the text.
// The text direction can be different than the GUI direction by using this parameter in creation
// of a widget.
//
// This property is only effective when `has("dojo-bidi")` is defined to be true.
//
// Allowed values:
//
// 1. "" - default value; text is same direction as widget
// 2. "ltr"
// 3. "rtl"
// 4. "auto" - contextual the direction of a text defined by first strong letter.
textDir: "",
//////////// INITIALIZATION METHODS ///////////////////////////////////////
/*=====
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:
//
// - use srcNodeRef.innerHTML as my contents
// - if this is a behavioral widget then apply behavior to that srcNodeRef
// - otherwise, replace srcNodeRef with my generated DOM tree
},
=====*/
_introspect: function(){
// summary:
// Collect metadata about this widget (only once per class, not once per instance):
//
// - list of attributes with custom setters, storing in this.constructor._setterAttrs
// - generate this.constructor._onMap, mapping names like "mousedown" to functions like onMouseDown
var ctor = this.constructor;
if(!ctor._setterAttrs){
var proto = ctor.prototype,
attrs = ctor._setterAttrs = [], // attributes with custom setters
onMap = (ctor._onMap = {});
// Items in this.attributeMap are like custom setters. For back-compat, remove for 2.0.
for(var name in proto.attributeMap){
attrs.push(name);
}
// Loop over widget properties, collecting properties with custom setters and filling in ctor._onMap.
for(name in proto){
if(/^on/.test(name)){
onMap[name.substring(2).toLowerCase()] = name;
}
if(/^_set[A-Z](.*)Attr$/.test(name)){
name = name.charAt(4).toLowerCase() + name.substr(5, name.length - 9);
if(!proto.attributeMap || !(name in proto.attributeMap)){
attrs.push(name);
}
}
}
// Note: this isn't picking up info on properties like aria-label and role, that don't have custom setters
// but that set() maps to attributes on this.domNode or this.focusNode
}
},
postscript: function(/*Object?*/params, /*DomNode|String*/srcNodeRef){
// summary:
// Kicks off widget instantiation. See create() for details.
// tags:
// private
// Note that we skip calling this.inherited(), i.e. dojo/Stateful::postscript(), because 1.x widgets don't
// expect their custom setters to get called until after buildRendering(). Consider changing for 2.0.
this.create(params, srcNodeRef);
},
create: function(params, srcNodeRef){
// summary:
// Kick off the life-cycle of a widget
// description:
// Create calls a number of widget methods (postMixInProperties, buildRendering, postCreate,
// etc.), some of which of you'll want to override. See http://dojotoolkit.org/reference-guide/dijit/_WidgetBase.html
// for a discussion of the widget creation lifecycle.
//
// Of course, adventurous developers could override create entirely, but this should
// only be done as a last resort.
// 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:
//
// - use srcNodeRef.innerHTML as my contents
// - if this is a behavioral widget then apply behavior to that srcNodeRef
// - otherwise, replace srcNodeRef with my generated DOM tree
// tags:
// private
// First time widget is instantiated, scan prototype to figure out info about custom setters etc.
this._introspect();
// store pointer to original DOM tree
this.srcNodeRef = dom.byId(srcNodeRef);
// No longer used, remove for 2.0.
this._connects = [];
this._supportingWidgets = [];
// this is here for back-compat, remove in 2.0 (but check NodeList-instantiate.html test)
if(this.srcNodeRef && this.srcNodeRef.id && (typeof this.srcNodeRef.id == "string")){
this.id = this.srcNodeRef.id;
}
// mix in our passed parameters
if(params){
this.params = params;
lang.mixin(this, params);
}
this.postMixInProperties();
// Generate an id for the widget if one wasn't specified, or it was specified as id: undefined.
// Do this before buildRendering() because it might expect the id to be there.
if(!this.id){
this.id = registry.getUniqueId(this.declaredClass.replace(/\./g, "_"));
if(this.params){
// if params contains {id: undefined}, prevent _applyAttributes() from processing it
delete this.params.id;
}
}
// The document and node this widget is associated with
this.ownerDocument = this.ownerDocument || (this.srcNodeRef ? this.srcNodeRef.ownerDocument : document);
this.ownerDocumentBody = win.body(this.ownerDocument);
registry.add(this);
this.buildRendering();
var deleteSrcNodeRef;
if(this.domNode){
// Copy attributes listed in attributeMap into the [newly created] DOM for the widget.
// Also calls custom setters for all attributes with custom setters.
this._applyAttributes();
// If srcNodeRef was specified, then swap out original srcNode for this widget's DOM tree.
// For 2.0, move this after postCreate(). postCreate() shouldn't depend on the
// widget being attached to the DOM since it isn't when a widget is created programmatically like
// new MyWidget({}). See #11635.
var source = this.srcNodeRef;
if(source && source.parentNode && this.domNode !== source){
source.parentNode.replaceChild(this.domNode, source);
deleteSrcNodeRef = true;
}
// Note: for 2.0 may want to rename widgetId to dojo._scopeName + "_widgetId",
// assuming that dojo._scopeName even exists in 2.0
this.domNode.setAttribute("widgetId", this.id);
}
this.postCreate();
// If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC.
// I think for back-compatibility it isn't deleting srcNodeRef until after postCreate() has run.
if(deleteSrcNodeRef){
delete this.srcNodeRef;
}
this._created = true;
},
_applyAttributes: function(){
// summary:
// Step during widget creation to copy widget attributes to the
// DOM according to attributeMap and _setXXXAttr objects, and also to call
// custom _setXXXAttr() methods.
//
// Skips over blank/false attribute values, unless they were explicitly specified
// as parameters to the widget, since those are the default anyway,
// and setting tabIndex="" is different than not setting tabIndex at all.
//
// For backwards-compatibility reasons attributeMap overrides _setXXXAttr when
// _setXXXAttr is a hash/string/array, but _setXXXAttr as a functions override attributeMap.
// tags:
// private
// Call this.set() for each property that was either specified as parameter to constructor,
// or is in the list found above. For correlated properties like value and displayedValue, the one
// specified as a parameter should take precedence.
// Particularly important for new DateTextBox({displayedValue: ...}) since DateTextBox's default value is
// NaN and thus is not ignored like a default value of "".
// Step 1: Save the current values of the widget properties that were specified as parameters to the constructor.
// Generally this.foo == this.params.foo, except if postMixInProperties() changed the value of this.foo.
var params = {};
for(var key in this.params || {}){
params[key] = this._get(key);
}
// Step 2: Call set() for each property with a non-falsy value that wasn't passed as a parameter to the constructor
array.forEach(this.constructor._setterAttrs, function(key){
if(!(key in params)){
var val = this._get(key);
if(val){
this.set(key, val);
}
}
}, this);
// Step 3: Call set() for each property that was specified as parameter to constructor.
// Use params hash created above to ignore side effects from step #2 above.
for(key in params){
this.set(key, params[key]);
}
},
postMixInProperties: function(){
// summary:
// Called after the parameters to the widget have been read-in,
// but before the widget template is instantiated. Especially
// useful to set properties that are referenced in the widget
// template.
// tags:
// protected
},
buildRendering: function(){
// summary:
// Construct the UI for this widget, setting this.domNode.
// Most widgets will mixin `dijit._TemplatedMixin`, which implements this method.
// tags:
// protected
if(!this.domNode){
// Create root node if it wasn't created by _TemplatedMixin
this.domNode = this.srcNodeRef || this.ownerDocument.createElement("div");
}
// baseClass is a single class name or occasionally a space-separated list of names.
// Add those classes to the DOMNode. If RTL mode then also add with Rtl suffix.
// TODO: make baseClass custom setter
if(this.baseClass){
var classes = this.baseClass.split(" ");
if(!this.isLeftToRight()){
classes = classes.concat(array.map(classes, function(name){
return name + "Rtl";
}));
}
domClass.add(this.domNode, classes);
}
},
postCreate: function(){
// summary:
// Processing after the DOM fragment is created
// description:
// Called after the DOM fragment has been created, but not necessarily
// added to the document. Do not include any operations which rely on
// node dimensions or placement.
// tags:
// protected
},
startup: function(){
// summary:
// Processing after the DOM fragment is added to the document
// description:
// Called after a widget and its children have been created and added to the page,
// and all related widgets have finished their create() cycle, up through postCreate().
//
// Note that startup() may be called while the widget is still hidden, for example if the widget is
// inside a hidden dijit/Dialog or an unselected tab of a dijit/layout/TabContainer.
// For widgets that need to do layout, it's best to put that layout code inside resize(), and then
// extend dijit/layout/_LayoutWidget so that resize() is called when the widget is visible.
if(this._started){
return;
}
this._started = true;
array.forEach(this.getChildren(), function(obj){
if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){
obj.startup();
obj._started = true;
}
});
},
//////////// DESTROY FUNCTIONS ////////////////////////////////
destroyRecursive: function(/*Boolean?*/ preserveDom){
// summary:
// Destroy this widget and its descendants
// description:
// This is the generic "destructor" function that all widget users
// should call to cleanly discard with a widget. Once a widget is
// destroyed, it is removed from the manager object.
// preserveDom:
// If true, this method will leave the original DOM structure
// alone of descendant Widgets. Note: This will NOT work with
// dijit._TemplatedMixin widgets.
this._beingDestroyed = true;
this.destroyDescendants(preserveDom);
this.destroy(preserveDom);
},
destroy: function(/*Boolean*/ preserveDom){
// summary:
// Destroy this widget, but not its descendants. Descendants means widgets inside of
// this.containerNode. Will also destroy any resources (including widgets) registered via this.own().
//
// This method will also destroy internal widgets such as those created from a template,
// assuming those widgets exist inside of this.domNode but outside of this.containerNode.
//
// For 2.0 it's planned that this method will also destroy descendant widgets, so apps should not
// depend on the current ability to destroy a widget without destroying its descendants. Generally
// they should use destroyRecursive() for widgets with children.
// preserveDom: Boolean
// If true, this method will leave the original DOM structure alone.
// Note: This will not yet work with _TemplatedMixin widgets
this._beingDestroyed = true;
this.uninitialize();
function destroy(w){
if(w.destroyRecursive){
w.destroyRecursive(preserveDom);
}else if(w.destroy){
w.destroy(preserveDom);
}
}
// Back-compat, remove for 2.0
array.forEach(this._connects, lang.hitch(this, "disconnect"));
array.forEach(this._supportingWidgets, destroy);
// Destroy supporting widgets, but not child widgets under this.containerNode (for 2.0, destroy child widgets
// here too). if() statement is to guard against exception if destroy() called multiple times (see #15815).
if(this.domNode){
array.forEach(registry.findWidgets(this.domNode, this.containerNode), destroy);
}
this.destroyRendering(preserveDom);
registry.remove(this.id);
this._destroyed = true;
},
destroyRendering: function(/*Boolean?*/ preserveDom){
// summary:
// Destroys the DOM nodes associated with this widget.
// preserveDom:
// If true, this method will leave the original DOM structure alone
// during tear-down. Note: this will not work with _Templated
// widgets yet.
// tags:
// protected
if(this.bgIframe){
this.bgIframe.destroy(preserveDom);
delete this.bgIframe;
}
if(this.domNode){
if(preserveDom){
domAttr.remove(this.domNode, "widgetId");
}else{
domConstruct.destroy(this.domNode);
}
delete this.domNode;
}
if(this.srcNodeRef){
if(!preserveDom){
domConstruct.destroy(this.srcNodeRef);
}
delete this.srcNodeRef;
}
},
destroyDescendants: function(/*Boolean?*/ preserveDom){
// summary:
// Recursively destroy the children of this widget and their
// descendants.
// preserveDom:
// If true, the preserveDom attribute is passed to all descendant
// widget's .destroy() method. Not for use with _Templated
// widgets.
// get all direct descendants and destroy them recursively
array.forEach(this.getChildren(), function(widget){
if(widget.destroyRecursive){
widget.destroyRecursive(preserveDom);
}
});
},
uninitialize: function(){
// summary:
// Deprecated. Override destroy() instead to implement custom widget tear-down
// behavior.
// tags:
// protected
return false;
},
////////////////// GET/SET, CUSTOM SETTERS, ETC. ///////////////////
_setStyleAttr: function(/*String||Object*/ value){
// summary:
// Sets the style attribute of the widget according to value,
// which is either a hash like {height: "5px", width: "3px"}
// or a plain string
// description:
// Determines which node to set the style on based on style setting
// in attributeMap.
// tags:
// protected
var mapNode = this.domNode;
// Note: technically we should revert any style setting made in a previous call
// to his method, but that's difficult to keep track of.
if(lang.isObject(value)){
domStyle.set(mapNode, value);
}else{
if(mapNode.style.cssText){
mapNode.style.cssText += "; " + value;
}else{
mapNode.style.cssText = value;
}
}
this._set("style", value);
},
_attrToDom: function(/*String*/ attr, /*String*/ value, /*Object?*/ commands){
// summary:
// Reflect a widget attribute (title, tabIndex, duration etc.) to
// the widget DOM, as specified by commands parameter.
// If commands isn't specified then it's looked up from attributeMap.
// Note some attributes like "type"
// cannot be processed this way as they are not mutable.
// attr:
// Name of member variable (ex: "focusNode" maps to this.focusNode) pointing
// to DOMNode inside the widget, or alternately pointing to a subwidget
// tags:
// private
commands = arguments.length >= 3 ? commands : this.attributeMap[attr];
array.forEach(lang.isArray(commands) ? commands : [commands], function(command){
// Get target node and what we are doing to that node
var mapNode = this[command.node || command || "domNode"]; // DOM node
var type = command.type || "attribute"; // class, innerHTML, innerText, or attribute
switch(type){
case "attribute":
if(lang.isFunction(value)){ // functions execute in the context of the widget
value = lang.hitch(this, value);
}
// Get the name of the DOM node attribute; usually it's the same
// as the name of the attribute in the widget (attr), but can be overridden.
// Also maps handler names to lowercase, like onSubmit --> onsubmit
var attrName = command.attribute ? command.attribute :
(/^on[A-Z][a-zA-Z]*$/.test(attr) ? attr.toLowerCase() : attr);
if(mapNode.tagName){
// Normal case, mapping to a DOMNode. Note that modern browsers will have a mapNode.set()
// method, but for consistency we still call domAttr
domAttr.set(mapNode, attrName, value);
}else{
// mapping to a sub-widget
mapNode.set(attrName, value);
}
break;
case "innerText":
// Deprecated, use "textContent" instead.
mapNode.innerHTML = "";
mapNode.appendChild(this.ownerDocument.createTextNode(value));
break;
case "textContent":
mapNode.textContent = value;
break;
case "innerHTML":
mapNode.innerHTML = value;
break;
case "class":
domClass.replace(mapNode, value, this[attr]);
break;
case "toggleClass":
domClass.toggle(mapNode, command.className || attr, value);
break;
}
}, this);
},
get: function(name){
// summary:
// Get a property from a widget.
// name:
// The property to get.
// description:
// Get a named property from a widget. The property may
// potentially be retrieved via a getter method. If no getter is defined, this
// just retrieves the object's property.
//
// For example, if the widget has properties `foo` and `bar`
// and a method named `_getFooAttr()`, calling:
// `myWidget.get("foo")` would be equivalent to calling
// `widget._getFooAttr()` and `myWidget.get("bar")`
// would be equivalent to the expression
// `widget.bar2`
var names = this._getAttrNames(name);
return this[names.g] ? this[names.g]() : this._get(name);
},
set: function(name, value){
// summary:
// Set a property on a widget
// name:
// The property to set.
// value:
// The value to set in the property.
// description:
// Sets named properties on a widget which may potentially be handled by a
// setter in the widget.
//
// For example, if the widget has properties `foo` and `bar`
// and a method named `_setFooAttr()`, calling
// `myWidget.set("foo", "Howdy!")` would be equivalent to calling
// `widget._setFooAttr("Howdy!")` and `myWidget.set("bar", 3)`
// would be equivalent to the statement `widget.bar = 3;`
//
// set() may also be called with a hash of name/value pairs, ex:
//
// | myWidget.set({
// | foo: "Howdy",
// | bar: 3
// | });
//
// This is equivalent to calling `set(foo, "Howdy")` and `set(bar, 3)`
if(typeof name === "object"){
for(var x in name){
this.set(x, name[x]);
}
return this;
}
var names = this._getAttrNames(name),
setter = this[names.s];
if(lang.isFunction(setter)){
// use the explicit setter
var result = setter.apply(this, Array.prototype.slice.call(arguments, 1));
}else{
// Mapping from widget attribute to DOMNode/subwidget attribute/value/etc.
// Map according to:
// 1. attributeMap setting, if one exists (TODO: attributeMap deprecated, remove in 2.0)
// 2. _setFooAttr: {...} type attribute in the widget (if one exists)
// 3. apply to focusNode or domNode if standard attribute name, excluding funcs like onClick.
// Checks if an attribute is a "standard attribute" by whether the DOMNode JS object has a similar
// attribute name (ex: accept-charset attribute matches jsObject.acceptCharset).
// Note also that Tree.focusNode() is a function not a DOMNode, so test for that.
var defaultNode = this.focusNode && !lang.isFunction(this.focusNode) ? "focusNode" : "domNode",
tag = this[defaultNode] && this[defaultNode].tagName,
attrsForTag = tag && (tagAttrs[tag] || (tagAttrs[tag] = getAttrs(this[defaultNode]))),
map = name in this.attributeMap ? this.attributeMap[name] :
names.s in this ? this[names.s] :
((attrsForTag && names.l in attrsForTag && typeof value != "function") ||
/^aria-|^data-|^role$/.test(name)) ? defaultNode : null;
if(map != null){
this._attrToDom(name, value, map);
}
this._set(name, value);
}
return result || this;
},
_attrPairNames: {}, // shared between all widgets
_getAttrNames: function(name){
// summary:
// Helper function for get() and set().
// Caches attribute name values so we don't do the string ops every time.
// tags:
// private
var apn = this._attrPairNames;
if(apn[name]){
return apn[name];
}
var uc = name.replace(/^[a-z]|-[a-zA-Z]/g, function(c){
return c.charAt(c.length - 1).toUpperCase();
});
return (apn[name] = {
n: name + "Node",
s: "_set" + uc + "Attr", // converts dashes to camel case, ex: accept-charset --> _setAcceptCharsetAttr
g: "_get" + uc + "Attr",
l: uc.toLowerCase() // lowercase name w/out dashes, ex: acceptcharset
});
},
_set: function(/*String*/ name, /*anything*/ value){
// summary:
// Helper function to set new value for specified property, and call handlers
// registered with watch() if the value has changed.
var oldValue = this[name];
this[name] = value;
if(this._created && !isEqual(oldValue, value)){
if(this._watchCallbacks){
this._watchCallbacks(name, oldValue, value);
}
this.emit("attrmodified-" + name, {
detail: {
prevValue: oldValue,
newValue: value
}
});
}
},
_get: function(/*String*/ name){
// summary:
// Helper function to get value for specified property stored by this._set(),
// i.e. for properties with custom setters. Used mainly by custom getters.
//
// For example, CheckBox._getValueAttr() calls this._get("value").
// future: return name in this.props ? this.props[name] : this[name];
return this[name];
},
emit: function(/*String*/ type, /*Object?*/ eventObj, /*Array?*/ callbackArgs){
// summary:
// Used by widgets to signal that a synthetic event occurred, ex:
// | myWidget.emit("attrmodified-selectedChildWidget", {}).
//
// Emits an event on this.domNode named type.toLowerCase(), based on eventObj.
// Also calls onType() method, if present, and returns value from that method.
// By default passes eventObj to callback, but will pass callbackArgs instead, if specified.
// Modifies eventObj by adding missing parameters (bubbles, cancelable, widget).
// tags:
// protected
// Specify fallback values for bubbles, cancelable in case they are not set in eventObj.
// Also set pointer to widget, although since we can't add a pointer to the widget for native events
// (see #14729), maybe we shouldn't do it here?
eventObj = eventObj || {};
if(eventObj.bubbles === undefined){
eventObj.bubbles = true;
}
if(eventObj.cancelable === undefined){
eventObj.cancelable = true;
}
if(!eventObj.detail){
eventObj.detail = {};
}
eventObj.detail.widget = this;
var ret, callback = this["on" + type];
if(callback){
ret = callback.apply(this, callbackArgs ? callbackArgs : [eventObj]);
}
// Emit event, but avoid spurious emit()'s as parent sets properties on child during startup/destroy
if(this._started && !this._beingDestroyed){
on.emit(this.domNode, type.toLowerCase(), eventObj);
}
return ret;
},
on: function(/*String|Function*/ type, /*Function*/ func){
// summary:
// Call specified function when event occurs, ex: myWidget.on("click", function(){ ... }).
// type:
// Name of event (ex: "click") or extension event like touch.press.
// description:
// Call specified function when event `type` occurs, ex: `myWidget.on("click", function(){ ... })`.
// Note that the function is not run in any particular scope, so if (for example) you want it to run in the
// widget's scope you must do `myWidget.on("click", lang.hitch(myWidget, func))`.
// For backwards compatibility, if there's an onType() method in the widget then connect to that.
// Remove in 2.0.
var widgetMethod = this._onMap(type);
if(widgetMethod){
return aspect.after(this, widgetMethod, func, true);
}
// Otherwise, just listen for the event on this.domNode.
return this.own(on(this.domNode, type, func))[0];
},
_onMap: function(/*String|Function*/ type){
// summary:
// Maps on() type parameter (ex: "mousemove") to method name (ex: "onMouseMove").
// If type is a synthetic event like touch.press then returns undefined.
var ctor = this.constructor, map = ctor._onMap;
if(!map){
map = (ctor._onMap = {});
for(var attr in ctor.prototype){
if(/^on/.test(attr)){
map[attr.replace(/^on/, "").toLowerCase()] = attr;
}
}
}
return map[typeof type == "string" && type.toLowerCase()]; // String
},
toString: function(){
// summary:
// Returns a string that represents the widget.
// description:
// When a widget is cast to a string, this method will be used to generate the
// output. Currently, it does not implement any sort of reversible
// serialization.
return '[Widget ' + this.declaredClass + ', ' + (this.id || 'NO ID') + ']'; // String
},
getChildren: function(){
// summary:
// Returns all direct children of this widget, i.e. all widgets underneath this.containerNode whose parent
// is this widget. Note that it does not return all descendants, but rather just direct children.
// Analogous to [Node.childNodes](https://developer.mozilla.org/en-US/docs/DOM/Node.childNodes),
// except containing widgets rather than DOMNodes.
//
// The result intentionally excludes internally created widgets (a.k.a. supporting widgets)
// outside of this.containerNode.
//
// Note that the array returned is a simple array. Application code should not assume
// existence of methods like forEach().
return this.containerNode ? registry.findWidgets(this.containerNode) : []; // dijit/_WidgetBase[]
},
getParent: function(){
// summary:
// Returns the parent widget of this widget.
return registry.getEnclosingWidget(this.domNode.parentNode);
},
connect: function(/*Object|null*/ obj, /*String|Function*/ event, /*String|Function*/ method){
// summary:
// Deprecated, will be removed in 2.0, use this.own(on(...)) or this.own(aspect.after(...)) instead.
//
// Connects specified obj/event to specified method of this object
// and registers for disconnect() on widget destroy.
//
// Provide widget-specific analog to dojo.connect, except with the
// implicit use of this widget as the target object.
// Events connected with `this.connect` are disconnected upon
// destruction.
// returns:
// A handle that can be passed to `disconnect` in order to disconnect before
// the widget is destroyed.
// example:
// | var btn = new Button();
// | // when foo.bar() is called, call the listener we're going to
// | // provide in the scope of btn
// | btn.connect(foo, "bar", function(){
// | console.debug(this.toString());
// | });
// tags:
// protected
return this.own(connect.connect(obj, event, this, method))[0]; // handle
},
disconnect: function(handle){
// summary:
// Deprecated, will be removed in 2.0, use handle.remove() instead.
//
// Disconnects handle created by `connect`.
// tags:
// protected
handle.remove();
},
subscribe: function(t, method){
// summary:
// Deprecated, will be removed in 2.0, use this.own(topic.subscribe()) instead.
//
// Subscribes to the specified topic and calls the specified method
// of this object and registers for unsubscribe() on widget destroy.
//
// Provide widget-specific analog to dojo.subscribe, except with the
// implicit use of this widget as the target object.
// t: String
// The topic
// method: Function
// The callback
// example:
// | var btn = new Button();
// | // when /my/topic is published, this button changes its label to
// | // be the parameter of the topic.
// | btn.subscribe("/my/topic", function(v){
// | this.set("label", v);
// | });
// tags:
// protected
return this.own(topic.subscribe(t, lang.hitch(this, method)))[0]; // handle
},
unsubscribe: function(/*Object*/ handle){
// summary:
// Deprecated, will be removed in 2.0, use handle.remove() instead.
//
// Unsubscribes handle created by this.subscribe.
// Also removes handle from this widget's list of subscriptions
// tags:
// protected
handle.remove();
},
isLeftToRight: function(){
// summary:
// Return this widget's explicit or implicit orientation (true for LTR, false for RTL)
// tags:
// protected
return this.dir ? (this.dir.toLowerCase() == "ltr") : domGeometry.isBodyLtr(this.ownerDocument); //Boolean
},
isFocusable: function(){
// summary:
// Return true if this widget can currently be focused
// and false if not
return this.focus && (domStyle.get(this.domNode, "display") != "none");
},
placeAt: function(/*String|DomNode|DocumentFragment|dijit/_WidgetBase*/ reference, /*String|Int?*/ position){
// summary:
// Place this widget somewhere in the DOM based
// on standard domConstruct.place() conventions.
// description:
// A convenience function provided in all _Widgets, providing a simple
// shorthand mechanism to put an existing (or newly created) Widget
// somewhere in the dom, and allow chaining.
// reference:
// Widget, DOMNode, DocumentFragment, or id of widget or DOMNode
// position:
// If reference is a widget (or id of widget), and that widget has an ".addChild" method,
// it will be called passing this widget instance into that method, supplying the optional
// position index passed. In this case position (if specified) should be an integer.
//
// If reference is a DOMNode (or id matching a DOMNode but not a widget),
// the position argument can be a numeric index or a string
// "first", "last", "before", or "after", same as dojo/dom-construct::place().
// returns: dijit/_WidgetBase
// Provides a useful return of the newly created dijit._Widget instance so you
// can "chain" this function by instantiating, placing, then saving the return value
// to a variable.
// example:
// | // create a Button with no srcNodeRef, and place it in the body:
// | var button = new Button({ label:"click" }).placeAt(win.body());
// | // now, 'button' is still the widget reference to the newly created button
// | button.on("click", function(e){ console.log('click'); }));
// example:
// | // create a button out of a node with id="src" and append it to id="wrapper":
// | var button = new Button({},"src").placeAt("wrapper");
// example:
// | // place a new button as the first element of some div
// | var button = new Button({ label:"click" }).placeAt("wrapper","first");
// example:
// | // create a contentpane and add it to a TabContainer
// | var tc = dijit.byId("myTabs");
// | new ContentPane({ href:"foo.html", title:"Wow!" }).placeAt(tc)
var refWidget = !reference.tagName && registry.byId(reference);
if(refWidget && refWidget.addChild && (!position || typeof position === "number")){
// Adding this to refWidget and can use refWidget.addChild() to handle everything.
refWidget.addChild(this, position);
}else{
// "reference" is a plain DOMNode, or we can't use refWidget.addChild(). Use domConstruct.place() and
// target refWidget.containerNode for nested placement (position==number, "first", "last", "only"), and
// refWidget.domNode otherwise ("after"/"before"/"replace"). (But not supported officially, see #14946.)
var ref = refWidget && ("domNode" in refWidget) ?
(refWidget.containerNode && !/after|before|replace/.test(position || "") ?
refWidget.containerNode : refWidget.domNode) : dom.byId(reference, this.ownerDocument);
domConstruct.place(this.domNode, ref, position);
// Start this iff it has a parent widget that's already started.
// TODO: for 2.0 maybe it should also start the widget when this.getParent() returns null??
if(!this._started && (this.getParent() || {})._started){
this.startup();
}
}
return this;
},
defer: function(fcn, delay){
// summary:
// Wrapper to setTimeout to avoid deferred functions executing
// after the originating widget has been destroyed.
// Returns an object handle with a remove method (that returns null) (replaces clearTimeout).
// fcn: Function
// Function reference.
// delay: Number?
// Delay, defaults to 0.
// tags:
// protected
var timer = setTimeout(lang.hitch(this,
function(){
if(!timer){
return;
}
timer = null;
if(!this._destroyed){
lang.hitch(this, fcn)();
}
}),
delay || 0
);
return {
remove: function(){
if(timer){
clearTimeout(timer);
timer = null;
}
return null; // so this works well: handle = handle.remove();
}
};
}
});
if(has("dojo-bidi")){
_WidgetBase.extend(_BidiMixin);
}
return _WidgetBase;
});
},
'dojo/Stateful':function(){
define(["./_base/declare", "./_base/lang", "./_base/array", "./when"], function(declare, lang, array, when){
// module:
// dojo/Stateful
return declare("dojo.Stateful", null, {
// summary:
// Base class for objects that provide named properties with optional getter/setter
// control and the ability to watch for property changes
//
// The class also provides the functionality to auto-magically manage getters
// and setters for object attributes/properties.
//
// Getters and Setters should follow the format of _xxxGetter or _xxxSetter where
// the xxx is a name of the attribute to handle. So an attribute of "foo"
// would have a custom getter of _fooGetter and a custom setter of _fooSetter.
//
// example:
// | require(["dojo/Stateful", function(Stateful) {
// | var obj = new Stateful();
// | obj.watch("foo", function(){
// | console.log("foo changed to " + this.get("foo"));
// | });
// | obj.set("foo","bar");
// | });
// _attrPairNames: Hash
// Used across all instances a hash to cache attribute names and their getter
// and setter names.
_attrPairNames: {},
_getAttrNames: function(name){
// summary:
// Helper function for get() and set().
// Caches attribute name values so we don't do the string ops every time.
// tags:
// private
var apn = this._attrPairNames;
if(apn[name]){ return apn[name]; }
return (apn[name] = {
s: "_" + name + "Setter",
g: "_" + name + "Getter"
});
},
postscript: function(/*Object?*/ params){
// Automatic setting of params during construction
if (params){ this.set(params); }
},
_get: function(name, names){
// summary:
// Private function that does a get based off a hash of names
// names:
// Hash of names of custom attributes
return typeof this[names.g] === "function" ? this[names.g]() : this[name];
},
get: function(/*String*/name){
// summary:
// Get a property on a Stateful instance.
// name:
// The property to get.
// returns:
// The property value on this Stateful instance.
// description:
// Get a named property on a Stateful object. The property may
// potentially be retrieved via a getter method in subclasses. In the base class
// this just retrieves the object's property.
// example:
// | require(["dojo/Stateful", function(Stateful) {
// | var stateful = new Stateful({foo: 3});
// | stateful.get("foo") // returns 3
// | stateful.foo // returns 3
// | });
return this._get(name, this._getAttrNames(name)); //Any
},
set: function(/*String*/name, /*Object*/value){
// summary:
// Set a property on a Stateful instance
// name:
// The property to set.
// value:
// The value to set in the property.
// returns:
// The function returns this dojo.Stateful instance.
// description:
// Sets named properties on a stateful object and notifies any watchers of
// the property. A programmatic setter may be defined in subclasses.
// example:
// | require(["dojo/Stateful", function(Stateful) {
// | var stateful = new Stateful();
// | stateful.watch(function(name, oldValue, value){
// | // this will be called on the set below
// | }
// | stateful.set(foo, 5);
// set() may also be called with a hash of name/value pairs, ex:
// | stateful.set({
// | foo: "Howdy",
// | bar: 3
// | });
// | });
// This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
// If an object is used, iterate through object
if(typeof name === "object"){
for(var x in name){
if(name.hasOwnProperty(x) && x !="_watchCallbacks"){
this.set(x, name[x]);
}
}
return this;
}
var names = this._getAttrNames(name),
oldValue = this._get(name, names),
setter = this[names.s],
result;
if(typeof setter === "function"){
// use the explicit setter
result = setter.apply(this, Array.prototype.slice.call(arguments, 1));
}else{
// no setter so set attribute directly
this[name] = value;
}
if(this._watchCallbacks){
var self = this;
// If setter returned a promise, wait for it to complete, otherwise call watches immediately
when(result, function(){
self._watchCallbacks(name, oldValue, value);
});
}
return this; // dojo/Stateful
},
_changeAttrValue: function(name, value){
// summary:
// Internal helper for directly changing an attribute value.
//
// name: String
// The property to set.
// value: Mixed
// The value to set in the property.
//
// description:
// Directly change the value of an attribute on an object, bypassing any
// accessor setter. Also handles the calling of watch and emitting events.
// It is designed to be used by descendant class when there are two values
// of attributes that are linked, but calling .set() is not appropriate.
var oldValue = this.get(name);
this[name] = value;
if(this._watchCallbacks){
this._watchCallbacks(name, oldValue, value);
}
return this; // dojo/Stateful
},
watch: function(/*String?*/name, /*Function*/callback){
// summary:
// Watches a property for changes
// name:
// Indicates the property to watch. This is optional (the callback may be the
// only parameter), and if omitted, all the properties will be watched
// returns:
// An object handle for the watch. The unwatch method of this object
// can be used to discontinue watching this property:
// | var watchHandle = obj.watch("foo", callback);
// | watchHandle.unwatch(); // callback won't be called now
// callback:
// The function to execute when the property changes. This will be called after
// the property has been changed. The callback will be called with the |this|
// set to the instance, the first argument as the name of the property, the
// second argument as the old value and the third argument as the new value.
var callbacks = this._watchCallbacks;
if(!callbacks){
var self = this;
callbacks = this._watchCallbacks = function(name, oldValue, value, ignoreCatchall){
var notify = function(propertyCallbacks){
if(propertyCallbacks){
propertyCallbacks = propertyCallbacks.slice();
for(var i = 0, l = propertyCallbacks.length; i < l; i++){
propertyCallbacks[i].call(self, name, oldValue, value);
}
}
};
notify(callbacks['_' + name]);
if(!ignoreCatchall){
notify(callbacks["*"]); // the catch-all
}
}; // we use a function instead of an object so it will be ignored by JSON conversion
}
if(!callback && typeof name === "function"){
callback = name;
name = "*";
}else{
// prepend with dash to prevent name conflicts with function (like "name" property)
name = '_' + name;
}
var propertyCallbacks = callbacks[name];
if(typeof propertyCallbacks !== "object"){
propertyCallbacks = callbacks[name] = [];
}
propertyCallbacks.push(callback);
// TODO: Remove unwatch in 2.0
var handle = {};
handle.unwatch = handle.remove = function(){
var index = array.indexOf(propertyCallbacks, callback);
if(index > -1){
propertyCallbacks.splice(index, 1);
}
};
return handle; //Object
}
});
});
},
'dijit/Destroyable':function(){
define([
"dojo/_base/array", // array.forEach array.map
"dojo/aspect",
"dojo/_base/declare"
], function(array, aspect, declare){
// module:
// dijit/Destroyable
return declare("dijit.Destroyable", null, {
// summary:
// Mixin to track handles and release them when instance is destroyed.
// description:
// Call this.own(...) on list of handles (returned from dojo/aspect, dojo/on,
// dojo/Stateful::watch, or any class (including widgets) with a destroyRecursive() or destroy() method.
// Then call destroy() later to destroy this instance and release the resources.
destroy: function(/*Boolean*/ preserveDom){
// summary:
// Destroy this class, releasing any resources registered via own().
this._destroyed = true;
},
own: function(){
// summary:
// Track specified handles and remove/destroy them when this instance is destroyed, unless they were
// already removed/destroyed manually.
// tags:
// protected
// returns:
// The array of specified handles, so you can do for example:
// | var handle = this.own(on(...))[0];
var cleanupMethods = [
"destroyRecursive",
"destroy",
"remove"
];
array.forEach(arguments, function(handle){
// When this.destroy() is called, destroy handle. Since I'm using aspect.before(),
// the handle will be destroyed before a subclass's destroy() method starts running, before it calls
// this.inherited() or even if it doesn't call this.inherited() at all. If that's an issue, make an
// onDestroy() method and connect to that instead.
var destroyMethodName;
var odh = aspect.before(this, "destroy", function (preserveDom){
handle[destroyMethodName](preserveDom);
});
// Callback for when handle is manually destroyed.
var hdhs = [];
function onManualDestroy(){
odh.remove();
array.forEach(hdhs, function(hdh){
hdh.remove();
});
}
// Setup listeners for manual destroy of handle.
// Also computes destroyMethodName, used in listener above.
if(handle.then){
// Special path for Promises. Detect when Promise is resolved, rejected, or
// canceled (nb: cancelling a Promise causes it to be rejected).
destroyMethodName = "cancel";
handle.then(onManualDestroy, onManualDestroy);
}else{
// Path for other handles. Just use AOP to detect when handle is manually destroyed.
array.forEach(cleanupMethods, function(cleanupMethod){
if(typeof handle[cleanupMethod] === "function"){
if(!destroyMethodName){
// Use first matching method name in above listener (prefer destroyRecursive() to destroy())
destroyMethodName = cleanupMethod;
}
hdhs.push(aspect.after(handle, cleanupMethod, onManualDestroy, true));
}
});
}
}, this);
return arguments; // handle
}
});
});
},
'dijit/_OnDijitClickMixin':function(){
define([
"dojo/on",
"dojo/_base/array", // array.forEach
"dojo/keys", // keys.ENTER keys.SPACE
"dojo/_base/declare", // declare
"dojo/has", // has("dom-addeventlistener")
"./a11yclick"
], function(on, array, keys, declare, has, a11yclick){
// module:
// dijit/_OnDijitClickMixin
var ret = declare("dijit._OnDijitClickMixin", null, {
// summary:
// Deprecated. New code should access the dijit/a11yclick event directly, ex:
// | this.own(on(node, a11yclick, function(){ ... }));
//
// Mixing in this class will make _WidgetBase.connect(node, "ondijitclick", ...) work.
// It also used to be necessary to make templates with ondijitclick work, but now you can just require
// dijit/a11yclick.
connect: function(obj, event, method){
// override _WidgetBase.connect() to make this.connect(node, "ondijitclick", ...) work
return this.inherited(arguments, [obj, event == "ondijitclick" ? a11yclick : event, method]);
}
});
ret.a11yclick = a11yclick; // back compat
return ret;
});
},
'dijit/a11yclick':function(){
define([
"dojo/keys", // keys.ENTER keys.SPACE
"dojo/mouse",
"dojo/on",
"dojo/touch" // touch support for click is now there
], function(keys, mouse, on, touch){
// module:
// dijit/a11yclick
/*=====
return {
// summary:
// Custom press, release, and click synthetic events
// which trigger on a left mouse click, touch, or space/enter keyup.
click: function(node, listener){
// summary:
// Logical click operation for mouse, touch, or keyboard (space/enter key)
},
press: function(node, listener){
// summary:
// Mousedown (left button), touchstart, or keydown (space or enter) corresponding to logical click operation.
},
release: function(node, listener){
// summary:
// Mouseup (left button), touchend, or keyup (space or enter) corresponding to logical click operation.
},
move: function(node, listener){
// summary:
// Mouse cursor or a finger is dragged over the given node.
}
};
=====*/
function clickKey(/*Event*/ e){
// Test if this keyboard event should be tracked as the start (if keydown) or end (if keyup) of a click event.
// Only track for nodes marked to be tracked, and not for buttons or inputs,
// since buttons handle keyboard click natively, and text inputs should not
// prevent typing spaces or newlines.
if((e.keyCode === keys.ENTER || e.keyCode === keys.SPACE) && !/input|button|textarea/i.test(e.target.nodeName)){
// Test if a node or its ancestor has been marked with the dojoClick property to indicate special processing
for(var node = e.target; node; node = node.parentNode){
if(node.dojoClick){ return true; }
}
}
}
var lastKeyDownNode;
on(document, "keydown", function(e){
//console.log("a11yclick: onkeydown, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
if(clickKey(e)){
// needed on IE for when focus changes between keydown and keyup - otherwise dropdown menus do not work
lastKeyDownNode = e.target;
// Prevent viewport scrolling on space key in IE<9.
// (Reproducible on test_Button.html on any of the first dijit/form/Button examples)
e.preventDefault();
}else{
lastKeyDownNode = null;
}
});
on(document, "keyup", function(e){
//console.log("a11yclick: onkeyup, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
if(clickKey(e) && e.target == lastKeyDownNode){ // === breaks greasemonkey
//need reset here or have problems in FF when focus returns to trigger element after closing popup/alert
lastKeyDownNode = null;
on.emit(e.target, "click", {
cancelable: true,
bubbles: true,
ctrlKey: e.ctrlKey,
shiftKey: e.shiftKey,
metaKey: e.metaKey,
altKey: e.altKey,
_origType: e.type
});
}
});
// I want to return a hash of the synthetic events, but for backwards compatibility the main return value
// needs to be the click event. Change for 2.0.
var click = function(node, listener){
// Set flag on node so that keydown/keyup above emits click event.
// Also enables fast click processing from dojo/touch.
node.dojoClick = true;
return on(node, "click", listener);
};
click.click = click; // forward compatibility with 2.0
click.press = function(node, listener){
var touchListener = on(node, touch.press, function(evt){
if(evt.type == "mousedown" && !mouse.isLeft(evt)){
// Ignore right click
return;
}
listener(evt);
}), keyListener = on(node, "keydown", function(evt){
if(evt.keyCode === keys.ENTER || evt.keyCode === keys.SPACE){
listener(evt);
}
});
return {
remove: function(){
touchListener.remove();
keyListener.remove();
}
};
};
click.release = function(node, listener){
var touchListener = on(node, touch.release, function(evt){
if(evt.type == "mouseup" && !mouse.isLeft(evt)){
// Ignore right click
return;
}
listener(evt);
}), keyListener = on(node, "keyup", function(evt){
if(evt.keyCode === keys.ENTER || evt.keyCode === keys.SPACE){
listener(evt);
}
});
return {
remove: function(){
touchListener.remove();
keyListener.remove();
}
};
};
click.move = touch.move; // just for convenience
return click;
});
},
'dojo/touch':function(){
define(["./_base/kernel", "./aspect", "./dom", "./dom-class", "./_base/lang", "./on", "./has", "./mouse", "./domReady", "./_base/window"],
function(dojo, aspect, dom, domClass, lang, on, has, mouse, domReady, win){
// module:
// dojo/touch
var ios4 = has("ios") < 5;
// Detect if platform supports Pointer Events, and if so, the names of the events (pointerdown vs. MSPointerDown).
var hasPointer = has("pointer-events") || has("MSPointer"),
pointer = (function () {
var pointer = {};
for (var type in { down: 1, move: 1, up: 1, cancel: 1, over: 1, out: 1 }) {
pointer[type] = has("MSPointer") ?
"MSPointer" + type.charAt(0).toUpperCase() + type.slice(1) :
"pointer" + type;
}
return pointer;
})();
// Detect if platform supports the webkit touchstart/touchend/... events
var hasTouch = has("touch-events");
// Click generation variables
var clicksInited, clickTracker, useTarget = false, clickTarget, clickX, clickY, clickDx, clickDy, clickTime;
// Time of most recent touchstart, touchmove, or touchend event
var lastTouch;
function dualEvent(mouseType, touchType, pointerType){
// Returns synthetic event that listens for both the specified mouse event and specified touch event.
// But ignore fake mouse events that were generated due to the user touching the screen.
if(hasPointer && pointerType){
// IE10+: MSPointer* events are designed to handle both mouse and touch in a uniform way,
// so just use that regardless of hasTouch.
return function(node, listener){
return on(node, pointerType, listener);
};
}else if(hasTouch){
return function(node, listener){
var handle1 = on(node, touchType, function(evt){
listener.call(this, evt);
// On slow mobile browsers (see https://bugs.dojotoolkit.org/ticket/17634),
// a handler for a touch event may take >1s to run. That time shouldn't
// be included in the calculation for lastTouch.
lastTouch = (new Date()).getTime();
}),
handle2 = on(node, mouseType, function(evt){
if(!lastTouch || (new Date()).getTime() > lastTouch + 1000){
listener.call(this, evt);
}
});
return {
remove: function(){
handle1.remove();
handle2.remove();
}
};
};
}else{
// Avoid creating listeners for touch events on performance sensitive older browsers like IE6
return function(node, listener){
return on(node, mouseType, listener);
};
}
}
function marked(/*DOMNode*/ node){
// Search for node ancestor has been marked with the dojoClick property to indicate special processing.
// Returns marked ancestor.
do{
if(node.dojoClick !== undefined){ return node; }
}while(node = node.parentNode);
}
function doClicks(e, moveType, endType){
// summary:
// Setup touch listeners to generate synthetic clicks immediately (rather than waiting for the browser
// to generate clicks after the double-tap delay) and consistently (regardless of whether event.preventDefault()
// was called in an event listener. Synthetic clicks are generated only if a node or one of its ancestors has
// its dojoClick property set to truthy. If a node receives synthetic clicks because one of its ancestors has its
// dojoClick property set to truthy, you can disable synthetic clicks on this node by setting its own dojoClick property
// to falsy.
if(mouse.isRight(e)){
return; // avoid spurious dojoclick event on IE10+; right click is just for context menu
}
var markedNode = marked(e.target);
clickTracker = !e.target.disabled && markedNode && markedNode.dojoClick; // click threshold = true, number, x/y object, or "useTarget"
if(clickTracker){
useTarget = (clickTracker == "useTarget");
clickTarget = (useTarget?markedNode:e.target);
if(useTarget){
// We expect a click, so prevent any other
// default action on "touchpress"
e.preventDefault();
}
clickX = e.changedTouches ? e.changedTouches[0].pageX - win.global.pageXOffset : e.clientX;
clickY = e.changedTouches ? e.changedTouches[0].pageY - win.global.pageYOffset : e.clientY;
clickDx = (typeof clickTracker == "object" ? clickTracker.x : (typeof clickTracker == "number" ? clickTracker : 0)) || 4;
clickDy = (typeof clickTracker == "object" ? clickTracker.y : (typeof clickTracker == "number" ? clickTracker : 0)) || 4;
// add move/end handlers only the first time a node with dojoClick is seen,
// so we don't add too much overhead when dojoClick is never set.
if(!clicksInited){
clicksInited = true;
var updateClickTracker = function updateClickTracker(e){
if(useTarget){
clickTracker = dom.isDescendant(
win.doc.elementFromPoint(
(e.changedTouches ? e.changedTouches[0].pageX - win.global.pageXOffset : e.clientX),
(e.changedTouches ? e.changedTouches[0].pageY - win.global.pageYOffset : e.clientY)),
clickTarget);
}else{
clickTracker = clickTracker &&
(e.changedTouches ? e.changedTouches[0].target : e.target) == clickTarget &&
Math.abs((e.changedTouches ? e.changedTouches[0].pageX - win.global.pageXOffset : e.clientX) - clickX) <= clickDx &&
Math.abs((e.changedTouches ? e.changedTouches[0].pageY - win.global.pageYOffset : e.clientY) - clickY) <= clickDy;
}
};
win.doc.addEventListener(moveType, function(e){
if(mouse.isRight(e)){
return; // avoid spurious dojoclick event on IE10+; right click is just for context menu
}
updateClickTracker(e);
if(useTarget){
// prevent native scroll event and ensure touchend is
// fire after touch moves between press and release.
e.preventDefault();
}
}, true);
win.doc.addEventListener(endType, function(e){
if(mouse.isRight(e)){
return; // avoid spurious dojoclick event on IE10+; right click is just for context menu
}
updateClickTracker(e);
if(clickTracker){
clickTime = (new Date()).getTime();
var target = (useTarget?clickTarget:e.target);
if(target.tagName === "LABEL"){
// when clicking on a label, forward click to its associated input if any
target = dom.byId(target.getAttribute("for")) || target;
}
//some attributes can be on the Touch object, not on the Event:
//http://www.w3.org/TR/touch-events/#touch-interface
var src = (e.changedTouches) ? e.changedTouches[0] : e;
var createMouseEvent = function createMouseEvent(type){
//create the synthetic event.
//http://www.w3.org/TR/DOM-Level-3-Events/#widl-MouseEvent-initMouseEvent
var evt = document.createEvent("MouseEvents");
evt._dojo_click = true;
evt.initMouseEvent(type,
true, //bubbles
true, //cancelable
e.view,
e.detail,
src.screenX,
src.screenY,
src.clientX,
src.clientY,
e.ctrlKey,
e.altKey,
e.shiftKey,
e.metaKey,
0, //button
null //related target
);
return evt;
};
var mouseDownEvt = createMouseEvent("mousedown");
var mouseUpEvt = createMouseEvent("mouseup");
var clickEvt = createMouseEvent("click");
setTimeout(function(){
on.emit(target, "mousedown", mouseDownEvt);
on.emit(target, "mouseup", mouseUpEvt);
on.emit(target, "click", clickEvt);
// refresh clickTime in case app-defined click handler took a long time to run
clickTime = (new Date()).getTime();
}, 0);
}
}, true);
var stopNativeEvents = function stopNativeEvents(type){
win.doc.addEventListener(type, function(e){
// Stop native events when we emitted our own click event. Note that the native click may occur
// on a different node than the synthetic click event was generated on. For example,
// click on a menu item, causing the menu to disappear, and then (~300ms later) the browser
// sends a click event to the node that was *underneath* the menu. So stop all native events
// sent shortly after ours, similar to what is done in dualEvent.
// The INPUT.dijitOffScreen test is for offscreen inputs used in dijit/form/Button, on which
// we call click() explicitly, we don't want to stop this event.
var target = e.target;
if(clickTracker && !e._dojo_click &&
(new Date()).getTime() <= clickTime + 1000 &&
!(target.tagName == "INPUT" && domClass.contains(target, "dijitOffScreen"))){
e.stopPropagation();
e.stopImmediatePropagation && e.stopImmediatePropagation();
if(type == "click" &&
(target.tagName != "INPUT" ||
(target.type == "radio" &&
// #18352 Do not preventDefault for radios that are not dijit or
// dojox/mobile widgets.
// (The CSS class dijitCheckBoxInput holds for both checkboxes and radio buttons.)
(domClass.contains(target, "dijitCheckBoxInput") ||
domClass.contains(target, "mblRadioButton"))) ||
(target.type == "checkbox" &&
// #18352 Do not preventDefault for checkboxes that are not dijit or
// dojox/mobile widgets.
(domClass.contains(target, "dijitCheckBoxInput") ||
domClass.contains(target, "mblCheckBox")))) &&
target.tagName != "TEXTAREA" && target.tagName != "AUDIO" && target.tagName != "VIDEO"){
// preventDefault() breaks textual s on android, keyboard doesn't popup,
// but it is still needed for checkboxes and radio buttons, otherwise in some cases
// the checked state becomes inconsistent with the widget's state
e.preventDefault();
}
}
}, true);
};
stopNativeEvents("click");
// We also stop mousedown/up since these would be sent well after with our "fast" click (300ms),
// which can confuse some dijit widgets.
stopNativeEvents("mousedown");
stopNativeEvents("mouseup");
}
}
}
var hoveredNode;
if(has("touch")){
if(hasPointer){
// MSPointer (IE10+) already has support for over and out, so we just need to init click support
domReady(function(){
win.doc.addEventListener(pointer.down, function(evt){
doClicks(evt, pointer.move, pointer.up);
}, true);
});
}else{
domReady(function(){
// Keep track of currently hovered node
hoveredNode = win.body(); // currently hovered node
win.doc.addEventListener("touchstart", function(evt){
lastTouch = (new Date()).getTime();
// Precede touchstart event with touch.over event. DnD depends on this.
// Use addEventListener(cb, true) to run cb before any touchstart handlers on node run,
// and to ensure this code runs even if the listener on the node does event.stop().
var oldNode = hoveredNode;
hoveredNode = evt.target;
on.emit(oldNode, "dojotouchout", {
relatedTarget: hoveredNode,
bubbles: true
});
on.emit(hoveredNode, "dojotouchover", {
relatedTarget: oldNode,
bubbles: true
});
doClicks(evt, "touchmove", "touchend"); // init click generation
}, true);
function copyEventProps(evt){
// Make copy of event object and also set bubbles:true. Used when calling on.emit().
var props = lang.delegate(evt, {
bubbles: true
});
if(has("ios") >= 6){
// On iOS6 "touches" became a non-enumerable property, which
// is not hit by for...in. Ditto for the other properties below.
props.touches = evt.touches;
props.altKey = evt.altKey;
props.changedTouches = evt.changedTouches;
props.ctrlKey = evt.ctrlKey;
props.metaKey = evt.metaKey;
props.shiftKey = evt.shiftKey;
props.targetTouches = evt.targetTouches;
}
return props;
}
on(win.doc, "touchmove", function(evt){
lastTouch = (new Date()).getTime();
var newNode = win.doc.elementFromPoint(
evt.pageX - (ios4 ? 0 : win.global.pageXOffset), // iOS 4 expects page coords
evt.pageY - (ios4 ? 0 : win.global.pageYOffset)
);
if(newNode){
// Fire synthetic touchover and touchout events on nodes since the browser won't do it natively.
if(hoveredNode !== newNode){
// touch out on the old node
on.emit(hoveredNode, "dojotouchout", {
relatedTarget: newNode,
bubbles: true
});
// touchover on the new node
on.emit(newNode, "dojotouchover", {
relatedTarget: hoveredNode,
bubbles: true
});
hoveredNode = newNode;
}
// Unlike a listener on "touchmove", on(node, "dojotouchmove", listener) fires when the finger
// drags over the specified node, regardless of which node the touch started on.
if(!on.emit(newNode, "dojotouchmove", copyEventProps(evt))){
// emit returns false when synthetic event "dojotouchmove" is cancelled, so we prevent the
// default behavior of the underlying native event "touchmove".
evt.preventDefault();
}
}
});
// Fire a dojotouchend event on the node where the finger was before it was removed from the screen.
// This is different than the native touchend, which fires on the node where the drag started.
on(win.doc, "touchend", function(evt){
lastTouch = (new Date()).getTime();
var node = win.doc.elementFromPoint(
evt.pageX - (ios4 ? 0 : win.global.pageXOffset), // iOS 4 expects page coords
evt.pageY - (ios4 ? 0 : win.global.pageYOffset)
) || win.body(); // if out of the screen
on.emit(node, "dojotouchend", copyEventProps(evt));
});
});
}
}
//device neutral events - touch.press|move|release|cancel/over/out
var touch = {
press: dualEvent("mousedown", "touchstart", pointer.down),
move: dualEvent("mousemove", "dojotouchmove", pointer.move),
release: dualEvent("mouseup", "dojotouchend", pointer.up),
cancel: dualEvent(mouse.leave, "touchcancel", hasPointer ? pointer.cancel : null),
over: dualEvent("mouseover", "dojotouchover", pointer.over),
out: dualEvent("mouseout", "dojotouchout", pointer.out),
enter: mouse._eventHandler(dualEvent("mouseover","dojotouchover", pointer.over)),
leave: mouse._eventHandler(dualEvent("mouseout", "dojotouchout", pointer.out))
};
/*=====
touch = {
// summary:
// This module provides unified touch event handlers by exporting
// press, move, release and cancel which can also run well on desktop.
// Based on http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html
// Also, if the dojoClick property is set to truthy on a DOM node, dojo/touch generates
// click events immediately for this node and its descendants (except for descendants that
// have a dojoClick property set to falsy), to avoid the delay before native browser click events,
// and regardless of whether evt.preventDefault() was called in a touch.press event listener.
//
// example:
// Used with dojo/on
// | define(["dojo/on", "dojo/touch"], function(on, touch){
// | on(node, touch.press, function(e){});
// | on(node, touch.move, function(e){});
// | on(node, touch.release, function(e){});
// | on(node, touch.cancel, function(e){});
// example:
// Used with touch.* directly
// | touch.press(node, function(e){});
// | touch.move(node, function(e){});
// | touch.release(node, function(e){});
// | touch.cancel(node, function(e){});
// example:
// Have dojo/touch generate clicks without delay, with a default move threshold of 4 pixels
// | node.dojoClick = true;
// example:
// Have dojo/touch generate clicks without delay, with a move threshold of 10 pixels horizontally and vertically
// | node.dojoClick = 10;
// example:
// Have dojo/touch generate clicks without delay, with a move threshold of 50 pixels horizontally and 10 pixels vertically
// | node.dojoClick = {x:50, y:5};
// example:
// Disable clicks without delay generated by dojo/touch on a node that has an ancestor with property dojoClick set to truthy
// | node.dojoClick = false;
press: function(node, listener){
// summary:
// Register a listener to 'touchstart'|'mousedown' for the given node
// node: Dom
// Target node to listen to
// listener: Function
// Callback function
// returns:
// A handle which will be used to remove the listener by handle.remove()
},
move: function(node, listener){
// summary:
// Register a listener that fires when the mouse cursor or a finger is dragged over the given node.
// node: Dom
// Target node to listen to
// listener: Function
// Callback function
// returns:
// A handle which will be used to remove the listener by handle.remove()
},
release: function(node, listener){
// summary:
// Register a listener to releasing the mouse button while the cursor is over the given node
// (i.e. "mouseup") or for removing the finger from the screen while touching the given node.
// node: Dom
// Target node to listen to
// listener: Function
// Callback function
// returns:
// A handle which will be used to remove the listener by handle.remove()
},
cancel: function(node, listener){
// summary:
// Register a listener to 'touchcancel'|'mouseleave' for the given node
// node: Dom
// Target node to listen to
// listener: Function
// Callback function
// returns:
// A handle which will be used to remove the listener by handle.remove()
},
over: function(node, listener){
// summary:
// Register a listener to 'mouseover' or touch equivalent for the given node
// node: Dom
// Target node to listen to
// listener: Function
// Callback function
// returns:
// A handle which will be used to remove the listener by handle.remove()
},
out: function(node, listener){
// summary:
// Register a listener to 'mouseout' or touch equivalent for the given node
// node: Dom
// Target node to listen to
// listener: Function
// Callback function
// returns:
// A handle which will be used to remove the listener by handle.remove()
},
enter: function(node, listener){
// summary:
// Register a listener to mouse.enter or touch equivalent for the given node
// node: Dom
// Target node to listen to
// listener: Function
// Callback function
// returns:
// A handle which will be used to remove the listener by handle.remove()
},
leave: function(node, listener){
// summary:
// Register a listener to mouse.leave or touch equivalent for the given node
// node: Dom
// Target node to listen to
// listener: Function
// Callback function
// returns:
// A handle which will be used to remove the listener by handle.remove()
}
};
=====*/
1 && (dojo.touch = touch);
return touch;
});
},
'dijit/_FocusMixin':function(){
define([
"./focus",
"./_WidgetBase",
"dojo/_base/declare", // declare
"dojo/_base/lang" // lang.extend
], function(focus, _WidgetBase, declare, lang){
// module:
// dijit/_FocusMixin
// We don't know where _FocusMixin will occur in the inheritance chain, but we need the _onFocus()/_onBlur() below
// to be last in the inheritance chain, so mixin to _WidgetBase.
lang.extend(_WidgetBase, {
// focused: [readonly] Boolean
// This widget or a widget it contains has focus, or is "active" because
// it was recently clicked.
focused: false,
onFocus: function(){
// summary:
// Called when the widget becomes "active" because
// it or a widget inside of it either has focus, or has recently
// been clicked.
// tags:
// callback
},
onBlur: function(){
// summary:
// Called when the widget stops being "active" because
// focus moved to something outside of it, or the user
// clicked somewhere outside of it, or the widget was
// hidden.
// tags:
// callback
},
_onFocus: function(){
// summary:
// This is where widgets do processing for when they are active,
// such as changing CSS classes. See onFocus() for more details.
// tags:
// protected
this.onFocus();
},
_onBlur: function(){
// summary:
// This is where widgets do processing for when they stop being active,
// such as changing CSS classes. See onBlur() for more details.
// tags:
// protected
this.onBlur();
}
});
return declare("dijit._FocusMixin", null, {
// summary:
// Mixin to widget to provide _onFocus() and _onBlur() methods that
// fire when a widget or its descendants get/lose focus
// flag that I want _onFocus()/_onBlur() notifications from focus manager
_focusManager: focus
});
});
},
'dijit/focus':function(){
define([
"dojo/aspect",
"dojo/_base/declare", // declare
"dojo/dom", // domAttr.get dom.isDescendant
"dojo/dom-attr", // domAttr.get dom.isDescendant
"dojo/dom-class",
"dojo/dom-construct", // connect to domConstruct.empty, domConstruct.destroy
"dojo/Evented",
"dojo/_base/lang", // lang.hitch
"dojo/on",
"dojo/domReady",
"dojo/sniff", // has("ie")
"dojo/Stateful",
"dojo/_base/window", // win.body
"dojo/window", // winUtils.get
"./a11y", // a11y.isTabNavigable
"./registry", // registry.byId
"./main" // to set dijit.focus
], function(aspect, declare, dom, domAttr, domClass, domConstruct, Evented, lang, on, domReady, has, Stateful, win, winUtils,
a11y, registry, dijit){
// module:
// dijit/focus
// Time of the last focusin event
var lastFocusin;
// Time of the last touch/mousedown or focusin event
var lastTouchOrFocusin;
var FocusManager = declare([Stateful, Evented], {
// summary:
// Tracks the currently focused node, and which widgets are currently "active".
// Access via require(["dijit/focus"], function(focus){ ... }).
//
// A widget is considered active if it or a descendant widget has focus,
// or if a non-focusable node of this widget or a descendant was recently clicked.
//
// Call focus.watch("curNode", callback) to track the current focused DOMNode,
// or focus.watch("activeStack", callback) to track the currently focused stack of widgets.
//
// Call focus.on("widget-blur", func) or focus.on("widget-focus", ...) to monitor when
// when widgets become active/inactive
//
// Finally, focus(node) will focus a node, suppressing errors if the node doesn't exist.
// curNode: DomNode
// Currently focused item on screen
curNode: null,
// activeStack: dijit/_WidgetBase[]
// List of currently active widgets (focused widget and it's ancestors)
activeStack: [],
constructor: function(){
// Don't leave curNode/prevNode pointing to bogus elements
var check = lang.hitch(this, function(node){
if(dom.isDescendant(this.curNode, node)){
this.set("curNode", null);
}
if(dom.isDescendant(this.prevNode, node)){
this.set("prevNode", null);
}
});
aspect.before(domConstruct, "empty", check);
aspect.before(domConstruct, "destroy", check);
},
registerIframe: function(/*DomNode*/ iframe){
// summary:
// Registers listeners on the specified iframe so that any click
// or focus event on that iframe (or anything in it) is reported
// as a focus/click event on the `