webui-aria2/js/libs/dijit/dijit-all.js.uncompressed.js

33909 lines
1.1 MiB
JavaScript
Raw Normal View History

/*
Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
Available via Academic Free License >= 2.1 OR the modified BSD license.
see: http://dojotoolkit.org/license for details
*/
/*
This is an optimized version of Dojo, built for deployment and not for
development. To get sources and documentation, please visit:
http://dojotoolkit.org
*/
//>>built
require({cache:{
'dijit/_editor/plugins/FontChoice':function(){
define([
"dojo/_base/array", // array.indexOf array.map
"dojo/_base/declare", // declare
"dojo/dom-construct", // domConstruct.place
"dojo/i18n", // i18n.getLocalization
"dojo/_base/lang", // lang.delegate lang.hitch lang.isString
"dojo/store/Memory", // MemoryStore
"dojo/_base/window", // win.withGlobal
"../../registry", // registry.getUniqueId
"../../_Widget",
"../../_TemplatedMixin",
"../../_WidgetsInTemplateMixin",
"../../form/FilteringSelect",
"../_Plugin",
"../range",
"../selection",
"dojo/i18n!../nls/FontChoice"
], function(array, declare, domConstruct, i18n, lang, MemoryStore, win,
registry, _Widget, _TemplatedMixin, _WidgetsInTemplateMixin, FilteringSelect, _Plugin, rangeapi, selectionapi){
/*=====
var _Plugin = dijit._editor._Plugin;
var _Widget = dijit._Widget;
var _TemplatedMixin = dijit._TemplatedMixin;
var _WidgetsInTemplateMixin = dijit._WidgetsInTemplateMixin;
var FilteringSelect = dijit.form.FilteringSelect;
=====*/
// module:
// dijit/_editor/plugins/FontChoice
// summary:
// fontchoice, fontsize, and formatblock editor plugins
var _FontDropDown = declare("dijit._editor.plugins._FontDropDown",
[_Widget, _TemplatedMixin, _WidgetsInTemplateMixin], {
// summary:
// Base class for widgets that contains a label (like "Font:")
// and a FilteringSelect drop down to pick a value.
// Used as Toolbar entry.
// label: [public] String
// The label to apply to this particular FontDropDown.
label: "",
// plainText: [public] boolean
// Flag to indicate that the returned label should be plain text
// instead of an example.
plainText: false,
// templateString: [public] String
// The template used to construct the labeled dropdown.
templateString:
"<span style='white-space: nowrap' class='dijit dijitReset dijitInline'>" +
"<label class='dijitLeft dijitInline' for='${selectId}'>${label}</label>" +
"<input data-dojo-type='dijit.form.FilteringSelect' required='false' " +
"data-dojo-props='labelType:\"html\", labelAttr:\"label\", searchAttr:\"name\"' " +
"tabIndex='-1' id='${selectId}' data-dojo-attach-point='select' value=''/>" +
"</span>",
postMixInProperties: function(){
// summary:
// Over-ride to set specific properties.
this.inherited(arguments);
this.strings = i18n.getLocalization("dijit._editor", "FontChoice");
// Set some substitution variables used in the template
this.label = this.strings[this.command];
this.id = registry.getUniqueId(this.declaredClass.replace(/\./g,"_")); // TODO: unneeded??
this.selectId = this.id + "_select"; // used in template
this.inherited(arguments);
},
postCreate: function(){
// summary:
// Over-ride for the default postCreate action
// This establishes the filtering selects and the like.
// Initialize the list of items in the drop down by creating data store with items like:
// {value: 1, name: "xx-small", label: "<font size=1>xx-small</font-size>" }
this.select.set("store", new MemoryStore({
idProperty: "value",
data: array.map(this.values, function(value){
var name = this.strings[value] || value;
return {
label: this.getLabel(value, name),
name: name,
value: value
};
}, this)
}));
this.select.set("value", "", false);
this.disabled = this.select.get("disabled");
},
_setValueAttr: function(value, priorityChange){
// summary:
// Over-ride for the default action of setting the
// widget value, maps the input to known values
// value: Object|String
// The value to set in the select.
// priorityChange:
// Optional parameter used to tell the select whether or not to fire
// onChange event.
// if the value is not a permitted value, just set empty string to prevent showing the warning icon
priorityChange = priorityChange !== false;
this.select.set('value', array.indexOf(this.values,value) < 0 ? "" : value, priorityChange);
if(!priorityChange){
// Clear the last state in case of updateState calls. Ref: #10466
this.select._lastValueReported=null;
}
},
_getValueAttr: function(){
// summary:
// Allow retrieving the value from the composite select on
// call to button.get("value");
return this.select.get('value');
},
focus: function(){
// summary:
// Over-ride for focus control of this widget. Delegates focus down to the
// filtering select.
this.select.focus();
},
_setDisabledAttr: function(value){
// summary:
// Over-ride for the button's 'disabled' attribute so that it can be
// disabled programmatically.
// Save off ths disabled state so the get retrieves it correctly
//without needing to have a function proxy it.
this.disabled = value;
this.select.set("disabled", value);
}
});
var _FontNameDropDown = declare("dijit._editor.plugins._FontNameDropDown", _FontDropDown, {
// summary:
// Dropdown to select a font; goes in editor toolbar.
// generic: Boolean
// Use generic (web standard) font names
generic: false,
// command: [public] String
// The editor 'command' implemented by this plugin.
command: "fontName",
postMixInProperties: function(){
// summary:
// Over-ride for the default posr mixin control
if(!this.values){
this.values = this.generic ?
["serif", "sans-serif", "monospace", "cursive", "fantasy"] : // CSS font-family generics
["Arial", "Times New Roman", "Comic Sans MS", "Courier New"];
}
this.inherited(arguments);
},
getLabel: function(value, name){
// summary:
// Function used to generate the labels of the format dropdown
// will return a formatted, or plain label based on the value
// of the plainText option.
// value: String
// The 'insert value' associated with a name
// name: String
// The text name of the value
if(this.plainText){
return name;
}else{
return "<div style='font-family: "+value+"'>" + name + "</div>";
}
},
_setValueAttr: function(value, priorityChange){
// summary:
// Over-ride for the default action of setting the
// widget value, maps the input to known values
priorityChange = priorityChange !== false;
if(this.generic){
var map = {
"Arial": "sans-serif",
"Helvetica": "sans-serif",
"Myriad": "sans-serif",
"Times": "serif",
"Times New Roman": "serif",
"Comic Sans MS": "cursive",
"Apple Chancery": "cursive",
"Courier": "monospace",
"Courier New": "monospace",
"Papyrus": "fantasy",
"Estrangelo Edessa": "cursive", // Windows 7
"Gabriola": "fantasy" // Windows 7
};
value = map[value] || value;
}
this.inherited(arguments, [value, priorityChange]);
}
});
var _FontSizeDropDown = declare("dijit._editor.plugins._FontSizeDropDown", _FontDropDown, {
// summary:
// Dropdown to select a font size; goes in editor toolbar.
// command: [public] String
// The editor 'command' implemented by this plugin.
command: "fontSize",
// values: [public] Number[]
// The HTML font size values supported by this plugin
values: [1,2,3,4,5,6,7], // sizes according to the old HTML FONT SIZE
getLabel: function(value, name){
// summary:
// Function used to generate the labels of the format dropdown
// will return a formatted, or plain label based on the value
// of the plainText option.
// We're stuck using the deprecated FONT tag to correspond
// with the size measurements used by the editor
// value: String
// The 'insert value' associated with a name
// name: String
// The text name of the value
if(this.plainText){
return name;
}else{
return "<font size=" + value + "'>" + name + "</font>";
}
},
_setValueAttr: function(value, priorityChange){
// summary:
// Over-ride for the default action of setting the
// widget value, maps the input to known values
priorityChange = priorityChange !== false;
if(value.indexOf && value.indexOf("px") != -1){
var pixels = parseInt(value, 10);
value = {10:1, 13:2, 16:3, 18:4, 24:5, 32:6, 48:7}[pixels] || value;
}
this.inherited(arguments, [value, priorityChange]);
}
});
var _FormatBlockDropDown = declare("dijit._editor.plugins._FormatBlockDropDown", _FontDropDown, {
// summary:
// Dropdown to select a format (like paragraph or heading); goes in editor toolbar.
// command: [public] String
// The editor 'command' implemented by this plugin.
command: "formatBlock",
// values: [public] Array
// The HTML format tags supported by this plugin
values: ["noFormat", "p", "h1", "h2", "h3", "pre"],
postCreate: function(){
// Init and set the default value to no formatting. Update state will adjust it
// as needed.
this.inherited(arguments);
this.set("value", "noFormat", false);
},
getLabel: function(value, name){
// summary:
// Function used to generate the labels of the format dropdown
// will return a formatted, or plain label based on the value
// of the plainText option.
// value: String
// The 'insert value' associated with a name
// name: String
// The text name of the value
if(this.plainText || value == "noFormat"){
return name;
}else{
return "<" + value + ">" + name + "</" + value + ">";
}
},
_execCommand: function(editor, command, choice){
// summary:
// Over-ride for default exec-command label.
// Allows us to treat 'none' as special.
if(choice === "noFormat"){
var start;
var end;
var sel = rangeapi.getSelection(editor.window);
if(sel && sel.rangeCount > 0){
var range = sel.getRangeAt(0);
var node, tag;
if(range){
start = range.startContainer;
end = range.endContainer;
// find containing nodes of start/end.
while(start && start !== editor.editNode &&
start !== editor.document.body &&
start.nodeType !== 1){
start = start.parentNode;
}
while(end && end !== editor.editNode &&
end !== editor.document.body &&
end.nodeType !== 1){
end = end.parentNode;
}
var processChildren = lang.hitch(this, function(node, ary){
if(node.childNodes && node.childNodes.length){
var i;
for(i = 0; i < node.childNodes.length; i++){
var c = node.childNodes[i];
if(c.nodeType == 1){
if(win.withGlobal(editor.window, "inSelection", selectionapi, [c])){
var tag = c.tagName? c.tagName.toLowerCase(): "";
if(array.indexOf(this.values, tag) !== -1){
ary.push(c);
}
processChildren(c, ary);
}
}
}
}
});
var unformatNodes = lang.hitch(this, function(nodes){
// summary:
// Internal function to clear format nodes.
// nodes:
// The array of nodes to strip formatting from.
if(nodes && nodes.length){
editor.beginEditing();
while(nodes.length){
this._removeFormat(editor, nodes.pop());
}
editor.endEditing();
}
});
var clearNodes = [];
if(start == end){
//Contained within the same block, may be collapsed, but who cares, see if we
// have a block element to remove.
var block;
node = start;
while(node && node !== editor.editNode && node !== editor.document.body){
if(node.nodeType == 1){
tag = node.tagName? node.tagName.toLowerCase(): "";
if(array.indexOf(this.values, tag) !== -1){
block = node;
break;
}
}
node = node.parentNode;
}
//Also look for all child nodes in the selection that may need to be
//cleared of formatting
processChildren(start, clearNodes);
if(block){ clearNodes = [block].concat(clearNodes); }
unformatNodes(clearNodes);
}else{
// Probably a multi select, so we have to process it. Whee.
node = start;
while(win.withGlobal(editor.window, "inSelection", selectionapi, [node])){
if(node.nodeType == 1){
tag = node.tagName? node.tagName.toLowerCase(): "";
if(array.indexOf(this.values, tag) !== -1){
clearNodes.push(node);
}
processChildren(node,clearNodes);
}
node = node.nextSibling;
}
unformatNodes(clearNodes);
}
editor.onDisplayChanged();
}
}
}else{
editor.execCommand(command, choice);
}
},
_removeFormat: function(editor, node){
// summary:
// function to remove the block format node.
// node:
// The block format node to remove (and leave the contents behind)
if(editor.customUndo){
// So of course IE doesn't work right with paste-overs.
// We have to do this manually, which is okay since IE already uses
// customUndo and we turned it on for WebKit. WebKit pasted funny,
// so couldn't use the execCommand approach
while(node.firstChild){
domConstruct.place(node.firstChild, node, "before");
}
node.parentNode.removeChild(node);
}else{
// Everyone else works fine this way, a paste-over and is native
// undo friendly.
win.withGlobal(editor.window,
"selectElementChildren", selectionapi, [node]);
var html = win.withGlobal(editor.window,
"getSelectedHtml", selectionapi, [null]);
win.withGlobal(editor.window,
"selectElement", selectionapi, [node]);
editor.execCommand("inserthtml", html||"");
}
}
});
// TODO: for 2.0, split into FontChoice plugin into three separate classes,
// one for each command (and change registry below)
var FontChoice = declare("dijit._editor.plugins.FontChoice", _Plugin,{
// summary:
// This plugin provides three drop downs for setting style in the editor
// (font, font size, and format block), as controlled by command.
//
// description:
// The commands provided by this plugin are:
//
// * fontName
// | Provides a drop down to select from a list of font names
// * fontSize
// | Provides a drop down to select from a list of font sizes
// * formatBlock
// | Provides a drop down to select from a list of block styles
// |
//
// which can easily be added to an editor by including one or more of the above commands
// in the `plugins` attribute as follows:
//
// | plugins="['fontName','fontSize',...]"
//
// It is possible to override the default dropdown list by providing an Array for the `custom` property when
// instantiating this plugin, e.g.
//
// | plugins="[{name:'dijit._editor.plugins.FontChoice', command:'fontName', custom:['Verdana','Myriad','Garamond']},...]"
//
// Alternatively, for `fontName` only, `generic:true` may be specified to provide a dropdown with
// [CSS generic font families](http://www.w3.org/TR/REC-CSS2/fonts.html#generic-font-families)
//
// Note that the editor is often unable to properly handle font styling information defined outside
// the context of the current editor instance, such as pre-populated HTML.
// useDefaultCommand: [protected] Boolean
// Override _Plugin.useDefaultCommand...
// processing is handled by this plugin, not by dijit.Editor.
useDefaultCommand: false,
_initButton: function(){
// summary:
// Overrides _Plugin._initButton(), to initialize the FilteringSelect+label in toolbar,
// rather than a simple button.
// tags:
// protected
// Create the widget to go into the toolbar (the so-called "button")
var clazz = {
fontName: _FontNameDropDown,
fontSize: _FontSizeDropDown,
formatBlock: _FormatBlockDropDown
}[this.command],
params = this.params;
// For back-compat reasons support setting custom values via "custom" parameter
// rather than "values" parameter
if(this.params.custom){
params.values = this.params.custom;
}
var editor = this.editor;
this.button = new clazz(lang.delegate({dir: editor.dir, lang: editor.lang}, params));
// Reflect changes to the drop down in the editor
this.connect(this.button.select, "onChange", function(choice){
// User invoked change, since all internal updates set priorityChange to false and will
// not trigger an onChange event.
this.editor.focus();
if(this.command == "fontName" && choice.indexOf(" ") != -1){ choice = "'" + choice + "'"; }
// Invoke, the editor already normalizes commands called through its
// execCommand.
if(this.button._execCommand){
this.button._execCommand(this.editor, this.command, choice);
}else{
this.editor.execCommand(this.command, choice);
}
});
},
updateState: function(){
// summary:
// Overrides _Plugin.updateState(). This controls updating the menu
// options to the right values on state changes in the document (that trigger a
// test of the actions.)
// It set value of drop down in toolbar to reflect font/font size/format block
// of text at current caret position.
// tags:
// protected
var _e = this.editor;
var _c = this.command;
if(!_e || !_e.isLoaded || !_c.length){ return; }
if(this.button){
var disabled = this.get("disabled");
this.button.set("disabled", disabled);
if(disabled){ return; }
var value;
try{
value = _e.queryCommandValue(_c) || "";
}catch(e){
//Firefox may throw error above if the editor is just loaded, ignore it
value = "";
}
// strip off single quotes, if any
var quoted = lang.isString(value) && value.match(/'([^']*)'/);
if(quoted){ value = quoted[1]; }
if(_c === "formatBlock"){
if(!value || value == "p"){
// Some browsers (WebKit) doesn't actually get the tag info right.
// and IE returns paragraph when in a DIV!, so incorrect a lot,
// so we have double-check it.
value = null;
var elem;
// Try to find the current element where the caret is.
var sel = rangeapi.getSelection(this.editor.window);
if(sel && sel.rangeCount > 0){
var range = sel.getRangeAt(0);
if(range){
elem = range.endContainer;
}
}
// Okay, now see if we can find one of the formatting types we're in.
while(elem && elem !== _e.editNode && elem !== _e.document){
var tg = elem.tagName?elem.tagName.toLowerCase():"";
if(tg && array.indexOf(this.button.values, tg) > -1){
value = tg;
break;
}
elem = elem.parentNode;
}
if(!value){
// Still no value, so lets select 'none'.
value = "noFormat";
}
}else{
// Check that the block format is one allowed, if not,
// null it so that it gets set to empty.
if(array.indexOf(this.button.values, value) < 0){
value = "noFormat";
}
}
}
if(value !== this.button.get("value")){
// Set the value, but denote it is not a priority change, so no
// onchange fires.
this.button.set('value', value, false);
}
}
}
});
// Register these plugins
array.forEach(["fontName", "fontSize", "formatBlock"], function(name){
_Plugin.registry[name] = function(args){
return new FontChoice({
command: name,
plainText: args.plainText
});
};
});
});
},
'dijit/form/nls/validate':function(){
define({ root:
//begin v1.x content
({
invalidMessage: "The value entered is not valid.",
missingMessage: "This value is required.",
rangeMessage: "This value is out of range."
})
//end v1.x content
,
"zh": true,
"zh-tw": true,
"tr": true,
"th": true,
"sv": true,
"sl": true,
"sk": true,
"ru": true,
"ro": true,
"pt": true,
"pt-pt": true,
"pl": true,
"nl": true,
"nb": true,
"ko": true,
"kk": true,
"ja": true,
"it": true,
"hu": true,
"hr": true,
"he": true,
"fr": true,
"fi": true,
"es": true,
"el": true,
"de": true,
"da": true,
"cs": true,
"ca": true,
"az": true,
"ar": true
});
},
'url:dijit/templates/CheckedMenuItem.html':"<tr class=\"dijitReset dijitMenuItem\" data-dojo-attach-point=\"focusNode\" role=\"menuitemcheckbox\" tabIndex=\"-1\"\n\t\tdata-dojo-attach-event=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuItemIcon dijitCheckedMenuItemIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t\t<span class=\"dijitCheckedMenuItemIconChar\">&#10003;</span>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" data-dojo-attach-point=\"containerNode,labelNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" data-dojo-attach-point=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">&#160;</td>\n</tr>\n",
'dijit/form/TextBox':function(){
define([
"dojo/_base/declare", // declare
"dojo/dom-construct", // domConstruct.create
"dojo/dom-style", // domStyle.getComputedStyle
"dojo/_base/kernel", // kernel.deprecated
"dojo/_base/lang", // lang.hitch
"dojo/_base/sniff", // has("ie") has("mozilla")
"dojo/_base/window", // win.doc.selection.createRange
"./_FormValueWidget",
"./_TextBoxMixin",
"dojo/text!./templates/TextBox.html",
".." // to export dijit._setSelectionRange, remove in 2.0
], function(declare, domConstruct, domStyle, kernel, lang, has, win,
_FormValueWidget, _TextBoxMixin, template, dijit){
/*=====
var _FormValueWidget = dijit.form._FormValueWidget;
var _TextBoxMixin = dijit.form._TextBoxMixin;
=====*/
// module:
// dijit/form/TextBox
// summary:
// A base class for textbox form inputs
var TextBox = declare(/*====="dijit.form.TextBox", =====*/ [_FormValueWidget, _TextBoxMixin], {
// summary:
// A base class for textbox form inputs
templateString: template,
_singleNodeTemplate: '<input class="dijit dijitReset dijitLeft dijitInputField" data-dojo-attach-point="textbox,focusNode" autocomplete="off" type="${type}" ${!nameAttrSetting} />',
_buttonInputDisabled: has("ie") ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
baseClass: "dijitTextBox",
postMixInProperties: function(){
var type = this.type.toLowerCase();
if(this.templateString && this.templateString.toLowerCase() == "input" || ((type == "hidden" || type == "file") && this.templateString == this.constructor.prototype.templateString)){
this.templateString = this._singleNodeTemplate;
}
this.inherited(arguments);
},
_onInput: function(e){
this.inherited(arguments);
if(this.intermediateChanges){ // _TextBoxMixin uses onInput
var _this = this;
// the setTimeout allows the key to post to the widget input box
setTimeout(function(){ _this._handleOnChange(_this.get('value'), false); }, 0);
}
},
_setPlaceHolderAttr: function(v){
this._set("placeHolder", v);
if(!this._phspan){
this._attachPoints.push('_phspan');
// dijitInputField class gives placeHolder same padding as the input field
// parent node already has dijitInputField class but it doesn't affect this <span>
// since it's position: absolute.
this._phspan = domConstruct.create('span',{className:'dijitPlaceHolder dijitInputField'},this.textbox,'after');
}
this._phspan.innerHTML="";
this._phspan.appendChild(document.createTextNode(v));
this._updatePlaceHolder();
},
_updatePlaceHolder: function(){
if(this._phspan){
this._phspan.style.display=(this.placeHolder&&!this.focused&&!this.textbox.value)?"":"none";
}
},
_setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
this.inherited(arguments);
this._updatePlaceHolder();
},
getDisplayedValue: function(){
// summary:
// Deprecated. Use get('displayedValue') instead.
// tags:
// deprecated
kernel.deprecated(this.declaredClass+"::getDisplayedValue() is deprecated. Use set('displayedValue') instead.", "", "2.0");
return this.get('displayedValue');
},
setDisplayedValue: function(/*String*/ value){
// summary:
// Deprecated. Use set('displayedValue', ...) instead.
// tags:
// deprecated
kernel.deprecated(this.declaredClass+"::setDisplayedValue() is deprecated. Use set('displayedValue', ...) instead.", "", "2.0");
this.set('displayedValue', value);
},
_onBlur: function(e){
if(this.disabled){ return; }
this.inherited(arguments);
this._updatePlaceHolder();
},
_onFocus: function(/*String*/ by){
if(this.disabled || this.readOnly){ return; }
this.inherited(arguments);
this._updatePlaceHolder();
}
});
if(has("ie")){
TextBox = declare(/*===== "dijit.form.TextBox.IEMixin", =====*/ TextBox, {
declaredClass: "dijit.form.TextBox", // for user code referencing declaredClass
_isTextSelected: function(){
var range = win.doc.selection.createRange();
var parent = range.parentElement();
return parent == this.textbox && range.text.length == 0;
},
postCreate: function(){
this.inherited(arguments);
// IE INPUT tag fontFamily has to be set directly using STYLE
// the setTimeout gives IE a chance to render the TextBox and to deal with font inheritance
setTimeout(lang.hitch(this, function(){
try{
var s = domStyle.getComputedStyle(this.domNode); // can throw an exception if widget is immediately destroyed
if(s){
var ff = s.fontFamily;
if(ff){
var inputs = this.domNode.getElementsByTagName("INPUT");
if(inputs){
for(var i=0; i < inputs.length; i++){
inputs[i].style.fontFamily = ff;
}
}
}
}
}catch(e){/*when used in a Dialog, and this is called before the dialog is
shown, s.fontFamily would trigger "Invalid Argument" error.*/}
}), 0);
}
});
// Overrides definition of _setSelectionRange from _TextBoxMixin (TODO: move to _TextBoxMixin.js?)
dijit._setSelectionRange = _TextBoxMixin._setSelectionRange = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){
if(element.createTextRange){
var r = element.createTextRange();
r.collapse(true);
r.moveStart("character", -99999); // move to 0
r.moveStart("character", start); // delta from 0 is the correct position
r.moveEnd("character", stop-start);
r.select();
}
}
}else if(has("mozilla")){
TextBox = declare(/*===== "dijit.form.TextBox.MozMixin", =====*/TextBox, {
declaredClass: "dijit.form.TextBox", // for user code referencing declaredClass
_onBlur: function(e){
this.inherited(arguments);
if(this.selectOnClick){
// clear selection so that the next mouse click doesn't reselect
this.textbox.selectionStart = this.textbox.selectionEnd = undefined;
}
}
});
}else{
TextBox.prototype.declaredClass = "dijit.form.TextBox";
}
lang.setObject("dijit.form.TextBox", TextBox); // don't do direct assignment, it confuses API doc parser
return TextBox;
});
},
'dojo/currency':function(){
define(["./_base/kernel", "./_base/lang", "./_base/array", "./number", "./i18n", "./i18n!./cldr/nls/currency", "./cldr/monetary"], function(dojo, lang, darray, dnumber, i18n, nlsCurrency, cldrMonetary) {
// module:
// dojo/currency
// summary:
// TODOC
lang.getObject("currency", true, dojo);
/*=====
dojo.currency = {
// summary: localized formatting and parsing routines for currencies
//
// description: extends dojo.number to provide culturally-appropriate formatting of values
// in various world currencies, including use of a currency symbol. The currencies are specified
// by a three-letter international symbol in all uppercase, and support for the currencies is
// provided by the data in `dojo.cldr`. The scripts generating dojo.cldr specify which
// currency support is included. A fixed number of decimal places is determined based
// on the currency type and is not determined by the 'pattern' argument. The fractional
// portion is optional, by default, and variable length decimals are not supported.
}
=====*/
dojo.currency._mixInDefaults = function(options){
options = options || {};
options.type = "currency";
// Get locale-dependent currency data, like the symbol
var bundle = i18n.getLocalization("dojo.cldr", "currency", options.locale) || {};
// Mixin locale-independent currency data, like # of places
var iso = options.currency;
var data = cldrMonetary.getData(iso);
darray.forEach(["displayName","symbol","group","decimal"], function(prop){
data[prop] = bundle[iso+"_"+prop];
});
data.fractional = [true, false];
// Mixin with provided options
return lang.mixin(data, options);
};
/*=====
dojo.declare("dojo.currency.__FormatOptions", [dojo.number.__FormatOptions], {
// type: String?
// Should not be set. Value is assumed to be "currency".
// symbol: String?
// localized currency symbol. The default will be looked up in table of supported currencies in `dojo.cldr`
// A [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code will be used if not found.
// currency: String?
// an [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code, a three letter sequence like "USD".
// For use with dojo.currency only.
// places: Number?
// number of decimal places to show. Default is defined based on which currency is used.
type: "",
symbol: "",
currency: "",
places: ""
});
=====*/
dojo.currency.format = function(/*Number*/value, /*dojo.currency.__FormatOptions?*/options){
// summary:
// Format a Number as a currency, using locale-specific settings
//
// description:
// Create a string from a Number using a known, localized pattern.
// [Formatting patterns](http://www.unicode.org/reports/tr35/#Number_Elements)
// appropriate to the locale are chosen from the [CLDR](http://unicode.org/cldr)
// as well as the appropriate symbols and delimiters and number of decimal places.
//
// value:
// the number to be formatted.
return dnumber.format(value, dojo.currency._mixInDefaults(options));
};
dojo.currency.regexp = function(/*dojo.number.__RegexpOptions?*/options){
//
// summary:
// Builds the regular needed to parse a currency value
//
// description:
// Returns regular expression with positive and negative match, group and decimal separators
// Note: the options.places default, the number of decimal places to accept, is defined by the currency type.
return dnumber.regexp(dojo.currency._mixInDefaults(options)); // String
};
/*=====
dojo.declare("dojo.currency.__ParseOptions", [dojo.number.__ParseOptions], {
// type: String?
// Should not be set. Value is assumed to be currency.
// currency: String?
// an [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code, a three letter sequence like "USD".
// For use with dojo.currency only.
// symbol: String?
// localized currency symbol. The default will be looked up in table of supported currencies in `dojo.cldr`
// A [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code will be used if not found.
// places: Number?
// fixed number of decimal places to accept. The default is determined based on which currency is used.
// fractional: Boolean?|Array?
// Whether to include the fractional portion, where the number of decimal places are implied by the currency
// or explicit 'places' parameter. The value [true,false] makes the fractional portion optional.
// By default for currencies, it the fractional portion is optional.
type: "",
currency: "",
symbol: "",
places: "",
fractional: ""
});
=====*/
dojo.currency.parse = function(/*String*/expression, /*dojo.currency.__ParseOptions?*/options){
//
// summary:
// Convert a properly formatted currency string to a primitive Number,
// using locale-specific settings.
//
// description:
// Create a Number from a string using a known, localized pattern.
// [Formatting patterns](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
// are chosen appropriate to the locale, as well as the appropriate symbols and delimiters
// and number of decimal places.
//
// expression: A string representation of a currency value
return dnumber.parse(expression, dojo.currency._mixInDefaults(options));
};
return dojo.currency;
});
},
'dijit/DialogUnderlay':function(){
define([
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/_base/window", // win.body
"dojo/window", // winUtils.getBox
"./_Widget",
"./_TemplatedMixin",
"./BackgroundIframe"
], function(declare, domAttr, win, winUtils, _Widget, _TemplatedMixin, BackgroundIframe){
/*=====
var _Widget = dijit._Widget;
var _TemplatedMixin = dijit._TemplatedMixin;
=====*/
// module:
// dijit/DialogUnderlay
// summary:
// The component that blocks the screen behind a `dijit.Dialog`
return declare("dijit.DialogUnderlay", [_Widget, _TemplatedMixin], {
// summary:
// The component that blocks the screen behind a `dijit.Dialog`
//
// description:
// A component used to block input behind a `dijit.Dialog`. Only a single
// instance of this widget is created by `dijit.Dialog`, and saved as
// a reference to be shared between all Dialogs as `dijit._underlay`
//
// The underlay itself can be styled based on and id:
// | #myDialog_underlay { background-color:red; }
//
// In the case of `dijit.Dialog`, this id is based on the id of the Dialog,
// suffixed with _underlay.
// Template has two divs; outer div is used for fade-in/fade-out, and also to hold background iframe.
// Inner div has opacity specified in CSS file.
templateString: "<div class='dijitDialogUnderlayWrapper'><div class='dijitDialogUnderlay' data-dojo-attach-point='node'></div></div>",
// Parameters on creation or updatable later
// dialogId: String
// Id of the dialog.... DialogUnderlay's id is based on this id
dialogId: "",
// class: String
// This class name is used on the DialogUnderlay node, in addition to dijitDialogUnderlay
"class": "",
_setDialogIdAttr: function(id){
domAttr.set(this.node, "id", id + "_underlay");
this._set("dialogId", id);
},
_setClassAttr: function(clazz){
this.node.className = "dijitDialogUnderlay " + clazz;
this._set("class", clazz);
},
postCreate: function(){
// summary:
// Append the underlay to the body
win.body().appendChild(this.domNode);
},
layout: function(){
// summary:
// Sets the background to the size of the viewport
//
// description:
// Sets the background to the size of the viewport (rather than the size
// of the document) since we need to cover the whole browser window, even
// if the document is only a few lines long.
// tags:
// private
var is = this.node.style,
os = this.domNode.style;
// hide the background temporarily, so that the background itself isn't
// causing scrollbars to appear (might happen when user shrinks browser
// window and then we are called to resize)
os.display = "none";
// then resize and show
var viewport = winUtils.getBox();
os.top = viewport.t + "px";
os.left = viewport.l + "px";
is.width = viewport.w + "px";
is.height = viewport.h + "px";
os.display = "block";
},
show: function(){
// summary:
// Show the dialog underlay
this.domNode.style.display = "block";
this.layout();
this.bgIframe = new BackgroundIframe(this.domNode);
},
hide: function(){
// summary:
// Hides the dialog underlay
this.bgIframe.destroy();
delete this.bgIframe;
this.domNode.style.display = "none";
}
});
});
},
'url:dijit/form/templates/ComboButton.html':"<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tcellspacing='0' cellpadding='0' role=\"presentation\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonNode\" data-dojo-attach-point=\"buttonNode\" data-dojo-attach-event=\"ondijitclick:_onClick,onkeypress:_onButtonKeyPress\"\n\t\t><div id=\"${id}_button\" class=\"dijitReset dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"titleNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><div class=\"dijitReset dijitInline dijitIcon\" data-dojo-attach-point=\"iconNode\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitInline dijitButtonText\" id=\"${id}_label\" data-dojo-attach-point=\"containerNode\" role=\"presentation\"></div\n\t\t></div\n\t\t></td\n\t\t><td id=\"${id}_arrow\" class='dijitReset dijitRight dijitButtonNode dijitArrowButton'\n\t\t\tdata-dojo-attach-point=\"_popupStateNode,focusNode,_buttonNode\"\n\t\t\tdata-dojo-attach-event=\"onkeypress:_onArrowKeyPress\"\n\t\t\ttitle=\"${optionsTitle}\"\n\t\t\trole=\"button\" aria-haspopup=\"true\"\n\t\t\t><div class=\"dijitReset dijitArrowButtonInner\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitArrowButtonChar\" role=\"presentation\">&#9660;</div\n\t\t></td\n\t\t><td style=\"display:none !important;\"\n\t\t\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" data-dojo-attach-point=\"valueNode\"\n\t\t/></td></tr></tbody\n></table>\n",
'dijit/layout/ScrollingTabController':function(){
define([
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.add domClass.contains
"dojo/dom-geometry", // domGeometry.contentBox
"dojo/dom-style", // domStyle.style
"dojo/_base/fx", // Animation
"dojo/_base/lang", // lang.hitch
"dojo/query", // query
"dojo/_base/sniff", // has("ie"), has("webkit"), has("quirks")
"../registry", // registry.byId()
"dojo/text!./templates/ScrollingTabController.html",
"dojo/text!./templates/_ScrollingTabControllerButton.html",
"./TabController",
"./utils", // marginBox2contextBox, layoutChildren
"../_WidgetsInTemplateMixin",
"../Menu",
"../MenuItem",
"../form/Button",
"../_HasDropDown",
"dojo/NodeList-dom" // NodeList.style
], function(array, declare, domClass, domGeometry, domStyle, fx, lang, query, has,
registry, tabControllerTemplate, buttonTemplate, TabController, layoutUtils, _WidgetsInTemplateMixin,
Menu, MenuItem, Button, _HasDropDown){
/*=====
var _WidgetsInTemplateMixin = dijit._WidgetsInTemplateMixin;
var Menu = dijit.Menu;
var _HasDropDown = dijit._HasDropDown;
var TabController = dijit.layout.TabController;
=====*/
// module:
// dijit/layout/ScrollingTabController
// summary:
// Set of tabs with left/right arrow keys and a menu to switch between tabs not
// all fitting on a single row.
var ScrollingTabController = declare("dijit.layout.ScrollingTabController", [TabController, _WidgetsInTemplateMixin], {
// summary:
// Set of tabs with left/right arrow keys and a menu to switch between tabs not
// all fitting on a single row.
// Works only for horizontal tabs (either above or below the content, not to the left
// or right).
// tags:
// private
baseClass: "dijitTabController dijitScrollingTabController",
templateString: tabControllerTemplate,
// useMenu: [const] Boolean
// True if a menu should be used to select tabs when they are too
// wide to fit the TabContainer, false otherwise.
useMenu: true,
// useSlider: [const] Boolean
// True if a slider should be used to select tabs when they are too
// wide to fit the TabContainer, false otherwise.
useSlider: true,
// tabStripClass: [const] String
// The css class to apply to the tab strip, if it is visible.
tabStripClass: "",
widgetsInTemplate: true,
// _minScroll: Number
// The distance in pixels from the edge of the tab strip which,
// if a scroll animation is less than, forces the scroll to
// go all the way to the left/right.
_minScroll: 5,
// Override default behavior mapping class to DOMNode
_setClassAttr: { node: "containerNode", type: "class" },
buildRendering: function(){
this.inherited(arguments);
var n = this.domNode;
this.scrollNode = this.tablistWrapper;
this._initButtons();
if(!this.tabStripClass){
this.tabStripClass = "dijitTabContainer" +
this.tabPosition.charAt(0).toUpperCase() +
this.tabPosition.substr(1).replace(/-.*/, "") +
"None";
domClass.add(n, "tabStrip-disabled")
}
domClass.add(this.tablistWrapper, this.tabStripClass);
},
onStartup: function(){
this.inherited(arguments);
// TabController is hidden until it finishes drawing, to give
// a less visually jumpy instantiation. When it's finished, set visibility to ""
// to that the tabs are hidden/shown depending on the container's visibility setting.
domStyle.set(this.domNode, "visibility", "");
this._postStartup = true;
},
onAddChild: function(page, insertIndex){
this.inherited(arguments);
// changes to the tab button label or iconClass will have changed the width of the
// buttons, so do a resize
array.forEach(["label", "iconClass"], function(attr){
this.pane2watches[page.id].push(
this.pane2button[page.id].watch(attr, lang.hitch(this, function(){
if(this._postStartup && this._dim){
this.resize(this._dim);
}
}))
);
}, this);
// Increment the width of the wrapper when a tab is added
// This makes sure that the buttons never wrap.
// The value 200 is chosen as it should be bigger than most
// Tab button widths.
domStyle.set(this.containerNode, "width",
(domStyle.get(this.containerNode, "width") + 200) + "px");
},
onRemoveChild: function(page, insertIndex){
// null out _selectedTab because we are about to delete that dom node
var button = this.pane2button[page.id];
if(this._selectedTab === button.domNode){
this._selectedTab = null;
}
this.inherited(arguments);
},
_initButtons: function(){
// summary:
// Creates the buttons used to scroll to view tabs that
// may not be visible if the TabContainer is too narrow.
// Make a list of the buttons to display when the tab labels become
// wider than the TabContainer, and hide the other buttons.
// Also gets the total width of the displayed buttons.
this._btnWidth = 0;
this._buttons = query("> .tabStripButton", this.domNode).filter(function(btn){
if((this.useMenu && btn == this._menuBtn.domNode) ||
(this.useSlider && (btn == this._rightBtn.domNode || btn == this._leftBtn.domNode))){
this._btnWidth += domGeometry.getMarginSize(btn).w;
return true;
}else{
domStyle.set(btn, "display", "none");
return false;
}
}, this);
},
_getTabsWidth: function(){
var children = this.getChildren();
if(children.length){
var leftTab = children[this.isLeftToRight() ? 0 : children.length - 1].domNode,
rightTab = children[this.isLeftToRight() ? children.length - 1 : 0].domNode;
return rightTab.offsetLeft + domStyle.get(rightTab, "width") - leftTab.offsetLeft;
}else{
return 0;
}
},
_enableBtn: function(width){
// summary:
// Determines if the tabs are wider than the width of the TabContainer, and
// thus that we need to display left/right/menu navigation buttons.
var tabsWidth = this._getTabsWidth();
width = width || domStyle.get(this.scrollNode, "width");
return tabsWidth > 0 && width < tabsWidth;
},
resize: function(dim){
// summary:
// Hides or displays the buttons used to scroll the tab list and launch the menu
// that selects tabs.
// Save the dimensions to be used when a child is renamed.
this._dim = dim;
// Set my height to be my natural height (tall enough for one row of tab labels),
// and my content-box width based on margin-box width specified in dim parameter.
// But first reset scrollNode.height in case it was set by layoutChildren() call
// in a previous run of this method.
this.scrollNode.style.height = "auto";
var cb = this._contentBox = layoutUtils.marginBox2contentBox(this.domNode, {h: 0, w: dim.w});
cb.h = this.scrollNode.offsetHeight;
domGeometry.setContentSize(this.domNode, cb);
// Show/hide the left/right/menu navigation buttons depending on whether or not they
// are needed.
var enable = this._enableBtn(this._contentBox.w);
this._buttons.style("display", enable ? "" : "none");
// Position and size the navigation buttons and the tablist
this._leftBtn.layoutAlign = "left";
this._rightBtn.layoutAlign = "right";
this._menuBtn.layoutAlign = this.isLeftToRight() ? "right" : "left";
layoutUtils.layoutChildren(this.domNode, this._contentBox,
[this._menuBtn, this._leftBtn, this._rightBtn, {domNode: this.scrollNode, layoutAlign: "client"}]);
// set proper scroll so that selected tab is visible
if(this._selectedTab){
if(this._anim && this._anim.status() == "playing"){
this._anim.stop();
}
this.scrollNode.scrollLeft = this._convertToScrollLeft(this._getScrollForSelectedTab());
}
// Enable/disabled left right buttons depending on whether or not user can scroll to left or right
this._setButtonClass(this._getScroll());
this._postResize = true;
// Return my size so layoutChildren() can use it.
// Also avoids IE9 layout glitch on browser resize when scroll buttons present
return {h: this._contentBox.h, w: dim.w};
},
_getScroll: function(){
// summary:
// Returns the current scroll of the tabs where 0 means
// "scrolled all the way to the left" and some positive number, based on #
// of pixels of possible scroll (ex: 1000) means "scrolled all the way to the right"
return (this.isLeftToRight() || has("ie") < 8 || (has("ie") && has("quirks")) || has("webkit")) ? this.scrollNode.scrollLeft :
domStyle.get(this.containerNode, "width") - domStyle.get(this.scrollNode, "width")
+ (has("ie") == 8 ? -1 : 1) * this.scrollNode.scrollLeft;
},
_convertToScrollLeft: function(val){
// summary:
// Given a scroll value where 0 means "scrolled all the way to the left"
// and some positive number, based on # of pixels of possible scroll (ex: 1000)
// means "scrolled all the way to the right", return value to set this.scrollNode.scrollLeft
// to achieve that scroll.
//
// This method is to adjust for RTL funniness in various browsers and versions.
if(this.isLeftToRight() || has("ie") < 8 || (has("ie") && has("quirks")) || has("webkit")){
return val;
}else{
var maxScroll = domStyle.get(this.containerNode, "width") - domStyle.get(this.scrollNode, "width");
return (has("ie") == 8 ? -1 : 1) * (val - maxScroll);
}
},
onSelectChild: function(/*dijit._Widget*/ page){
// summary:
// Smoothly scrolls to a tab when it is selected.
var tab = this.pane2button[page.id];
if(!tab || !page){return;}
var node = tab.domNode;
// Save the selection
if(node != this._selectedTab){
this._selectedTab = node;
// Scroll to the selected tab, except on startup, when scrolling is handled in resize()
if(this._postResize){
var sl = this._getScroll();
if(sl > node.offsetLeft ||
sl + domStyle.get(this.scrollNode, "width") <
node.offsetLeft + domStyle.get(node, "width")){
this.createSmoothScroll().play();
}
}
}
this.inherited(arguments);
},
_getScrollBounds: function(){
// summary:
// Returns the minimum and maximum scroll setting to show the leftmost and rightmost
// tabs (respectively)
var children = this.getChildren(),
scrollNodeWidth = domStyle.get(this.scrollNode, "width"), // about 500px
containerWidth = domStyle.get(this.containerNode, "width"), // 50,000px
maxPossibleScroll = containerWidth - scrollNodeWidth, // scrolling until right edge of containerNode visible
tabsWidth = this._getTabsWidth();
if(children.length && tabsWidth > scrollNodeWidth){
// Scrolling should happen
return {
min: this.isLeftToRight() ? 0 : children[children.length-1].domNode.offsetLeft,
max: this.isLeftToRight() ?
(children[children.length-1].domNode.offsetLeft + domStyle.get(children[children.length-1].domNode, "width")) - scrollNodeWidth :
maxPossibleScroll
};
}else{
// No scrolling needed, all tabs visible, we stay either scrolled to far left or far right (depending on dir)
var onlyScrollPosition = this.isLeftToRight() ? 0 : maxPossibleScroll;
return {
min: onlyScrollPosition,
max: onlyScrollPosition
};
}
},
_getScrollForSelectedTab: function(){
// summary:
// Returns the scroll value setting so that the selected tab
// will appear in the center
var w = this.scrollNode,
n = this._selectedTab,
scrollNodeWidth = domStyle.get(this.scrollNode, "width"),
scrollBounds = this._getScrollBounds();
// TODO: scroll minimal amount (to either right or left) so that
// selected tab is fully visible, and just return if it's already visible?
var pos = (n.offsetLeft + domStyle.get(n, "width")/2) - scrollNodeWidth/2;
pos = Math.min(Math.max(pos, scrollBounds.min), scrollBounds.max);
// TODO:
// If scrolling close to the left side or right side, scroll
// all the way to the left or right. See this._minScroll.
// (But need to make sure that doesn't scroll the tab out of view...)
return pos;
},
createSmoothScroll: function(x){
// summary:
// Creates a dojo._Animation object that smoothly scrolls the tab list
// either to a fixed horizontal pixel value, or to the selected tab.
// description:
// If an number argument is passed to the function, that horizontal
// pixel position is scrolled to. Otherwise the currently selected
// tab is scrolled to.
// x: Integer?
// An optional pixel value to scroll to, indicating distance from left.
// Calculate position to scroll to
if(arguments.length > 0){
// position specified by caller, just make sure it's within bounds
var scrollBounds = this._getScrollBounds();
x = Math.min(Math.max(x, scrollBounds.min), scrollBounds.max);
}else{
// scroll to center the current tab
x = this._getScrollForSelectedTab();
}
if(this._anim && this._anim.status() == "playing"){
this._anim.stop();
}
var self = this,
w = this.scrollNode,
anim = new fx.Animation({
beforeBegin: function(){
if(this.curve){ delete this.curve; }
var oldS = w.scrollLeft,
newS = self._convertToScrollLeft(x);
anim.curve = new fx._Line(oldS, newS);
},
onAnimate: function(val){
w.scrollLeft = val;
}
});
this._anim = anim;
// Disable/enable left/right buttons according to new scroll position
this._setButtonClass(x);
return anim; // dojo._Animation
},
_getBtnNode: function(/*Event*/ e){
// summary:
// Gets a button DOM node from a mouse click event.
// e:
// The mouse click event.
var n = e.target;
while(n && !domClass.contains(n, "tabStripButton")){
n = n.parentNode;
}
return n;
},
doSlideRight: function(/*Event*/ e){
// summary:
// Scrolls the menu to the right.
// e:
// The mouse click event.
this.doSlide(1, this._getBtnNode(e));
},
doSlideLeft: function(/*Event*/ e){
// summary:
// Scrolls the menu to the left.
// e:
// The mouse click event.
this.doSlide(-1,this._getBtnNode(e));
},
doSlide: function(/*Number*/ direction, /*DomNode*/ node){
// summary:
// Scrolls the tab list to the left or right by 75% of the widget width.
// direction:
// If the direction is 1, the widget scrolls to the right, if it is
// -1, it scrolls to the left.
if(node && domClass.contains(node, "dijitTabDisabled")){return;}
var sWidth = domStyle.get(this.scrollNode, "width");
var d = (sWidth * 0.75) * direction;
var to = this._getScroll() + d;
this._setButtonClass(to);
this.createSmoothScroll(to).play();
},
_setButtonClass: function(/*Number*/ scroll){
// summary:
// Disables the left scroll button if the tabs are scrolled all the way to the left,
// or the right scroll button in the opposite case.
// scroll: Integer
// amount of horizontal scroll
var scrollBounds = this._getScrollBounds();
this._leftBtn.set("disabled", scroll <= scrollBounds.min);
this._rightBtn.set("disabled", scroll >= scrollBounds.max);
}
});
var ScrollingTabControllerButtonMixin = declare("dijit.layout._ScrollingTabControllerButtonMixin", null, {
baseClass: "dijitTab tabStripButton",
templateString: buttonTemplate,
// Override inherited tabIndex: 0 from dijit.form.Button, because user shouldn't be
// able to tab to the left/right/menu buttons
tabIndex: "",
// Similarly, override FormWidget.isFocusable() because clicking a button shouldn't focus it
// either (this override avoids focus() call in FormWidget.js)
isFocusable: function(){ return false; }
});
/*=====
ScrollingTabControllerButtonMixin = dijit.layout._ScrollingTabControllerButtonMixin;
=====*/
// Class used in template
declare("dijit.layout._ScrollingTabControllerButton",
[Button, ScrollingTabControllerButtonMixin]);
// Class used in template
declare(
"dijit.layout._ScrollingTabControllerMenuButton",
[Button, _HasDropDown, ScrollingTabControllerButtonMixin],
{
// id of the TabContainer itself
containerId: "",
// -1 so user can't tab into the button, but so that button can still be focused programatically.
// Because need to move focus to the button (or somewhere) before the menu is hidden or IE6 will crash.
tabIndex: "-1",
isLoaded: function(){
// recreate menu every time, in case the TabContainer's list of children (or their icons/labels) have changed
return false;
},
loadDropDown: function(callback){
this.dropDown = new Menu({
id: this.containerId + "_menu",
dir: this.dir,
lang: this.lang,
textDir: this.textDir
});
var container = registry.byId(this.containerId);
array.forEach(container.getChildren(), function(page){
var menuItem = new MenuItem({
id: page.id + "_stcMi",
label: page.title,
iconClass: page.iconClass,
dir: page.dir,
lang: page.lang,
textDir: page.textDir,
onClick: function(){
container.selectChild(page);
}
});
this.dropDown.addChild(menuItem);
}, this);
callback();
},
closeDropDown: function(/*Boolean*/ focus){
this.inherited(arguments);
if(this.dropDown){
this.dropDown.destroyRecursive();
delete this.dropDown;
}
}
});
return ScrollingTabController;
});
},
'dijit/_editor/html':function(){
define([
"dojo/_base/lang", // lang.isString
"dojo/_base/sniff", // has("ie")
".." // for exporting symbols to dijit._editor (remove for 2.0)
], function(lang, has, dijit){
// module:
// dijit/_editor/html
// summary:
// Utility functions used by editor
lang.getObject("_editor", true, dijit);
dijit._editor.escapeXml=function(/*String*/str, /*Boolean?*/noSingleQuotes){
// summary:
// Adds escape sequences for special characters in XML: &<>"'
// Optionally skips escapes for single quotes
str = str.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
if(!noSingleQuotes){
str = str.replace(/'/gm, "&#39;");
}
return str; // string
};
dijit._editor.getNodeHtml=function(/* DomNode */node){
var output;
switch(node.nodeType){
case 1: //element node
var lName = node.nodeName.toLowerCase();
if(!lName || lName.charAt(0) == "/"){
// IE does some strange things with malformed HTML input, like
// treating a close tag </span> without an open tag <span>, as
// a new tag with tagName of /span. Corrupts output HTML, remove
// them. Other browsers don't prefix tags that way, so will
// never show up.
return "";
}
output = '<' + lName;
//store the list of attributes and sort it to have the
//attributes appear in the dictionary order
var attrarray = [];
var attr;
if(has("ie") && node.outerHTML){
var s = node.outerHTML;
s = s.substr(0, s.indexOf('>'))
.replace(/(['"])[^"']*\1/g, ''); //to make the following regexp safe
var reg = /(\b\w+)\s?=/g;
var m, key;
while((m = reg.exec(s))){
key = m[1];
if(key.substr(0,3) != '_dj'){
if(key == 'src' || key == 'href'){
if(node.getAttribute('_djrealurl')){
attrarray.push([key,node.getAttribute('_djrealurl')]);
continue;
}
}
var val, match;
switch(key){
case 'style':
val = node.style.cssText.toLowerCase();
break;
case 'class':
val = node.className;
break;
case 'width':
if(lName === "img"){
// This somehow gets lost on IE for IMG tags and the like
// and we have to find it in outerHTML, known IE oddity.
match=/width=(\S+)/i.exec(s);
if(match){
val = match[1];
}
break;
}
case 'height':
if(lName === "img"){
// This somehow gets lost on IE for IMG tags and the like
// and we have to find it in outerHTML, known IE oddity.
match=/height=(\S+)/i.exec(s);
if(match){
val = match[1];
}
break;
}
default:
val = node.getAttribute(key);
}
if(val != null){
attrarray.push([key, val.toString()]);
}
}
}
}else{
var i = 0;
while((attr = node.attributes[i++])){
//ignore all attributes starting with _dj which are
//internal temporary attributes used by the editor
var n = attr.name;
if(n.substr(0,3) != '_dj' /*&&
(attr.specified == undefined || attr.specified)*/){
var v = attr.value;
if(n == 'src' || n == 'href'){
if(node.getAttribute('_djrealurl')){
v = node.getAttribute('_djrealurl');
}
}
attrarray.push([n,v]);
}
}
}
attrarray.sort(function(a,b){
return a[0] < b[0] ? -1 : (a[0] == b[0] ? 0 : 1);
});
var j = 0;
while((attr = attrarray[j++])){
output += ' ' + attr[0] + '="' +
(lang.isString(attr[1]) ? dijit._editor.escapeXml(attr[1], true) : attr[1]) + '"';
}
if(lName === "script"){
// Browsers handle script tags differently in how you get content,
// but innerHTML always seems to work, so insert its content that way
// Yes, it's bad to allow script tags in the editor code, but some people
// seem to want to do it, so we need to at least return them right.
// other plugins/filters can strip them.
output += '>' + node.innerHTML +'</' + lName + '>';
}else{
if(node.childNodes.length){
output += '>' + dijit._editor.getChildrenHtml(node)+'</' + lName +'>';
}else{
switch(lName){
case 'br':
case 'hr':
case 'img':
case 'input':
case 'base':
case 'meta':
case 'area':
case 'basefont':
// These should all be singly closed
output += ' />';
break;
default:
// Assume XML style separate closure for everything else.
output += '></' + lName + '>';
}
}
}
break;
case 4: // cdata
case 3: // text
// FIXME:
output = dijit._editor.escapeXml(node.nodeValue, true);
break;
case 8: //comment
// FIXME:
output = '<!--' + dijit._editor.escapeXml(node.nodeValue, true) + '-->';
break;
default:
output = "<!-- Element not recognized - Type: " + node.nodeType + " Name: " + node.nodeName + "-->";
}
return output;
};
dijit._editor.getChildrenHtml = function(/* DomNode */dom){
// summary:
// Returns the html content of a DomNode and children
var out = "";
if(!dom){ return out; }
var nodes = dom["childNodes"] || dom;
//IE issue.
//If we have an actual node we can check parent relationships on for IE,
//We should check, as IE sometimes builds invalid DOMS. If no parent, we can't check
//And should just process it and hope for the best.
var checkParent = !has("ie") || nodes !== dom;
var node, i = 0;
while((node = nodes[i++])){
//IE is broken. DOMs are supposed to be a tree. But in the case of malformed HTML, IE generates a graph
//meaning one node ends up with multiple references (multiple parents). This is totally wrong and invalid, but
//such is what it is. We have to keep track and check for this because otherise the source output HTML will have dups.
//No other browser generates a graph. Leave it to IE to break a fundamental DOM rule. So, we check the parent if we can
//If we can't, nothing more we can do other than walk it.
if(!checkParent || node.parentNode == dom){
out += dijit._editor.getNodeHtml(node);
}
}
return out; // String
};
return dijit._editor;
});
},
'dijit/_editor/nls/commands':function(){
define({ root:
//begin v1.x content
({
'bold': 'Bold',
'copy': 'Copy',
'cut': 'Cut',
'delete': 'Delete',
'indent': 'Indent',
'insertHorizontalRule': 'Horizontal Rule',
'insertOrderedList': 'Numbered List',
'insertUnorderedList': 'Bullet List',
'italic': 'Italic',
'justifyCenter': 'Align Center',
'justifyFull': 'Justify',
'justifyLeft': 'Align Left',
'justifyRight': 'Align Right',
'outdent': 'Outdent',
'paste': 'Paste',
'redo': 'Redo',
'removeFormat': 'Remove Format',
'selectAll': 'Select All',
'strikethrough': 'Strikethrough',
'subscript': 'Subscript',
'superscript': 'Superscript',
'underline': 'Underline',
'undo': 'Undo',
'unlink': 'Remove Link',
'createLink': 'Create Link',
'toggleDir': 'Toggle Direction',
'insertImage': 'Insert Image',
'insertTable': 'Insert/Edit Table',
'toggleTableBorder': 'Toggle Table Border',
'deleteTable': 'Delete Table',
'tableProp': 'Table Property',
'htmlToggle': 'HTML Source',
'foreColor': 'Foreground Color',
'hiliteColor': 'Background Color',
'plainFormatBlock': 'Paragraph Style',
'formatBlock': 'Paragraph Style',
'fontSize': 'Font Size',
'fontName': 'Font Name',
'tabIndent': 'Tab Indent',
"fullScreen": "Toggle Full Screen",
"viewSource": "View HTML Source",
"print": "Print",
"newPage": "New Page",
/* Error messages */
'systemShortcut': 'The "${0}" action is only available in your browser using a keyboard shortcut. Use ${1}.',
'ctrlKey':'ctrl+${0}',
'appleKey':'\u2318${0}' // "command" or open-apple key on Macintosh
})
//end v1.x content
,
"zh": true,
"zh-tw": true,
"tr": true,
"th": true,
"sv": true,
"sl": true,
"sk": true,
"ru": true,
"ro": true,
"pt": true,
"pt-pt": true,
"pl": true,
"nl": true,
"nb": true,
"ko": true,
"kk": true,
"ja": true,
"it": true,
"hu": true,
"hr": true,
"he": true,
"fr": true,
"fi": true,
"es": true,
"el": true,
"de": true,
"da": true,
"cs": true,
"ca": true,
"az": true,
"ar": true
});
},
'dijit/_HasDropDown':function(){
define([
"dojo/_base/declare", // declare
"dojo/_base/Deferred",
"dojo/_base/event", // event.stop
"dojo/dom", // dom.isDescendant
"dojo/dom-attr", // domAttr.set
"dojo/dom-class", // domClass.add domClass.contains domClass.remove
"dojo/dom-geometry", // domGeometry.marginBox domGeometry.position
"dojo/dom-style", // domStyle.set
"dojo/has",
"dojo/keys", // keys.DOWN_ARROW keys.ENTER keys.ESCAPE
"dojo/_base/lang", // lang.hitch lang.isFunction
"dojo/touch",
"dojo/_base/window", // win.doc
"dojo/window", // winUtils.getBox
"./registry", // registry.byNode()
"./focus",
"./popup",
"./_FocusMixin"
], function(declare, Deferred, event,dom, domAttr, domClass, domGeometry, domStyle, has, keys, lang, touch,
win, winUtils, registry, focus, popup, _FocusMixin){
/*=====
var _FocusMixin = dijit._FocusMixin;
=====*/
// module:
// dijit/_HasDropDown
// summary:
// Mixin for widgets that need drop down ability.
return declare("dijit._HasDropDown", _FocusMixin, {
// summary:
// Mixin for widgets that need drop down ability.
// _buttonNode: [protected] DomNode
// The button/icon/node to click to display the drop down.
// Can be set via a data-dojo-attach-point assignment.
// If missing, then either focusNode or domNode (if focusNode is also missing) will be used.
_buttonNode: null,
// _arrowWrapperNode: [protected] DomNode
// Will set CSS class dijitUpArrow, dijitDownArrow, dijitRightArrow etc. on this node depending
// on where the drop down is set to be positioned.
// Can be set via a data-dojo-attach-point assignment.
// If missing, then _buttonNode will be used.
_arrowWrapperNode: null,
// _popupStateNode: [protected] DomNode
// The node to set the popupActive class on.
// Can be set via a data-dojo-attach-point assignment.
// If missing, then focusNode or _buttonNode (if focusNode is missing) will be used.
_popupStateNode: null,
// _aroundNode: [protected] DomNode
// The node to display the popup around.
// Can be set via a data-dojo-attach-point assignment.
// If missing, then domNode will be used.
_aroundNode: null,
// dropDown: [protected] Widget
// The widget to display as a popup. This widget *must* be
// defined before the startup function is called.
dropDown: null,
// autoWidth: [protected] Boolean
// Set to true to make the drop down at least as wide as this
// widget. Set to false if the drop down should just be its
// default width
autoWidth: true,
// forceWidth: [protected] Boolean
// Set to true to make the drop down exactly as wide as this
// widget. Overrides autoWidth.
forceWidth: false,
// maxHeight: [protected] Integer
// The max height for our dropdown.
// Any dropdown taller than this will have scrollbars.
// Set to 0 for no max height, or -1 to limit height to available space in viewport
maxHeight: 0,
// dropDownPosition: [const] String[]
// This variable controls the position of the drop down.
// It's an array of strings with the following values:
//
// * before: places drop down to the left of the target node/widget, or to the right in
// the case of RTL scripts like Hebrew and Arabic
// * after: places drop down to the right of the target node/widget, or to the left in
// the case of RTL scripts like Hebrew and Arabic
// * above: drop down goes above target node
// * below: drop down goes below target node
//
// The list is positions is tried, in order, until a position is found where the drop down fits
// within the viewport.
//
dropDownPosition: ["below","above"],
// _stopClickEvents: Boolean
// When set to false, the click events will not be stopped, in
// case you want to use them in your subwidget
_stopClickEvents: true,
_onDropDownMouseDown: function(/*Event*/ e){
// summary:
// Callback when the user mousedown's on the arrow icon
if(this.disabled || this.readOnly){ return; }
// Prevent default to stop things like text selection, but don't stop propogation, so that:
// 1. TimeTextBox etc. can focusthe <input> on mousedown
// 2. dropDownButtonActive class applied by _CssStateMixin (on button depress)
// 3. user defined onMouseDown handler fires
e.preventDefault();
this._docHandler = this.connect(win.doc, touch.release, "_onDropDownMouseUp");
this.toggleDropDown();
},
_onDropDownMouseUp: function(/*Event?*/ e){
// summary:
// Callback when the user lifts their mouse after mouse down on the arrow icon.
// If the drop down is a simple menu and the mouse is over the menu, we execute it, otherwise, we focus our
// drop down widget. If the event is missing, then we are not
// a mouseup event.
//
// This is useful for the common mouse movement pattern
// with native browser <select> nodes:
// 1. mouse down on the select node (probably on the arrow)
// 2. move mouse to a menu item while holding down the mouse button
// 3. mouse up. this selects the menu item as though the user had clicked it.
if(e && this._docHandler){
this.disconnect(this._docHandler);
}
var dropDown = this.dropDown, overMenu = false;
if(e && this._opened){
// This code deals with the corner-case when the drop down covers the original widget,
// because it's so large. In that case mouse-up shouldn't select a value from the menu.
// Find out if our target is somewhere in our dropdown widget,
// but not over our _buttonNode (the clickable node)
var c = domGeometry.position(this._buttonNode, true);
if(!(e.pageX >= c.x && e.pageX <= c.x + c.w) ||
!(e.pageY >= c.y && e.pageY <= c.y + c.h)){
var t = e.target;
while(t && !overMenu){
if(domClass.contains(t, "dijitPopup")){
overMenu = true;
}else{
t = t.parentNode;
}
}
if(overMenu){
t = e.target;
if(dropDown.onItemClick){
var menuItem;
while(t && !(menuItem = registry.byNode(t))){
t = t.parentNode;
}
if(menuItem && menuItem.onClick && menuItem.getParent){
menuItem.getParent().onItemClick(menuItem, e);
}
}
return;
}
}
}
if(this._opened){
if(dropDown.focus && dropDown.autoFocus !== false){
// Focus the dropdown widget - do it on a delay so that we
// don't steal our own focus.
window.setTimeout(lang.hitch(dropDown, "focus"), 1);
}
}else{
// The drop down arrow icon probably can't receive focus, but widget itself should get focus.
// setTimeout() needed to make it work on IE (test DateTextBox)
setTimeout(lang.hitch(this, "focus"), 0);
}
if(has("ios")){
this._justGotMouseUp = true;
setTimeout(lang.hitch(this, function(){
this._justGotMouseUp = false;
}), 0);
}
},
_onDropDownClick: function(/*Event*/ e){
if(has("ios") && !this._justGotMouseUp){
// This branch fires on iPhone for ComboBox, because the button node is an <input> and doesn't
// generate touchstart/touchend events. Pretend we just got a mouse down / mouse up.
// The if(has("ios") is necessary since IE and desktop safari get spurious onclick events
// when there are nested tables (specifically, clicking on a table that holds a dijit.form.Select,
// but not on the Select itself, causes an onclick event on the Select)
this._onDropDownMouseDown(e);
this._onDropDownMouseUp(e);
}
// The drop down was already opened on mousedown/keydown; just need to call stopEvent().
if(this._stopClickEvents){
event.stop(e);
}
},
buildRendering: function(){
this.inherited(arguments);
this._buttonNode = this._buttonNode || this.focusNode || this.domNode;
this._popupStateNode = this._popupStateNode || this.focusNode || this._buttonNode;
// Add a class to the "dijitDownArrowButton" type class to _buttonNode so theme can set direction of arrow
// based on where drop down will normally appear
var defaultPos = {
"after" : this.isLeftToRight() ? "Right" : "Left",
"before" : this.isLeftToRight() ? "Left" : "Right",
"above" : "Up",
"below" : "Down",
"left" : "Left",
"right" : "Right"
}[this.dropDownPosition[0]] || this.dropDownPosition[0] || "Down";
domClass.add(this._arrowWrapperNode || this._buttonNode, "dijit" + defaultPos + "ArrowButton");
},
postCreate: function(){
// summary:
// set up nodes and connect our mouse and keypress events
this.inherited(arguments);
this.connect(this._buttonNode, touch.press, "_onDropDownMouseDown");
this.connect(this._buttonNode, "onclick", "_onDropDownClick");
this.connect(this.focusNode, "onkeypress", "_onKey");
this.connect(this.focusNode, "onkeyup", "_onKeyUp");
},
destroy: function(){
if(this.dropDown){
// Destroy the drop down, unless it's already been destroyed. This can happen because
// the drop down is a direct child of <body> even though it's logically my child.
if(!this.dropDown._destroyed){
this.dropDown.destroyRecursive();
}
delete this.dropDown;
}
this.inherited(arguments);
},
_onKey: function(/*Event*/ e){
// summary:
// Callback when the user presses a key while focused on the button node
if(this.disabled || this.readOnly){ return; }
var d = this.dropDown, target = e.target;
if(d && this._opened && d.handleKey){
if(d.handleKey(e) === false){
/* false return code means that the drop down handled the key */
event.stop(e);
return;
}
}
if(d && this._opened && e.charOrCode == keys.ESCAPE){
this.closeDropDown();
event.stop(e);
}else if(!this._opened &&
(e.charOrCode == keys.DOWN_ARROW ||
( (e.charOrCode == keys.ENTER || e.charOrCode == " ") &&
//ignore enter and space if the event is for a text input
((target.tagName || "").toLowerCase() !== 'input' ||
(target.type && target.type.toLowerCase() !== 'text'))))){
// Toggle the drop down, but wait until keyup so that the drop down doesn't
// get a stray keyup event, or in the case of key-repeat (because user held
// down key for too long), stray keydown events
this._toggleOnKeyUp = true;
event.stop(e);
}
},
_onKeyUp: function(){
if(this._toggleOnKeyUp){
delete this._toggleOnKeyUp;
this.toggleDropDown();
var d = this.dropDown; // drop down may not exist until toggleDropDown() call
if(d && d.focus){
setTimeout(lang.hitch(d, "focus"), 1);
}
}
},
_onBlur: function(){
// summary:
// Called magically when focus has shifted away from this widget and it's dropdown
// Don't focus on button if the user has explicitly focused on something else (happens
// when user clicks another control causing the current popup to close)..
// But if focus is inside of the drop down then reset focus to me, because IE doesn't like
// it when you display:none a node with focus.
var focusMe = focus.curNode && this.dropDown && dom.isDescendant(focus.curNode, this.dropDown.domNode);
this.closeDropDown(focusMe);
this.inherited(arguments);
},
isLoaded: function(){
// summary:
// Returns true if the dropdown exists and it's data is loaded. This can
// be overridden in order to force a call to loadDropDown().
// tags:
// protected
return true;
},
loadDropDown: function(/*Function*/ loadCallback){
// summary:
// Creates the drop down if it doesn't exist, loads the data
// if there's an href and it hasn't been loaded yet, and then calls
// the given callback.
// tags:
// protected
// TODO: for 2.0, change API to return a Deferred, instead of calling loadCallback?
loadCallback();
},
loadAndOpenDropDown: function(){
// summary:
// Creates the drop down if it doesn't exist, loads the data
// if there's an href and it hasn't been loaded yet, and
// then opens the drop down. This is basically a callback when the
// user presses the down arrow button to open the drop down.
// returns: Deferred
// Deferred for the drop down widget that
// fires when drop down is created and loaded
// tags:
// protected
var d = new Deferred(),
afterLoad = lang.hitch(this, function(){
this.openDropDown();
d.resolve(this.dropDown);
});
if(!this.isLoaded()){
this.loadDropDown(afterLoad);
}else{
afterLoad();
}
return d;
},
toggleDropDown: function(){
// summary:
// Callback when the user presses the down arrow button or presses
// the down arrow key to open/close the drop down.
// Toggle the drop-down widget; if it is up, close it, if not, open it
// tags:
// protected
if(this.disabled || this.readOnly){ return; }
if(!this._opened){
this.loadAndOpenDropDown();
}else{
this.closeDropDown();
}
},
openDropDown: function(){
// summary:
// Opens the dropdown for this widget. To be called only when this.dropDown
// has been created and is ready to display (ie, it's data is loaded).
// returns:
// return value of dijit.popup.open()
// tags:
// protected
var dropDown = this.dropDown,
ddNode = dropDown.domNode,
aroundNode = this._aroundNode || this.domNode,
self = this;
// Prepare our popup's height and honor maxHeight if it exists.
// TODO: isn't maxHeight dependent on the return value from dijit.popup.open(),
// ie, dependent on how much space is available (BK)
if(!this._preparedNode){
this._preparedNode = true;
// Check if we have explicitly set width and height on the dropdown widget dom node
if(ddNode.style.width){
this._explicitDDWidth = true;
}
if(ddNode.style.height){
this._explicitDDHeight = true;
}
}
// Code for resizing dropdown (height limitation, or increasing width to match my width)
if(this.maxHeight || this.forceWidth || this.autoWidth){
var myStyle = {
display: "",
visibility: "hidden"
};
if(!this._explicitDDWidth){
myStyle.width = "";
}
if(!this._explicitDDHeight){
myStyle.height = "";
}
domStyle.set(ddNode, myStyle);
// Figure out maximum height allowed (if there is a height restriction)
var maxHeight = this.maxHeight;
if(maxHeight == -1){
// limit height to space available in viewport either above or below my domNode
// (whichever side has more room)
var viewport = winUtils.getBox(),
position = domGeometry.position(aroundNode, false);
maxHeight = Math.floor(Math.max(position.y, viewport.h - (position.y + position.h)));
}
// Attach dropDown to DOM and make make visibility:hidden rather than display:none
// so we call startup() and also get the size
popup.moveOffScreen(dropDown);
if(dropDown.startup && !dropDown._started){
dropDown.startup(); // this has to be done after being added to the DOM
}
// Get size of drop down, and determine if vertical scroll bar needed
var mb = domGeometry.getMarginSize(ddNode);
var overHeight = (maxHeight && mb.h > maxHeight);
domStyle.set(ddNode, {
overflowX: "hidden",
overflowY: overHeight ? "auto" : "hidden"
});
if(overHeight){
mb.h = maxHeight;
if("w" in mb){
mb.w += 16; // room for vertical scrollbar
}
}else{
delete mb.h;
}
// Adjust dropdown width to match or be larger than my width
if(this.forceWidth){
mb.w = aroundNode.offsetWidth;
}else if(this.autoWidth){
mb.w = Math.max(mb.w, aroundNode.offsetWidth);
}else{
delete mb.w;
}
// And finally, resize the dropdown to calculated height and width
if(lang.isFunction(dropDown.resize)){
dropDown.resize(mb);
}else{
domGeometry.setMarginBox(ddNode, mb);
}
}
var retVal = popup.open({
parent: this,
popup: dropDown,
around: aroundNode,
orient: this.dropDownPosition,
onExecute: function(){
self.closeDropDown(true);
},
onCancel: function(){
self.closeDropDown(true);
},
onClose: function(){
domAttr.set(self._popupStateNode, "popupActive", false);
domClass.remove(self._popupStateNode, "dijitHasDropDownOpen");
self._opened = false;
}
});
domAttr.set(this._popupStateNode, "popupActive", "true");
domClass.add(self._popupStateNode, "dijitHasDropDownOpen");
this._opened=true;
// TODO: set this.checked and call setStateClass(), to affect button look while drop down is shown
return retVal;
},
closeDropDown: function(/*Boolean*/ focus){
// summary:
// Closes the drop down on this widget
// focus:
// If true, refocuses the button widget
// tags:
// protected
if(this._opened){
if(focus){ this.focus(); }
popup.close(this.dropDown);
this._opened = false;
}
}
});
});
},
'dijit/tree/TreeStoreModel':function(){
define([
"dojo/_base/array", // array.filter array.forEach array.indexOf array.some
"dojo/aspect", // aspect.after
"dojo/_base/declare", // declare
"dojo/_base/json", // json.stringify
"dojo/_base/lang" // lang.hitch
], function(array, aspect, declare, json, lang){
// module:
// dijit/tree/TreeStoreModel
// summary:
// Implements dijit.Tree.model connecting to a dojo.data store with a single
// root item.
return declare("dijit.tree.TreeStoreModel", null, {
// summary:
// Implements dijit.Tree.model connecting to a dojo.data store with a single
// root item. Any methods passed into the constructor will override
// the ones defined here.
// store: dojo.data.Store
// Underlying store
store: null,
// childrenAttrs: String[]
// One or more attribute names (attributes in the dojo.data item) that specify that item's children
childrenAttrs: ["children"],
// newItemIdAttr: String
// Name of attribute in the Object passed to newItem() that specifies the id.
//
// If newItemIdAttr is set then it's used when newItem() is called to see if an
// item with the same id already exists, and if so just links to the old item
// (so that the old item ends up with two parents).
//
// Setting this to null or "" will make every drop create a new item.
newItemIdAttr: "id",
// labelAttr: String
// If specified, get label for tree node from this attribute, rather
// than by calling store.getLabel()
labelAttr: "",
// root: [readonly] dojo.data.Item
// Pointer to the root item (read only, not a parameter)
root: null,
// query: anything
// Specifies datastore query to return the root item for the tree.
// Must only return a single item. Alternately can just pass in pointer
// to root item.
// example:
// | {id:'ROOT'}
query: null,
// deferItemLoadingUntilExpand: Boolean
// Setting this to true will cause the TreeStoreModel to defer calling loadItem on nodes
// until they are expanded. This allows for lazying loading where only one
// loadItem (and generally one network call, consequently) per expansion
// (rather than one for each child).
// This relies on partial loading of the children items; each children item of a
// fully loaded item should contain the label and info about having children.
deferItemLoadingUntilExpand: false,
constructor: function(/* Object */ args){
// summary:
// Passed the arguments listed above (store, etc)
// tags:
// private
lang.mixin(this, args);
this.connects = [];
var store = this.store;
if(!store.getFeatures()['dojo.data.api.Identity']){
throw new Error("dijit.Tree: store must support dojo.data.Identity");
}
// if the store supports Notification, subscribe to the notification events
if(store.getFeatures()['dojo.data.api.Notification']){
this.connects = this.connects.concat([
aspect.after(store, "onNew", lang.hitch(this, "onNewItem"), true),
aspect.after(store, "onDelete", lang.hitch(this, "onDeleteItem"), true),
aspect.after(store, "onSet", lang.hitch(this, "onSetItem"), true)
]);
}
},
destroy: function(){
var h;
while(h = this.connects.pop()){ h.remove(); }
// TODO: should cancel any in-progress processing of getRoot(), getChildren()
},
// =======================================================================
// Methods for traversing hierarchy
getRoot: function(onItem, onError){
// summary:
// Calls onItem with the root item for the tree, possibly a fabricated item.
// Calls onError on error.
if(this.root){
onItem(this.root);
}else{
this.store.fetch({
query: this.query,
onComplete: lang.hitch(this, function(items){
if(items.length != 1){
throw new Error(this.declaredClass + ": query " + json.stringify(this.query) + " returned " + items.length +
" items, but must return exactly one item");
}
this.root = items[0];
onItem(this.root);
}),
onError: onError
});
}
},
mayHaveChildren: function(/*dojo.data.Item*/ item){
// summary:
// Tells if an item has or may have children. Implementing logic here
// avoids showing +/- expando icon for nodes that we know don't have children.
// (For efficiency reasons we may not want to check if an element actually
// has children until user clicks the expando node)
return array.some(this.childrenAttrs, function(attr){
return this.store.hasAttribute(item, attr);
}, this);
},
getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete, /*function*/ onError){
// summary:
// Calls onComplete() with array of child items of given parent item, all loaded.
var store = this.store;
if(!store.isItemLoaded(parentItem)){
// The parent is not loaded yet, we must be in deferItemLoadingUntilExpand
// mode, so we will load it and just return the children (without loading each
// child item)
var getChildren = lang.hitch(this, arguments.callee);
store.loadItem({
item: parentItem,
onItem: function(parentItem){
getChildren(parentItem, onComplete, onError);
},
onError: onError
});
return;
}
// get children of specified item
var childItems = [];
for(var i=0; i<this.childrenAttrs.length; i++){
var vals = store.getValues(parentItem, this.childrenAttrs[i]);
childItems = childItems.concat(vals);
}
// count how many items need to be loaded
var _waitCount = 0;
if(!this.deferItemLoadingUntilExpand){
array.forEach(childItems, function(item){ if(!store.isItemLoaded(item)){ _waitCount++; } });
}
if(_waitCount == 0){
// all items are already loaded (or we aren't loading them). proceed...
onComplete(childItems);
}else{
// still waiting for some or all of the items to load
array.forEach(childItems, function(item, idx){
if(!store.isItemLoaded(item)){
store.loadItem({
item: item,
onItem: function(item){
childItems[idx] = item;
if(--_waitCount == 0){
// all nodes have been loaded, send them to the tree
onComplete(childItems);
}
},
onError: onError
});
}
});
}
},
// =======================================================================
// Inspecting items
isItem: function(/* anything */ something){
return this.store.isItem(something); // Boolean
},
fetchItemByIdentity: function(/* object */ keywordArgs){
this.store.fetchItemByIdentity(keywordArgs);
},
getIdentity: function(/* item */ item){
return this.store.getIdentity(item); // Object
},
getLabel: function(/*dojo.data.Item*/ item){
// summary:
// Get the label for an item
if(this.labelAttr){
return this.store.getValue(item,this.labelAttr); // String
}else{
return this.store.getLabel(item); // String
}
},
// =======================================================================
// Write interface
newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
// summary:
// Creates a new item. See `dojo.data.api.Write` for details on args.
// Used in drag & drop when item from external source dropped onto tree.
// description:
// Developers will need to override this method if new items get added
// to parents with multiple children attributes, in order to define which
// children attribute points to the new item.
var pInfo = {parent: parent, attribute: this.childrenAttrs[0]}, LnewItem;
if(this.newItemIdAttr && args[this.newItemIdAttr]){
// Maybe there's already a corresponding item in the store; if so, reuse it.
this.fetchItemByIdentity({identity: args[this.newItemIdAttr], scope: this, onItem: function(item){
if(item){
// There's already a matching item in store, use it
this.pasteItem(item, null, parent, true, insertIndex);
}else{
// Create new item in the tree, based on the drag source.
LnewItem=this.store.newItem(args, pInfo);
if(LnewItem && (insertIndex!=undefined)){
// Move new item to desired position
this.pasteItem(LnewItem, parent, parent, false, insertIndex);
}
}
}});
}else{
// [as far as we know] there is no id so we must assume this is a new item
LnewItem=this.store.newItem(args, pInfo);
if(LnewItem && (insertIndex!=undefined)){
// Move new item to desired position
this.pasteItem(LnewItem, parent, parent, false, insertIndex);
}
}
},
pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
// summary:
// Move or copy an item from one parent item to another.
// Used in drag & drop
var store = this.store,
parentAttr = this.childrenAttrs[0]; // name of "children" attr in parent item
// remove child from source item, and record the attribute that child occurred in
if(oldParentItem){
array.forEach(this.childrenAttrs, function(attr){
if(store.containsValue(oldParentItem, attr, childItem)){
if(!bCopy){
var values = array.filter(store.getValues(oldParentItem, attr), function(x){
return x != childItem;
});
store.setValues(oldParentItem, attr, values);
}
parentAttr = attr;
}
});
}
// modify target item's children attribute to include this item
if(newParentItem){
if(typeof insertIndex == "number"){
// call slice() to avoid modifying the original array, confusing the data store
var childItems = store.getValues(newParentItem, parentAttr).slice();
childItems.splice(insertIndex, 0, childItem);
store.setValues(newParentItem, parentAttr, childItems);
}else{
store.setValues(newParentItem, parentAttr,
store.getValues(newParentItem, parentAttr).concat(childItem));
}
}
},
// =======================================================================
// Callbacks
onChange: function(/*dojo.data.Item*/ /*===== item =====*/){
// summary:
// Callback whenever an item has changed, so that Tree
// can update the label, icon, etc. Note that changes
// to an item's children or parent(s) will trigger an
// onChildrenChange() so you can ignore those changes here.
// tags:
// callback
},
onChildrenChange: function(/*===== parent, newChildrenList =====*/){
// summary:
// Callback to do notifications about new, updated, or deleted items.
// parent: dojo.data.Item
// newChildrenList: dojo.data.Item[]
// tags:
// callback
},
onDelete: function(/*dojo.data.Item*/ /*===== item =====*/){
// summary:
// Callback when an item has been deleted.
// description:
// Note that there will also be an onChildrenChange() callback for the parent
// of this item.
// tags:
// callback
},
// =======================================================================
// Events from data store
onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
// summary:
// Handler for when new items appear in the store, either from a drop operation
// or some other way. Updates the tree view (if necessary).
// description:
// If the new item is a child of an existing item,
// calls onChildrenChange() with the new list of children
// for that existing item.
//
// tags:
// extension
// We only care about the new item if it has a parent that corresponds to a TreeNode
// we are currently displaying
if(!parentInfo){
return;
}
// Call onChildrenChange() on parent (ie, existing) item with new list of children
// In the common case, the new list of children is simply parentInfo.newValue or
// [ parentInfo.newValue ], although if items in the store has multiple
// child attributes (see `childrenAttr`), then it's a superset of parentInfo.newValue,
// so call getChildren() to be sure to get right answer.
this.getChildren(parentInfo.item, lang.hitch(this, function(children){
this.onChildrenChange(parentInfo.item, children);
}));
},
onDeleteItem: function(/*Object*/ item){
// summary:
// Handler for delete notifications from underlying store
this.onDelete(item);
},
onSetItem: function(item, attribute /*===== , oldValue, newValue =====*/){
// summary:
// Updates the tree view according to changes in the data store.
// description:
// Handles updates to an item's children by calling onChildrenChange(), and
// other updates to an item by calling onChange().
//
// See `onNewItem` for more details on handling updates to an item's children.
// item: Item
// attribute: attribute-name-string
// oldValue: object | array
// newValue: object | array
// tags:
// extension
if(array.indexOf(this.childrenAttrs, attribute) != -1){
// item's children list changed
this.getChildren(item, lang.hitch(this, function(children){
// See comments in onNewItem() about calling getChildren()
this.onChildrenChange(item, children);
}));
}else{
// item's label/icon/etc. changed.
this.onChange(item);
}
}
});
});
},
'dijit/_editor/plugins/EnterKeyHandling':function(){
define([
"dojo/_base/declare", // declare
"dojo/dom-construct", // domConstruct.destroy domConstruct.place
"dojo/_base/event", // event.stop
"dojo/keys", // keys.ENTER
"dojo/_base/lang",
"dojo/_base/sniff", // has("ie") has("mozilla") has("webkit")
"dojo/_base/window", // win.global win.withGlobal
"dojo/window", // winUtils.scrollIntoView
"../_Plugin",
"../RichText",
"../range",
"../selection"
], function(declare, domConstruct, event, keys, lang, has, win, winUtils, _Plugin, RichText, rangeapi, selectionapi){
/*=====
var _Plugin = dijit._editor._Plugin;
=====*/
// module:
// dijit/_editor/plugins/EnterKeyHandling
// summary:
// This plugin tries to make all browsers behave consistently with regard to
// how ENTER behaves in the editor window. It traps the ENTER key and alters
// the way DOM is constructed in certain cases to try to commonize the generated
// DOM and behaviors across browsers.
return declare("dijit._editor.plugins.EnterKeyHandling", _Plugin, {
// summary:
// This plugin tries to make all browsers behave consistently with regard to
// how ENTER behaves in the editor window. It traps the ENTER key and alters
// the way DOM is constructed in certain cases to try to commonize the generated
// DOM and behaviors across browsers.
//
// description:
// This plugin has three modes:
//
// * blockNodeForEnter=BR
// * blockNodeForEnter=DIV
// * blockNodeForEnter=P
//
// In blockNodeForEnter=P, the ENTER key starts a new
// paragraph, and shift-ENTER starts a new line in the current paragraph.
// For example, the input:
//
// | first paragraph <shift-ENTER>
// | second line of first paragraph <ENTER>
// | second paragraph
//
// will generate:
//
// | <p>
// | first paragraph
// | <br/>
// | second line of first paragraph
// | </p>
// | <p>
// | second paragraph
// | </p>
//
// In BR and DIV mode, the ENTER key conceptually goes to a new line in the
// current paragraph, and users conceptually create a new paragraph by pressing ENTER twice.
// For example, if the user enters text into an editor like this:
//
// | one <ENTER>
// | two <ENTER>
// | three <ENTER>
// | <ENTER>
// | four <ENTER>
// | five <ENTER>
// | six <ENTER>
//
// It will appear on the screen as two 'paragraphs' of three lines each. Markupwise, this generates:
//
// BR:
// | one<br/>
// | two<br/>
// | three<br/>
// | <br/>
// | four<br/>
// | five<br/>
// | six<br/>
//
// DIV:
// | <div>one</div>
// | <div>two</div>
// | <div>three</div>
// | <div>&nbsp;</div>
// | <div>four</div>
// | <div>five</div>
// | <div>six</div>
// blockNodeForEnter: String
// This property decides the behavior of Enter key. It can be either P,
// DIV, BR, or empty (which means disable this feature). Anything else
// will trigger errors. The default is 'BR'
//
// See class description for more details.
blockNodeForEnter: 'BR',
constructor: function(args){
if(args){
if("blockNodeForEnter" in args){
args.blockNodeForEnter = args.blockNodeForEnter.toUpperCase();
}
lang.mixin(this,args);
}
},
setEditor: function(editor){
// Overrides _Plugin.setEditor().
if(this.editor === editor){ return; }
this.editor = editor;
if(this.blockNodeForEnter == 'BR'){
// While Moz has a mode tht mostly works, it's still a little different,
// So, try to just have a common mode and be consistent. Which means
// we need to enable customUndo, if not already enabled.
this.editor.customUndo = true;
editor.onLoadDeferred.addCallback(lang.hitch(this,function(d){
this.connect(editor.document, "onkeypress", function(e){
if(e.charOrCode == keys.ENTER){
// Just do it manually. The handleEnterKey has a shift mode that
// Always acts like <br>, so just use it.
var ne = lang.mixin({},e);
ne.shiftKey = true;
if(!this.handleEnterKey(ne)){
event.stop(e);
}
}
});
return d;
}));
}else if(this.blockNodeForEnter){
// add enter key handler
// FIXME: need to port to the new event code!!
var h = lang.hitch(this,this.handleEnterKey);
editor.addKeyHandler(13, 0, 0, h); //enter
editor.addKeyHandler(13, 0, 1, h); //shift+enter
this.connect(this.editor,'onKeyPressed','onKeyPressed');
}
},
onKeyPressed: function(){
// summary:
// Handler for keypress events.
// tags:
// private
if(this._checkListLater){
if(win.withGlobal(this.editor.window, 'isCollapsed', dijit)){
var liparent=win.withGlobal(this.editor.window, 'getAncestorElement', selection, ['LI']);
if(!liparent){
// circulate the undo detection code by calling RichText::execCommand directly
RichText.prototype.execCommand.call(this.editor, 'formatblock',this.blockNodeForEnter);
// set the innerHTML of the new block node
var block = win.withGlobal(this.editor.window, 'getAncestorElement', selection, [this.blockNodeForEnter]);
if(block){
block.innerHTML=this.bogusHtmlContent;
if(has("ie")){
// move to the start by moving backwards one char
var r = this.editor.document.selection.createRange();
r.move('character',-1);
r.select();
}
}else{
console.error('onKeyPressed: Cannot find the new block node'); // FIXME
}
}else{
if(has("mozilla")){
if(liparent.parentNode.parentNode.nodeName == 'LI'){
liparent=liparent.parentNode.parentNode;
}
}
var fc=liparent.firstChild;
if(fc && fc.nodeType == 1 && (fc.nodeName == 'UL' || fc.nodeName == 'OL')){
liparent.insertBefore(fc.ownerDocument.createTextNode('\xA0'),fc);
var newrange = rangeapi.create(this.editor.window);
newrange.setStart(liparent.firstChild,0);
var selection = rangeapi.getSelection(this.editor.window, true);
selection.removeAllRanges();
selection.addRange(newrange);
}
}
}
this._checkListLater = false;
}
if(this._pressedEnterInBlock){
// the new created is the original current P, so we have previousSibling below
if(this._pressedEnterInBlock.previousSibling){
this.removeTrailingBr(this._pressedEnterInBlock.previousSibling);
}
delete this._pressedEnterInBlock;
}
},
// bogusHtmlContent: [private] String
// HTML to stick into a new empty block
bogusHtmlContent: '&#160;', // &nbsp;
// blockNodes: [private] Regex
// Regex for testing if a given tag is a block level (display:block) tag
blockNodes: /^(?:P|H1|H2|H3|H4|H5|H6|LI)$/,
handleEnterKey: function(e){
// summary:
// Handler for enter key events when blockNodeForEnter is DIV or P.
// description:
// Manually handle enter key event to make the behavior consistent across
// all supported browsers. See class description for details.
// tags:
// private
var selection, range, newrange, startNode, endNode, brNode, doc=this.editor.document,br,rs,txt;
if(e.shiftKey){ // shift+enter always generates <br>
var parent = win.withGlobal(this.editor.window, "getParentElement", selectionapi);
var header = rangeapi.getAncestor(parent,this.blockNodes);
if(header){
if(header.tagName == 'LI'){
return true; // let browser handle
}
selection = rangeapi.getSelection(this.editor.window);
range = selection.getRangeAt(0);
if(!range.collapsed){
range.deleteContents();
selection = rangeapi.getSelection(this.editor.window);
range = selection.getRangeAt(0);
}
if(rangeapi.atBeginningOfContainer(header, range.startContainer, range.startOffset)){
br=doc.createElement('br');
newrange = rangeapi.create(this.editor.window);
header.insertBefore(br,header.firstChild);
newrange.setStartAfter(br);
selection.removeAllRanges();
selection.addRange(newrange);
}else if(rangeapi.atEndOfContainer(header, range.startContainer, range.startOffset)){
newrange = rangeapi.create(this.editor.window);
br=doc.createElement('br');
header.appendChild(br);
header.appendChild(doc.createTextNode('\xA0'));
newrange.setStart(header.lastChild,0);
selection.removeAllRanges();
selection.addRange(newrange);
}else{
rs = range.startContainer;
if(rs && rs.nodeType == 3){
// Text node, we have to split it.
txt = rs.nodeValue;
win.withGlobal(this.editor.window, function(){
startNode = doc.createTextNode(txt.substring(0, range.startOffset));
endNode = doc.createTextNode(txt.substring(range.startOffset));
brNode = doc.createElement("br");
if(endNode.nodeValue == "" && has("webkit")){
endNode = doc.createTextNode('\xA0')
}
domConstruct.place(startNode, rs, "after");
domConstruct.place(brNode, startNode, "after");
domConstruct.place(endNode, brNode, "after");
domConstruct.destroy(rs);
newrange = rangeapi.create();
newrange.setStart(endNode,0);
selection.removeAllRanges();
selection.addRange(newrange);
});
return false;
}
return true; // let browser handle
}
}else{
selection = rangeapi.getSelection(this.editor.window);
if(selection.rangeCount){
range = selection.getRangeAt(0);
if(range && range.startContainer){
if(!range.collapsed){
range.deleteContents();
selection = rangeapi.getSelection(this.editor.window);
range = selection.getRangeAt(0);
}
rs = range.startContainer;
if(rs && rs.nodeType == 3){
// Text node, we have to split it.
win.withGlobal(this.editor.window, lang.hitch(this, function(){
var endEmpty = false;
var offset = range.startOffset;
if(rs.length < offset){
//We are not splitting the right node, try to locate the correct one
ret = this._adjustNodeAndOffset(rs, offset);
rs = ret.node;
offset = ret.offset;
}
txt = rs.nodeValue;
startNode = doc.createTextNode(txt.substring(0, offset));
endNode = doc.createTextNode(txt.substring(offset));
brNode = doc.createElement("br");
if(!endNode.length){
endNode = doc.createTextNode('\xA0');
endEmpty = true;
}
if(startNode.length){
domConstruct.place(startNode, rs, "after");
}else{
startNode = rs;
}
domConstruct.place(brNode, startNode, "after");
domConstruct.place(endNode, brNode, "after");
domConstruct.destroy(rs);
newrange = rangeapi.create();
newrange.setStart(endNode,0);
newrange.setEnd(endNode, endNode.length);
selection.removeAllRanges();
selection.addRange(newrange);
if(endEmpty && !has("webkit")){
selectionapi.remove();
}else{
selectionapi.collapse(true);
}
}));
}else{
var targetNode;
if(range.startOffset >= 0){
targetNode = rs.childNodes[range.startOffset];
}
win.withGlobal(this.editor.window, lang.hitch(this, function(){
var brNode = doc.createElement("br");
var endNode = doc.createTextNode('\xA0');
if(!targetNode){
rs.appendChild(brNode);
rs.appendChild(endNode);
}else{
domConstruct.place(brNode, targetNode, "before");
domConstruct.place(endNode, brNode, "after");
}
newrange = rangeapi.create(win.global);
newrange.setStart(endNode,0);
newrange.setEnd(endNode, endNode.length);
selection.removeAllRanges();
selection.addRange(newrange);
selectionapi.collapse(true);
}));
}
}
}else{
// don't change this: do not call this.execCommand, as that may have other logic in subclass
RichText.prototype.execCommand.call(this.editor, 'inserthtml', '<br>');
}
}
return false;
}
var _letBrowserHandle = true;
// first remove selection
selection = rangeapi.getSelection(this.editor.window);
range = selection.getRangeAt(0);
if(!range.collapsed){
range.deleteContents();
selection = rangeapi.getSelection(this.editor.window);
range = selection.getRangeAt(0);
}
var block = rangeapi.getBlockAncestor(range.endContainer, null, this.editor.editNode);
var blockNode = block.blockNode;
// if this is under a LI or the parent of the blockNode is LI, just let browser to handle it
if((this._checkListLater = (blockNode && (blockNode.nodeName == 'LI' || blockNode.parentNode.nodeName == 'LI')))){
if(has("mozilla")){
// press enter in middle of P may leave a trailing <br/>, let's remove it later
this._pressedEnterInBlock = blockNode;
}
// if this li only contains spaces, set the content to empty so the browser will outdent this item
if(/^(\s|&nbsp;|&#160;|\xA0|<span\b[^>]*\bclass=['"]Apple-style-span['"][^>]*>(\s|&nbsp;|&#160;|\xA0)<\/span>)?(<br>)?$/.test(blockNode.innerHTML)){
// empty LI node
blockNode.innerHTML = '';
if(has("webkit")){ // WebKit tosses the range when innerHTML is reset
newrange = rangeapi.create(this.editor.window);
newrange.setStart(blockNode, 0);
selection.removeAllRanges();
selection.addRange(newrange);
}
this._checkListLater = false; // nothing to check since the browser handles outdent
}
return true;
}
// text node directly under body, let's wrap them in a node
if(!block.blockNode || block.blockNode===this.editor.editNode){
try{
RichText.prototype.execCommand.call(this.editor, 'formatblock',this.blockNodeForEnter);
}catch(e2){ /*squelch FF3 exception bug when editor content is a single BR*/ }
// get the newly created block node
// FIXME
block = {blockNode:win.withGlobal(this.editor.window, "getAncestorElement", selectionapi, [this.blockNodeForEnter]),
blockContainer: this.editor.editNode};
if(block.blockNode){
if(block.blockNode != this.editor.editNode &&
(!(block.blockNode.textContent || block.blockNode.innerHTML).replace(/^\s+|\s+$/g, "").length)){
this.removeTrailingBr(block.blockNode);
return false;
}
}else{ // we shouldn't be here if formatblock worked
block.blockNode = this.editor.editNode;
}
selection = rangeapi.getSelection(this.editor.window);
range = selection.getRangeAt(0);
}
var newblock = doc.createElement(this.blockNodeForEnter);
newblock.innerHTML=this.bogusHtmlContent;
this.removeTrailingBr(block.blockNode);
var endOffset = range.endOffset;
var node = range.endContainer;
if(node.length < endOffset){
//We are not checking the right node, try to locate the correct one
var ret = this._adjustNodeAndOffset(node, endOffset);
node = ret.node;
endOffset = ret.offset;
}
if(rangeapi.atEndOfContainer(block.blockNode, node, endOffset)){
if(block.blockNode === block.blockContainer){
block.blockNode.appendChild(newblock);
}else{
domConstruct.place(newblock, block.blockNode, "after");
}
_letBrowserHandle = false;
// lets move caret to the newly created block
newrange = rangeapi.create(this.editor.window);
newrange.setStart(newblock, 0);
selection.removeAllRanges();
selection.addRange(newrange);
if(this.editor.height){
winUtils.scrollIntoView(newblock);
}
}else if(rangeapi.atBeginningOfContainer(block.blockNode,
range.startContainer, range.startOffset)){
domConstruct.place(newblock, block.blockNode, block.blockNode === block.blockContainer ? "first" : "before");
if(newblock.nextSibling && this.editor.height){
// position input caret - mostly WebKit needs this
newrange = rangeapi.create(this.editor.window);
newrange.setStart(newblock.nextSibling, 0);
selection.removeAllRanges();
selection.addRange(newrange);
// browser does not scroll the caret position into view, do it manually
winUtils.scrollIntoView(newblock.nextSibling);
}
_letBrowserHandle = false;
}else{ //press enter in the middle of P/DIV/Whatever/
if(block.blockNode === block.blockContainer){
block.blockNode.appendChild(newblock);
}else{
domConstruct.place(newblock, block.blockNode, "after");
}
_letBrowserHandle = false;
// Clone any block level styles.
if(block.blockNode.style){
if(newblock.style){
if(block.blockNode.style.cssText){
newblock.style.cssText = block.blockNode.style.cssText;
}
}
}
// Okay, we probably have to split.
rs = range.startContainer;
var firstNodeMoved;
if(rs && rs.nodeType == 3){
// Text node, we have to split it.
var nodeToMove, tNode;
endOffset = range.endOffset;
if(rs.length < endOffset){
//We are not splitting the right node, try to locate the correct one
ret = this._adjustNodeAndOffset(rs, endOffset);
rs = ret.node;
endOffset = ret.offset;
}
txt = rs.nodeValue;
startNode = doc.createTextNode(txt.substring(0, endOffset));
endNode = doc.createTextNode(txt.substring(endOffset, txt.length));
// Place the split, then remove original nodes.
domConstruct.place(startNode, rs, "before");
domConstruct.place(endNode, rs, "after");
domConstruct.destroy(rs);
// Okay, we split the text. Now we need to see if we're
// parented to the block element we're splitting and if
// not, we have to split all the way up. Ugh.
var parentC = startNode.parentNode;
while(parentC !== block.blockNode){
var tg = parentC.tagName;
var newTg = doc.createElement(tg);
// Clone over any 'style' data.
if(parentC.style){
if(newTg.style){
if(parentC.style.cssText){
newTg.style.cssText = parentC.style.cssText;
}
}
}
// If font also need to clone over any font data.
if(parentC.tagName === "FONT"){
if(parentC.color){
newTg.color = parentC.color;
}
if(parentC.face){
newTg.face = parentC.face;
}
if(parentC.size){ // this check was necessary on IE
newTg.size = parentC.size;
}
}
nodeToMove = endNode;
while(nodeToMove){
tNode = nodeToMove.nextSibling;
newTg.appendChild(nodeToMove);
nodeToMove = tNode;
}
domConstruct.place(newTg, parentC, "after");
startNode = parentC;
endNode = newTg;
parentC = parentC.parentNode;
}
// Lastly, move the split out tags to the new block.
// as they should now be split properly.
nodeToMove = endNode;
if(nodeToMove.nodeType == 1 || (nodeToMove.nodeType == 3 && nodeToMove.nodeValue)){
// Non-blank text and non-text nodes need to clear out that blank space
// before moving the contents.
newblock.innerHTML = "";
}
firstNodeMoved = nodeToMove;
while(nodeToMove){
tNode = nodeToMove.nextSibling;
newblock.appendChild(nodeToMove);
nodeToMove = tNode;
}
}
//lets move caret to the newly created block
newrange = rangeapi.create(this.editor.window);
var nodeForCursor;
var innerMostFirstNodeMoved = firstNodeMoved;
if(this.blockNodeForEnter !== 'BR'){
while(innerMostFirstNodeMoved){
nodeForCursor = innerMostFirstNodeMoved;
tNode = innerMostFirstNodeMoved.firstChild;
innerMostFirstNodeMoved = tNode;
}
if(nodeForCursor && nodeForCursor.parentNode){
newblock = nodeForCursor.parentNode;
newrange.setStart(newblock, 0);
selection.removeAllRanges();
selection.addRange(newrange);
if(this.editor.height){
winUtils.scrollIntoView(newblock);
}
if(has("mozilla")){
// press enter in middle of P may leave a trailing <br/>, let's remove it later
this._pressedEnterInBlock = block.blockNode;
}
}else{
_letBrowserHandle = true;
}
}else{
newrange.setStart(newblock, 0);
selection.removeAllRanges();
selection.addRange(newrange);
if(this.editor.height){
winUtils.scrollIntoView(newblock);
}
if(has("mozilla")){
// press enter in middle of P may leave a trailing <br/>, let's remove it later
this._pressedEnterInBlock = block.blockNode;
}
}
}
return _letBrowserHandle;
},
_adjustNodeAndOffset: function(/*DomNode*/node, /*Int*/offset){
// summary:
// In the case there are multiple text nodes in a row the offset may not be within the node. If the offset is larger than the node length, it will attempt to find
// the next text sibling until it locates the text node in which the offset refers to
// node:
// The node to check.
// offset:
// The position to find within the text node
// tags:
// private.
while(node.length < offset && node.nextSibling && node.nextSibling.nodeType==3){
//Adjust the offset and node in the case of multiple text nodes in a row
offset = offset - node.length;
node = node.nextSibling;
}
return {"node": node, "offset": offset};
},
removeTrailingBr: function(container){
// summary:
// If last child of container is a <br>, then remove it.
// tags:
// private
var para = /P|DIV|LI/i.test(container.tagName) ?
container : selectionapi.getParentOfType(container,['P','DIV','LI']);
if(!para){ return; }
if(para.lastChild){
if((para.childNodes.length > 1 && para.lastChild.nodeType == 3 && /^[\s\xAD]*$/.test(para.lastChild.nodeValue)) ||
para.lastChild.tagName=='BR'){
domConstruct.destroy(para.lastChild);
}
}
if(!para.childNodes.length){
para.innerHTML=this.bogusHtmlContent;
}
}
});
});
},
'dijit/_MenuBase':function(){
define([
"./popup",
"dojo/window",
"./_Widget",
"./_KeyNavContainer",
"./_TemplatedMixin",
"dojo/_base/declare", // declare
"dojo/dom", // dom.isDescendant domClass.replace
"dojo/dom-attr",
"dojo/dom-class", // domClass.replace
"dojo/_base/lang", // lang.hitch
"dojo/_base/array" // array.indexOf
], function(pm, winUtils, _Widget, _KeyNavContainer, _TemplatedMixin,
declare, dom, domAttr, domClass, lang, array){
/*=====
var _Widget = dijit._Widget;
var _TemplatedMixin = dijit._TemplatedMixin;
var _KeyNavContainer = dijit._KeyNavContainer;
=====*/
// module:
// dijit/_MenuBase
// summary:
// Base class for Menu and MenuBar
return declare("dijit._MenuBase",
[_Widget, _TemplatedMixin, _KeyNavContainer],
{
// summary:
// Base class for Menu and MenuBar
// parentMenu: [readonly] Widget
// pointer to menu that displayed me
parentMenu: null,
// popupDelay: Integer
// number of milliseconds before hovering (without clicking) causes the popup to automatically open.
popupDelay: 500,
onExecute: function(){
// summary:
// Attach point for notification about when a menu item has been executed.
// This is an internal mechanism used for Menus to signal to their parent to
// close them, because they are about to execute the onClick handler. In
// general developers should not attach to or override this method.
// tags:
// protected
},
onCancel: function(/*Boolean*/ /*===== closeAll =====*/){
// summary:
// Attach point for notification about when the user cancels the current menu
// This is an internal mechanism used for Menus to signal to their parent to
// close them. In general developers should not attach to or override this method.
// tags:
// protected
},
_moveToPopup: function(/*Event*/ evt){
// summary:
// This handles the right arrow key (left arrow key on RTL systems),
// which will either open a submenu, or move to the next item in the
// ancestor MenuBar
// tags:
// private
if(this.focusedChild && this.focusedChild.popup && !this.focusedChild.disabled){
this.focusedChild._onClick(evt);
}else{
var topMenu = this._getTopMenu();
if(topMenu && topMenu._isMenuBar){
topMenu.focusNext();
}
}
},
_onPopupHover: function(/*Event*/ /*===== evt =====*/){
// summary:
// This handler is called when the mouse moves over the popup.
// tags:
// private
// if the mouse hovers over a menu popup that is in pending-close state,
// then stop the close operation.
// This can't be done in onItemHover since some popup targets don't have MenuItems (e.g. ColorPicker)
if(this.currentPopup && this.currentPopup._pendingClose_timer){
var parentMenu = this.currentPopup.parentMenu;
// highlight the parent menu item pointing to this popup
if(parentMenu.focusedChild){
parentMenu.focusedChild._setSelected(false);
}
parentMenu.focusedChild = this.currentPopup.from_item;
parentMenu.focusedChild._setSelected(true);
// cancel the pending close
this._stopPendingCloseTimer(this.currentPopup);
}
},
onItemHover: function(/*MenuItem*/ item){
// summary:
// Called when cursor is over a MenuItem.
// tags:
// protected
// Don't do anything unless user has "activated" the menu by:
// 1) clicking it
// 2) opening it from a parent menu (which automatically focuses it)
if(this.isActive){
this.focusChild(item);
if(this.focusedChild.popup && !this.focusedChild.disabled && !this.hover_timer){
this.hover_timer = setTimeout(lang.hitch(this, "_openPopup"), this.popupDelay);
}
}
// if the user is mixing mouse and keyboard navigation,
// then the menu may not be active but a menu item has focus,
// but it's not the item that the mouse just hovered over.
// To avoid both keyboard and mouse selections, use the latest.
if(this.focusedChild){
this.focusChild(item);
}
this._hoveredChild = item;
},
_onChildBlur: function(item){
// summary:
// Called when a child MenuItem becomes inactive because focus
// has been removed from the MenuItem *and* it's descendant menus.
// tags:
// private
this._stopPopupTimer();
item._setSelected(false);
// Close all popups that are open and descendants of this menu
var itemPopup = item.popup;
if(itemPopup){
this._stopPendingCloseTimer(itemPopup);
itemPopup._pendingClose_timer = setTimeout(function(){
itemPopup._pendingClose_timer = null;
if(itemPopup.parentMenu){
itemPopup.parentMenu.currentPopup = null;
}
pm.close(itemPopup); // this calls onClose
}, this.popupDelay);
}
},
onItemUnhover: function(/*MenuItem*/ item){
// summary:
// Callback fires when mouse exits a MenuItem
// tags:
// protected
if(this.isActive){
this._stopPopupTimer();
}
if(this._hoveredChild == item){ this._hoveredChild = null; }
},
_stopPopupTimer: function(){
// summary:
// Cancels the popup timer because the user has stop hovering
// on the MenuItem, etc.
// tags:
// private
if(this.hover_timer){
clearTimeout(this.hover_timer);
this.hover_timer = null;
}
},
_stopPendingCloseTimer: function(/*dijit._Widget*/ popup){
// summary:
// Cancels the pending-close timer because the close has been preempted
// tags:
// private
if(popup._pendingClose_timer){
clearTimeout(popup._pendingClose_timer);
popup._pendingClose_timer = null;
}
},
_stopFocusTimer: function(){
// summary:
// Cancels the pending-focus timer because the menu was closed before focus occured
// tags:
// private
if(this._focus_timer){
clearTimeout(this._focus_timer);
this._focus_timer = null;
}
},
_getTopMenu: function(){
// summary:
// Returns the top menu in this chain of Menus
// tags:
// private
for(var top=this; top.parentMenu; top=top.parentMenu);
return top;
},
onItemClick: function(/*dijit._Widget*/ item, /*Event*/ evt){
// summary:
// Handle clicks on an item.
// tags:
// private
// this can't be done in _onFocus since the _onFocus events occurs asynchronously
if(typeof this.isShowingNow == 'undefined'){ // non-popup menu
this._markActive();
}
this.focusChild(item);
if(item.disabled){ return false; }
if(item.popup){
this._openPopup();
}else{
// before calling user defined handler, close hierarchy of menus
// and restore focus to place it was when menu was opened
this.onExecute();
// user defined handler for click
item.onClick(evt);
}
},
_openPopup: function(){
// summary:
// Open the popup to the side of/underneath the current menu item
// tags:
// protected
this._stopPopupTimer();
var from_item = this.focusedChild;
if(!from_item){ return; } // the focused child lost focus since the timer was started
var popup = from_item.popup;
if(popup.isShowingNow){ return; }
if(this.currentPopup){
this._stopPendingCloseTimer(this.currentPopup);
pm.close(this.currentPopup);
}
popup.parentMenu = this;
popup.from_item = from_item; // helps finding the parent item that should be focused for this popup
var self = this;
pm.open({
parent: this,
popup: popup,
around: from_item.domNode,
orient: this._orient || ["after", "before"],
onCancel: function(){ // called when the child menu is canceled
// set isActive=false (_closeChild vs _cleanUp) so that subsequent hovering will NOT open child menus
// which seems aligned with the UX of most applications (e.g. notepad, wordpad, paint shop pro)
self.focusChild(from_item); // put focus back on my node
self._cleanUp(); // close the submenu (be sure this is done _after_ focus is moved)
from_item._setSelected(true); // oops, _cleanUp() deselected the item
self.focusedChild = from_item; // and unset focusedChild
},
onExecute: lang.hitch(this, "_cleanUp")
});
this.currentPopup = popup;
// detect mouseovers to handle lazy mouse movements that temporarily focus other menu items
popup.connect(popup.domNode, "onmouseenter", lang.hitch(self, "_onPopupHover")); // cleaned up when the popped-up widget is destroyed on close
if(popup.focus){
// If user is opening the popup via keyboard (right arrow, or down arrow for MenuBar),
// if the cursor happens to collide with the popup, it will generate an onmouseover event
// even though the mouse wasn't moved. Use a setTimeout() to call popup.focus so that
// our focus() call overrides the onmouseover event, rather than vice-versa. (#8742)
popup._focus_timer = setTimeout(lang.hitch(popup, function(){
this._focus_timer = null;
this.focus();
}), 0);
}
},
_markActive: function(){
// summary:
// Mark this menu's state as active.
// Called when this Menu gets focus from:
// 1) clicking it (mouse or via space/arrow key)
// 2) being opened by a parent menu.
// This is not called just from mouse hover.
// Focusing a menu via TAB does NOT automatically set isActive
// since TAB is a navigation operation and not a selection one.
// For Windows apps, pressing the ALT key focuses the menubar
// menus (similar to TAB navigation) but the menu is not active
// (ie no dropdown) until an item is clicked.
this.isActive = true;
domClass.replace(this.domNode, "dijitMenuActive", "dijitMenuPassive");
},
onOpen: function(/*Event*/ /*===== e =====*/){
// summary:
// Callback when this menu is opened.
// This is called by the popup manager as notification that the menu
// was opened.
// tags:
// private
this.isShowingNow = true;
this._markActive();
},
_markInactive: function(){
// summary:
// Mark this menu's state as inactive.
this.isActive = false; // don't do this in _onBlur since the state is pending-close until we get here
domClass.replace(this.domNode, "dijitMenuPassive", "dijitMenuActive");
},
onClose: function(){
// summary:
// Callback when this menu is closed.
// This is called by the popup manager as notification that the menu
// was closed.
// tags:
// private
this._stopFocusTimer();
this._markInactive();
this.isShowingNow = false;
this.parentMenu = null;
},
_closeChild: function(){
// summary:
// Called when submenu is clicked or focus is lost. Close hierarchy of menus.
// tags:
// private
this._stopPopupTimer();
if(this.currentPopup){
// If focus is on a descendant MenuItem then move focus to me,
// because IE doesn't like it when you display:none a node with focus,
// and also so keyboard users don't lose control.
// Likely, immediately after a user defined onClick handler will move focus somewhere
// else, like a Dialog.
if(array.indexOf(this._focusManager.activeStack, this.id) >= 0){
domAttr.set(this.focusedChild.focusNode, "tabIndex", this.tabIndex);
this.focusedChild.focusNode.focus();
}
// Close all popups that are open and descendants of this menu
pm.close(this.currentPopup);
this.currentPopup = null;
}
if(this.focusedChild){ // unhighlight the focused item
this.focusedChild._setSelected(false);
this.focusedChild._onUnhover();
this.focusedChild = null;
}
},
_onItemFocus: function(/*MenuItem*/ item){
// summary:
// Called when child of this Menu gets focus from:
// 1) clicking it
// 2) tabbing into it
// 3) being opened by a parent menu.
// This is not called just from mouse hover.
if(this._hoveredChild && this._hoveredChild != item){
this._hoveredChild._onUnhover(); // any previous mouse movement is trumped by focus selection
}
},
_onBlur: function(){
// summary:
// Called when focus is moved away from this Menu and it's submenus.
// tags:
// protected
this._cleanUp();
this.inherited(arguments);
},
_cleanUp: function(){
// summary:
// Called when the user is done with this menu. Closes hierarchy of menus.
// tags:
// private
this._closeChild(); // don't call this.onClose since that's incorrect for MenuBar's that never close
if(typeof this.isShowingNow == 'undefined'){ // non-popup menu doesn't call onClose
this._markInactive();
}
}
});
});
},
'dijit/PopupMenuBarItem':function(){
define([
"dojo/_base/declare", // declare
"./PopupMenuItem",
"./MenuBarItem"
], function(declare, PopupMenuItem, MenuBarItem){
// module:
// dijit/PopupMenuBarItem
// summary:
// Item in a MenuBar like "File" or "Edit", that spawns a submenu when pressed (or hovered)
var _MenuBarItemMixin = MenuBarItem._MenuBarItemMixin;
/*=====
var PopupMenuItem = dijit.PopupMenuItem;
var _MenuBarItemMixin = dijit._MenuBarItemMixin;
=====*/
return declare("dijit.PopupMenuBarItem", [PopupMenuItem, _MenuBarItemMixin], {
// summary:
// Item in a MenuBar like "File" or "Edit", that spawns a submenu when pressed (or hovered)
});
});
},
'dijit/tree/ForestStoreModel':function(){
define("dijit/tree/ForestStoreModel", [
"dojo/_base/array", // array.indexOf array.some
"dojo/_base/declare", // declare
"dojo/_base/lang", // lang.hitch
"dojo/_base/window", // win.global
"./TreeStoreModel"
], function(array, declare, lang, win, TreeStoreModel){
/*=====
var TreeStoreModel = dijit.tree.TreeStoreModel;
=====*/
// module:
// dijit/tree/ForestStoreModel
// summary:
// Interface between a dijit.Tree and a dojo.data store that doesn't have a root item,
// a.k.a. a store that has multiple "top level" items.
return declare("dijit.tree.ForestStoreModel", TreeStoreModel, {
// summary:
// Interface between a dijit.Tree and a dojo.data store that doesn't have a root item,
// a.k.a. a store that has multiple "top level" items.
//
// description
// Use this class to wrap a dojo.data store, making all the items matching the specified query
// appear as children of a fabricated "root item". If no query is specified then all the
// items returned by fetch() on the underlying store become children of the root item.
// This class allows dijit.Tree to assume a single root item, even if the store doesn't have one.
//
// When using this class the developer must override a number of methods according to their app and
// data, including:
// - onNewRootItem
// - onAddToRoot
// - onLeaveRoot
// - onNewItem
// - onSetItem
// Parameters to constructor
// rootId: String
// ID of fabricated root item
rootId: "$root$",
// rootLabel: String
// Label of fabricated root item
rootLabel: "ROOT",
// query: String
// Specifies the set of children of the root item.
// example:
// | {type:'continent'}
query: null,
// End of parameters to constructor
constructor: function(params){
// summary:
// Sets up variables, etc.
// tags:
// private
// Make dummy root item
this.root = {
store: this,
root: true,
id: params.rootId,
label: params.rootLabel,
children: params.rootChildren // optional param
};
},
// =======================================================================
// Methods for traversing hierarchy
mayHaveChildren: function(/*dojo.data.Item*/ item){
// summary:
// Tells if an item has or may have children. Implementing logic here
// avoids showing +/- expando icon for nodes that we know don't have children.
// (For efficiency reasons we may not want to check if an element actually
// has children until user clicks the expando node)
// tags:
// extension
return item === this.root || this.inherited(arguments);
},
getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ callback, /*function*/ onError){
// summary:
// Calls onComplete() with array of child items of given parent item, all loaded.
if(parentItem === this.root){
if(this.root.children){
// already loaded, just return
callback(this.root.children);
}else{
this.store.fetch({
query: this.query,
onComplete: lang.hitch(this, function(items){
this.root.children = items;
callback(items);
}),
onError: onError
});
}
}else{
this.inherited(arguments);
}
},
// =======================================================================
// Inspecting items
isItem: function(/* anything */ something){
return (something === this.root) ? true : this.inherited(arguments);
},
fetchItemByIdentity: function(/* object */ keywordArgs){
if(keywordArgs.identity == this.root.id){
var scope = keywordArgs.scope?keywordArgs.scope:win.global;
if(keywordArgs.onItem){
keywordArgs.onItem.call(scope, this.root);
}
}else{
this.inherited(arguments);
}
},
getIdentity: function(/* item */ item){
return (item === this.root) ? this.root.id : this.inherited(arguments);
},
getLabel: function(/* item */ item){
return (item === this.root) ? this.root.label : this.inherited(arguments);
},
// =======================================================================
// Write interface
newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
// summary:
// Creates a new item. See dojo.data.api.Write for details on args.
// Used in drag & drop when item from external source dropped onto tree.
if(parent === this.root){
this.onNewRootItem(args);
return this.store.newItem(args);
}else{
return this.inherited(arguments);
}
},
onNewRootItem: function(/* dojo.dnd.Item */ /*===== args =====*/){
// summary:
// User can override this method to modify a new element that's being
// added to the root of the tree, for example to add a flag like root=true
},
pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
// summary:
// Move or copy an item from one parent item to another.
// Used in drag & drop
if(oldParentItem === this.root){
if(!bCopy){
// It's onLeaveRoot()'s responsibility to modify the item so it no longer matches
// this.query... thus triggering an onChildrenChange() event to notify the Tree
// that this element is no longer a child of the root node
this.onLeaveRoot(childItem);
}
}
this.inherited(arguments, [childItem,
oldParentItem === this.root ? null : oldParentItem,
newParentItem === this.root ? null : newParentItem,
bCopy,
insertIndex
]);
if(newParentItem === this.root){
// It's onAddToRoot()'s responsibility to modify the item so it matches
// this.query... thus triggering an onChildrenChange() event to notify the Tree
// that this element is now a child of the root node
this.onAddToRoot(childItem);
}
},
// =======================================================================
// Handling for top level children
onAddToRoot: function(/* item */ item){
// summary:
// Called when item added to root of tree; user must override this method
// to modify the item so that it matches the query for top level items
// example:
// | store.setValue(item, "root", true);
// tags:
// extension
console.log(this, ": item ", item, " added to root");
},
onLeaveRoot: function(/* item */ item){
// summary:
// Called when item removed from root of tree; user must override this method
// to modify the item so it doesn't match the query for top level items
// example:
// | store.unsetAttribute(item, "root");
// tags:
// extension
console.log(this, ": item ", item, " removed from root");
},
// =======================================================================
// Events from data store
_requeryTop: function(){
// reruns the query for the children of the root node,
// sending out an onSet notification if those children have changed
var oldChildren = this.root.children || [];
this.store.fetch({
query: this.query,
onComplete: lang.hitch(this, function(newChildren){
this.root.children = newChildren;
// If the list of children or the order of children has changed...
if(oldChildren.length != newChildren.length ||
array.some(oldChildren, function(item, idx){ return newChildren[idx] != item;})){
this.onChildrenChange(this.root, newChildren);
}
})
});
},
onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
// summary:
// Handler for when new items appear in the store. Developers should override this
// method to be more efficient based on their app/data.
// description:
// Note that the default implementation requeries the top level items every time
// a new item is created, since any new item could be a top level item (even in
// addition to being a child of another item, since items can have multiple parents).
//
// If developers can detect which items are possible top level items (based on the item and the
// parentInfo parameters), they should override this method to only call _requeryTop() for top
// level items. Often all top level items have parentInfo==null, but
// that will depend on which store you use and what your data is like.
// tags:
// extension
this._requeryTop();
this.inherited(arguments);
},
onDeleteItem: function(/*Object*/ item){
// summary:
// Handler for delete notifications from underlying store
// check if this was a child of root, and if so send notification that root's children
// have changed
if(array.indexOf(this.root.children, item) != -1){
this._requeryTop();
}
this.inherited(arguments);
},
onSetItem: function(/* item */ item,
/* attribute-name-string */ attribute,
/* object | array */ oldValue,
/* object | array */ newValue){
// summary:
// Updates the tree view according to changes to an item in the data store.
// Developers should override this method to be more efficient based on their app/data.
// description:
// Handles updates to an item's children by calling onChildrenChange(), and
// other updates to an item by calling onChange().
//
// Also, any change to any item re-executes the query for the tree's top-level items,
// since this modified item may have started/stopped matching the query for top level items.
//
// If possible, developers should override this function to only call _requeryTop() when
// the change to the item has caused it to stop/start being a top level item in the tree.
// tags:
// extension
this._requeryTop();
this.inherited(arguments);
}
});
});
},
'url:dijit/layout/templates/AccordionButton.html':"<div data-dojo-attach-event='onclick:_onTitleClick' class='dijitAccordionTitle' role=\"presentation\">\n\t<div data-dojo-attach-point='titleNode,focusNode' data-dojo-attach-event='onkeypress:_onTitleKeyPress'\n\t\t\tclass='dijitAccordionTitleFocus' role=\"tab\" aria-expanded=\"false\"\n\t\t><span class='dijitInline dijitAccordionArrow' role=\"presentation\"></span\n\t\t><span class='arrowTextUp' role=\"presentation\">+</span\n\t\t><span class='arrowTextDown' role=\"presentation\">-</span\n\t\t><img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon\" data-dojo-attach-point='iconNode' style=\"vertical-align: middle\" role=\"presentation\"/>\n\t\t<span role=\"presentation\" data-dojo-attach-point='titleTextNode' class='dijitAccordionText'></span>\n\t</div>\n</div>\n",
'dijit/TitlePane':function(){
define([
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"dojo/dom-attr", // domAttr.set or get domAttr.remove
"dojo/dom-class", // domClass.replace
"dojo/dom-geometry", // domGeometry.setMarginBox domGeometry.getMarginBox
"dojo/_base/event", // event.stop
"dojo/fx", // fxUtils.wipeIn fxUtils.wipeOut
"dojo/_base/kernel", // kernel.deprecated
"dojo/keys", // keys.DOWN_ARROW keys.ENTER
"./_CssStateMixin",
"./_TemplatedMixin",
"./layout/ContentPane",
"dojo/text!./templates/TitlePane.html",
"./_base/manager" // defaultDuration
], function(array, declare, dom, domAttr, domClass, domGeometry, event, fxUtils, kernel, keys,
_CssStateMixin, _TemplatedMixin, ContentPane, template, manager){
/*=====
var _Widget = dijit._Widget;
var _TemplatedMixin = dijit._TemplatedMixin;
var _CssStateMixin = dijit._CssStateMixin;
var ContentPane = dijit.layout.ContentPane;
=====*/
// module:
// dijit/TitlePane
// summary:
// A pane with a title on top, that can be expanded or collapsed.
return declare("dijit.TitlePane", [ContentPane, _TemplatedMixin, _CssStateMixin], {
// summary:
// A pane with a title on top, that can be expanded or collapsed.
//
// description:
// An accessible container with a title Heading, and a content
// section that slides open and closed. TitlePane is an extension to
// `dijit.layout.ContentPane`, providing all the useful content-control aspects from it.
//
// example:
// | // load a TitlePane from remote file:
// | var foo = new dijit.TitlePane({ href: "foobar.html", title:"Title" });
// | foo.startup();
//
// example:
// | <!-- markup href example: -->
// | <div data-dojo-type="dijit.TitlePane" data-dojo-props="href: 'foobar.html', title: 'Title'"></div>
//
// example:
// | <!-- markup with inline data -->
// | <div data-dojo-type="dijit.TitlePane" title="Title">
// | <p>I am content</p>
// | </div>
// title: String
// Title of the pane
title: "",
_setTitleAttr: { node: "titleNode", type: "innerHTML" }, // override default where title becomes a hover tooltip
// open: Boolean
// Whether pane is opened or closed.
open: true,
// toggleable: Boolean
// Whether pane can be opened or closed by clicking the title bar.
toggleable: true,
// tabIndex: String
// Tabindex setting for the title (so users can tab to the title then
// use space/enter to open/close the title pane)
tabIndex: "0",
// duration: Integer
// Time in milliseconds to fade in/fade out
duration: manager.defaultDuration,
// baseClass: [protected] String
// The root className to be placed on this widget's domNode.
baseClass: "dijitTitlePane",
templateString: template,
// doLayout: [protected] Boolean
// Don't change this parameter from the default value.
// This ContentPane parameter doesn't make sense for TitlePane, since TitlePane
// is never a child of a layout container, nor should TitlePane try to control
// the size of an inner widget.
doLayout: false,
// Tooltip is defined in _WidgetBase but we need to handle the mapping to DOM here
_setTooltipAttr: {node: "focusNode", type: "attribute", attribute: "title"}, // focusNode spans the entire width, titleNode doesn't
buildRendering: function(){
this.inherited(arguments);
dom.setSelectable(this.titleNode, false);
},
postCreate: function(){
this.inherited(arguments);
// Hover and focus effect on title bar, except for non-toggleable TitlePanes
// This should really be controlled from _setToggleableAttr() but _CssStateMixin
// doesn't provide a way to disconnect a previous _trackMouseState() call
if(this.toggleable){
this._trackMouseState(this.titleBarNode, "dijitTitlePaneTitle");
}
// setup open/close animations
var hideNode = this.hideNode, wipeNode = this.wipeNode;
this._wipeIn = fxUtils.wipeIn({
node: wipeNode,
duration: this.duration,
beforeBegin: function(){
hideNode.style.display="";
}
});
this._wipeOut = fxUtils.wipeOut({
node: wipeNode,
duration: this.duration,
onEnd: function(){
hideNode.style.display="none";
}
});
},
_setOpenAttr: function(/*Boolean*/ open, /*Boolean*/ animate){
// summary:
// Hook to make set("open", boolean) control the open/closed state of the pane.
// open: Boolean
// True if you want to open the pane, false if you want to close it.
array.forEach([this._wipeIn, this._wipeOut], function(animation){
if(animation && animation.status() == "playing"){
animation.stop();
}
});
if(animate){
var anim = this[open ? "_wipeIn" : "_wipeOut"];
anim.play();
}else{
this.hideNode.style.display = this.wipeNode.style.display = open ? "" : "none";
}
// load content (if this is the first time we are opening the TitlePane
// and content is specified as an href, or href was set when hidden)
if(this._started){
if(open){
this._onShow();
}else{
this.onHide();
}
}
this.arrowNodeInner.innerHTML = open ? "-" : "+";
this.containerNode.setAttribute("aria-hidden", open ? "false" : "true");
this.focusNode.setAttribute("aria-pressed", open ? "true" : "false");
this._set("open", open);
this._setCss();
},
_setToggleableAttr: function(/*Boolean*/ canToggle){
// summary:
// Hook to make set("toggleable", boolean) work.
// canToggle: Boolean
// True to allow user to open/close pane by clicking title bar.
this.focusNode.setAttribute("role", canToggle ? "button" : "heading");
if(canToggle){
// TODO: if canToggle is switched from true to false shouldn't we remove this setting?
this.focusNode.setAttribute("aria-controls", this.id+"_pane");
domAttr.set(this.focusNode, "tabIndex", this.tabIndex);
}else{
domAttr.remove(this.focusNode, "tabIndex");
}
this._set("toggleable", canToggle);
this._setCss();
},
_setContentAttr: function(/*String|DomNode|Nodelist*/ content){
// summary:
// Hook to make set("content", ...) work.
// Typically called when an href is loaded. Our job is to make the animation smooth.
if(!this.open || !this._wipeOut || this._wipeOut.status() == "playing"){
// we are currently *closing* the pane (or the pane is closed), so just let that continue
this.inherited(arguments);
}else{
if(this._wipeIn && this._wipeIn.status() == "playing"){
this._wipeIn.stop();
}
// freeze container at current height so that adding new content doesn't make it jump
domGeometry.setMarginBox(this.wipeNode, { h: domGeometry.getMarginBox(this.wipeNode).h });
// add the new content (erasing the old content, if any)
this.inherited(arguments);
// call _wipeIn.play() to animate from current height to new height
if(this._wipeIn){
this._wipeIn.play();
}else{
this.hideNode.style.display = "";
}
}
},
toggle: function(){
// summary:
// Switches between opened and closed state
// tags:
// private
this._setOpenAttr(!this.open, true);
},
_setCss: function(){
// summary:
// Set the open/close css state for the TitlePane
// tags:
// private
var node = this.titleBarNode || this.focusNode;
var oldCls = this._titleBarClass;
this._titleBarClass = "dijit" + (this.toggleable ? "" : "Fixed") + (this.open ? "Open" : "Closed");
domClass.replace(node, this._titleBarClass, oldCls || "");
this.arrowNodeInner.innerHTML = this.open ? "-" : "+";
},
_onTitleKey: function(/*Event*/ e){
// summary:
// Handler for when user hits a key
// tags:
// private
if(e.charOrCode == keys.ENTER || e.charOrCode == ' '){
if(this.toggleable){
this.toggle();
}
event.stop(e);
}else if(e.charOrCode == keys.DOWN_ARROW && this.open){
this.containerNode.focus();
e.preventDefault();
}
},
_onTitleClick: function(){
// summary:
// Handler when user clicks the title bar
// tags:
// private
if(this.toggleable){
this.toggle();
}
},
setTitle: function(/*String*/ title){
// summary:
// Deprecated. Use set('title', ...) instead.
// tags:
// deprecated
kernel.deprecated("dijit.TitlePane.setTitle() is deprecated. Use set('title', ...) instead.", "", "2.0");
this.set("title", title);
}
});
});
},
'dijit/form/_ComboBoxMenuMixin':function(){
define([
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/i18n", // i18n.getLocalization
"dojo/_base/window", // win.doc.createTextNode
"dojo/i18n!./nls/ComboBox"
], function(array, declare, domAttr, i18n, win){
// module:
// dijit/form/_ComboBoxMenuMixin
// summary:
// Focus-less menu for internal use in `dijit.form.ComboBox`
return declare( "dijit.form._ComboBoxMenuMixin", null, {
// summary:
// Focus-less menu for internal use in `dijit.form.ComboBox`
// tags:
// private
// _messages: Object
// Holds "next" and "previous" text for paging buttons on drop down
_messages: null,
postMixInProperties: function(){
this.inherited(arguments);
this._messages = i18n.getLocalization("dijit.form", "ComboBox", this.lang);
},
buildRendering: function(){
this.inherited(arguments);
// fill in template with i18n messages
this.previousButton.innerHTML = this._messages["previousMessage"];
this.nextButton.innerHTML = this._messages["nextMessage"];
},
_setValueAttr: function(/*Object*/ value){
this.value = value;
this.onChange(value);
},
onClick: function(/*DomNode*/ node){
if(node == this.previousButton){
this._setSelectedAttr(null);
this.onPage(-1);
}else if(node == this.nextButton){
this._setSelectedAttr(null);
this.onPage(1);
}else{
this.onChange(node);
}
},
// stubs
onChange: function(/*Number*/ /*===== direction =====*/){
// summary:
// Notifies ComboBox/FilteringSelect that user selected an option.
// tags:
// callback
},
onPage: function(/*Number*/ /*===== direction =====*/){
// summary:
// Notifies ComboBox/FilteringSelect that user clicked to advance to next/previous page.
// tags:
// callback
},
onClose: function(){
// summary:
// Callback from dijit.popup code to this widget, notifying it that it closed
// tags:
// private
this._setSelectedAttr(null);
},
_createOption: function(/*Object*/ item, labelFunc){
// summary:
// Creates an option to appear on the popup menu subclassed by
// `dijit.form.FilteringSelect`.
var menuitem = this._createMenuItem();
var labelObject = labelFunc(item);
if(labelObject.html){
menuitem.innerHTML = labelObject.label;
}else{
menuitem.appendChild(
win.doc.createTextNode(labelObject.label)
);
}
// #3250: in blank options, assign a normal height
if(menuitem.innerHTML == ""){
menuitem.innerHTML = "&#160;"; // &nbsp;
}
// update menuitem.dir if BidiSupport was required
this.applyTextDir(menuitem, (menuitem.innerText || menuitem.textContent || ""));
menuitem.item=item;
return menuitem;
},
createOptions: function(results, options, labelFunc){
// summary:
// Fills in the items in the drop down list
// results:
// Array of items
// options:
// The options to the query function of the store
//
// labelFunc:
// Function to produce a label in the drop down list from a dojo.data item
// display "Previous . . ." button
this.previousButton.style.display = (options.start == 0) ? "none" : "";
domAttr.set(this.previousButton, "id", this.id + "_prev");
// create options using _createOption function defined by parent
// ComboBox (or FilteringSelect) class
// #2309:
// iterate over cache nondestructively
array.forEach(results, function(item, i){
var menuitem = this._createOption(item, labelFunc);
domAttr.set(menuitem, "id", this.id + i);
this.nextButton.parentNode.insertBefore(menuitem, this.nextButton);
}, this);
// display "Next . . ." button
var displayMore = false;
// Try to determine if we should show 'more'...
if(results.total && !results.total.then && results.total != -1){
if((options.start + options.count) < results.total){
displayMore = true;
}else if((options.start + options.count) > results.total && options.count == results.length){
// Weird return from a data store, where a start + count > maxOptions
// implies maxOptions isn't really valid and we have to go into faking it.
// And more or less assume more if count == results.length
displayMore = true;
}
}else if(options.count == results.length){
//Don't know the size, so we do the best we can based off count alone.
//So, if we have an exact match to count, assume more.
displayMore = true;
}
this.nextButton.style.display = displayMore ? "" : "none";
domAttr.set(this.nextButton,"id", this.id + "_next");
return this.containerNode.childNodes;
},
clearResultList: function(){
// summary:
// Clears the entries in the drop down list, but of course keeps the previous and next buttons.
var container = this.containerNode;
while(container.childNodes.length > 2){
container.removeChild(container.childNodes[container.childNodes.length-2]);
}
this._setSelectedAttr(null);
},
highlightFirstOption: function(){
// summary:
// Highlight the first real item in the list (not Previous Choices).
this.selectFirstNode();
},
highlightLastOption: function(){
// summary:
// Highlight the last real item in the list (not More Choices).
this.selectLastNode();
},
selectFirstNode: function(){
this.inherited(arguments);
if(this.getHighlightedOption() == this.previousButton){
this.selectNextNode();
}
},
selectLastNode: function(){
this.inherited(arguments);
if(this.getHighlightedOption() == this.nextButton){
this.selectPreviousNode();
}
},
getHighlightedOption: function(){
return this._getSelectedAttr();
}
});
});
},
'url:dijit/form/templates/DropDownButton.html':"<span class=\"dijit dijitReset dijitInline\"\n\t><span class='dijitReset dijitInline dijitButtonNode'\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" data-dojo-attach-point=\"_buttonNode\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"focusNode,titleNode,_arrowWrapperNode\"\n\t\t\trole=\"button\" aria-haspopup=\"true\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\"\n\t\t\t\tdata-dojo-attach-point=\"iconNode\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tdata-dojo-attach-point=\"containerNode,_popupStateNode\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonInner\"></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonChar\">&#9660;</span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\" tabIndex=\"-1\"\n\t\tdata-dojo-attach-point=\"valueNode\"\n/></span>\n",
'dijit/form/ToggleButton':function(){
define("dijit/form/ToggleButton", [
"dojo/_base/declare", // declare
"dojo/_base/kernel", // kernel.deprecated
"./Button",
"./_ToggleButtonMixin"
], function(declare, kernel, Button, _ToggleButtonMixin){
/*=====
var Button = dijit.form.Button;
var _ToggleButtonMixin = dijit.form._ToggleButtonMixin;
=====*/
// module:
// dijit/form/ToggleButton
// summary:
// A templated button widget that can be in two states (checked or not).
return declare("dijit.form.ToggleButton", [Button, _ToggleButtonMixin], {
// summary:
// A templated button widget that can be in two states (checked or not).
// Can be base class for things like tabs or checkbox or radio buttons
baseClass: "dijitToggleButton",
setChecked: function(/*Boolean*/ checked){
// summary:
// Deprecated. Use set('checked', true/false) instead.
kernel.deprecated("setChecked("+checked+") is deprecated. Use set('checked',"+checked+") instead.", "", "2.0");
this.set('checked', checked);
}
});
});
},
'dijit/form/NumberSpinner':function(){
define([
"dojo/_base/declare", // declare
"dojo/_base/event", // event.stop
"dojo/keys", // keys.END keys.HOME
"./_Spinner",
"./NumberTextBox"
], function(declare, event, keys, _Spinner, NumberTextBox){
/*=====
var _Spinner = dijit.form._Spinner;
var NumberTextBox = dijit.form.NumberTextBox;
=====*/
// module:
// dijit/form/NumberSpinner
// summary:
// Extends NumberTextBox to add up/down arrows and pageup/pagedown for incremental change to the value
return declare("dijit.form.NumberSpinner", [_Spinner, NumberTextBox.Mixin], {
// summary:
// Extends NumberTextBox to add up/down arrows and pageup/pagedown for incremental change to the value
//
// description:
// A `dijit.form.NumberTextBox` extension to provide keyboard accessible value selection
// as well as icons for spinning direction. When using the keyboard, the typematic rules
// apply, meaning holding the key will gradually increase or decrease the value and
// accelerate.
//
// example:
// | new dijit.form.NumberSpinner({ constraints:{ max:300, min:100 }}, "someInput");
adjust: function(/*Object*/ val, /*Number*/ delta){
// summary:
// Change Number val by the given amount
// tags:
// protected
var tc = this.constraints,
v = isNaN(val),
gotMax = !isNaN(tc.max),
gotMin = !isNaN(tc.min)
;
if(v && delta != 0){ // blank or invalid value and they want to spin, so create defaults
val = (delta > 0) ?
gotMin ? tc.min : gotMax ? tc.max : 0 :
gotMax ? this.constraints.max : gotMin ? tc.min : 0
;
}
var newval = val + delta;
if(v || isNaN(newval)){ return val; }
if(gotMax && (newval > tc.max)){
newval = tc.max;
}
if(gotMin && (newval < tc.min)){
newval = tc.min;
}
return newval;
},
_onKeyPress: function(e){
if((e.charOrCode == keys.HOME || e.charOrCode == keys.END) && !(e.ctrlKey || e.altKey || e.metaKey)
&& typeof this.get('value') != 'undefined' /* gibberish, so HOME and END are default editing keys*/){
var value = this.constraints[(e.charOrCode == keys.HOME ? "min" : "max")];
if(typeof value == "number"){
this._setValueAttr(value, false);
}
// eat home or end key whether we change the value or not
event.stop(e);
}
}
});
});
},
'dijit/form/Textarea':function(){
define([
"dojo/_base/declare", // declare
"dojo/dom-style", // domStyle.set
"./_ExpandingTextAreaMixin",
"./SimpleTextarea"
], function(declare, domStyle, _ExpandingTextAreaMixin, SimpleTextarea){
/*=====
var _ExpandingTextAreaMixin = dijit.form._ExpandingTextAreaMixin;
var SimpleTextarea = dijit.form.SimpleTextarea;
=====*/
// module:
// dijit/form/Textarea
// summary:
// A textarea widget that adjusts it's height according to the amount of data.
return declare("dijit.form.Textarea", [SimpleTextarea, _ExpandingTextAreaMixin], {
// summary:
// A textarea widget that adjusts it's height according to the amount of data.
//
// description:
// A textarea that dynamically expands/contracts (changing it's height) as
// the user types, to display all the text without requiring a scroll bar.
//
// Takes nearly all the parameters (name, value, etc.) that a vanilla textarea takes.
// Rows is not supported since this widget adjusts the height.
//
// example:
// | <textarea data-dojo-type="dijit.form.TextArea">...</textarea>
// TODO: for 2.0, rename this to ExpandingTextArea, and rename SimpleTextarea to TextArea
baseClass: "dijitTextBox dijitTextArea dijitExpandingTextArea",
// Override SimpleTextArea.cols to default to width:100%, for backward compatibility
cols: "",
buildRendering: function(){
this.inherited(arguments);
// tweak textarea style to reduce browser differences
domStyle.set(this.textbox, { overflowY: 'hidden', overflowX: 'auto', boxSizing: 'border-box', MsBoxSizing: 'border-box', WebkitBoxSizing: 'border-box', MozBoxSizing: 'border-box' });
}
});
});
},
'dijit/form/DateTextBox':function(){
define([
"dojo/_base/declare", // declare
"../Calendar",
"./_DateTimeTextBox"
], function(declare, Calendar, _DateTimeTextBox){
/*=====
var Calendar = dijit.Calendar;
var _DateTimeTextBox = dijit.form._DateTimeTextBox;
=====*/
// module:
// dijit/form/DateTextBox
// summary:
// A validating, serializable, range-bound date text box with a drop down calendar
return declare("dijit.form.DateTextBox", _DateTimeTextBox, {
// summary:
// A validating, serializable, range-bound date text box with a drop down calendar
//
// Example:
// | new dijit.form.DateTextBox({value: new Date(2009, 0, 20)})
//
// Example:
// | <input data-dojo-type='dijit.form.DateTextBox' value='2009-01-20'>
baseClass: "dijitTextBox dijitComboBox dijitDateTextBox",
popupClass: Calendar,
_selector: "date",
// value: Date
// The value of this widget as a JavaScript Date object, with only year/month/day specified.
// If specified in markup, use the format specified in `stamp.fromISOString`.
// set("value", ...) accepts either a Date object or a string.
value: new Date("") // value.toString()="NaN"
});
});
},
'dijit/form/ComboButton':function(){
define([
"dojo/_base/declare", // declare
"dojo/_base/event", // event.stop
"dojo/keys", // keys
"../focus", // focus.focus()
"./DropDownButton",
"dojo/text!./templates/ComboButton.html"
], function(declare, event, keys, focus, DropDownButton, template){
/*=====
var DropDownButton = dijit.form.DropDownButton;
=====*/
// module:
// dijit/form/ComboButton
// summary:
// A combination button and drop-down button.
return declare("dijit.form.ComboButton", DropDownButton, {
// summary:
// A combination button and drop-down button.
// Users can click one side to "press" the button, or click an arrow
// icon to display the drop down.
//
// example:
// | <button data-dojo-type="dijit.form.ComboButton" onClick="...">
// | <span>Hello world</span>
// | <div data-dojo-type="dijit.Menu">...</div>
// | </button>
//
// example:
// | var button1 = new dijit.form.ComboButton({label: "hello world", onClick: foo, dropDown: "myMenu"});
// | dojo.body().appendChild(button1.domNode);
//
templateString: template,
// Map widget attributes to DOMNode attributes.
_setIdAttr: "", // override _FormWidgetMixin which puts id on the focusNode
_setTabIndexAttr: ["focusNode", "titleNode"],
_setTitleAttr: "titleNode",
// optionsTitle: String
// Text that describes the options menu (accessibility)
optionsTitle: "",
baseClass: "dijitComboButton",
// Set classes like dijitButtonContentsHover or dijitArrowButtonActive depending on
// mouse action over specified node
cssStateNodes: {
"buttonNode": "dijitButtonNode",
"titleNode": "dijitButtonContents",
"_popupStateNode": "dijitDownArrowButton"
},
_focusedNode: null,
_onButtonKeyPress: function(/*Event*/ evt){
// summary:
// Handler for right arrow key when focus is on left part of button
if(evt.charOrCode == keys[this.isLeftToRight() ? "RIGHT_ARROW" : "LEFT_ARROW"]){
focus.focus(this._popupStateNode);
event.stop(evt);
}
},
_onArrowKeyPress: function(/*Event*/ evt){
// summary:
// Handler for left arrow key when focus is on right part of button
if(evt.charOrCode == keys[this.isLeftToRight() ? "LEFT_ARROW" : "RIGHT_ARROW"]){
focus.focus(this.titleNode);
event.stop(evt);
}
},
focus: function(/*String*/ position){
// summary:
// Focuses this widget to according to position, if specified,
// otherwise on arrow node
// position:
// "start" or "end"
if(!this.disabled){
focus.focus(position == "start" ? this.titleNode : this._popupStateNode);
}
}
});
});
},
'dijit/layout/AccordionContainer':function(){
require({cache:{
'url:dijit/layout/templates/AccordionButton.html':"<div data-dojo-attach-event='onclick:_onTitleClick' class='dijitAccordionTitle' role=\"presentation\">\n\t<div data-dojo-attach-point='titleNode,focusNode' data-dojo-attach-event='onkeypress:_onTitleKeyPress'\n\t\t\tclass='dijitAccordionTitleFocus' role=\"tab\" aria-expanded=\"false\"\n\t\t><span class='dijitInline dijitAccordionArrow' role=\"presentation\"></span\n\t\t><span class='arrowTextUp' role=\"presentation\">+</span\n\t\t><span class='arrowTextDown' role=\"presentation\">-</span\n\t\t><img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon\" data-dojo-attach-point='iconNode' style=\"vertical-align: middle\" role=\"presentation\"/>\n\t\t<span role=\"presentation\" data-dojo-attach-point='titleTextNode' class='dijitAccordionText'></span>\n\t</div>\n</div>\n"}});
define("dijit/layout/AccordionContainer", [
"require",
"dojo/_base/array", // array.forEach array.map
"dojo/_base/declare", // declare
"dojo/_base/event", // event.stop
"dojo/_base/fx", // fx.Animation
"dojo/dom", // dom.setSelectable
"dojo/dom-attr", // domAttr.attr
"dojo/dom-class", // domClass.remove
"dojo/dom-construct", // domConstruct.place
"dojo/dom-geometry",
"dojo/_base/kernel",
"dojo/keys", // keys
"dojo/_base/lang", // lang.getObject lang.hitch
"dojo/_base/sniff", // has("ie")
"dojo/topic", // publish
"../focus", // focus.focus()
"../_base/manager", // manager.defaultDuration
"dojo/ready",
"../_Widget",
"../_Container",
"../_TemplatedMixin",
"../_CssStateMixin",
"./StackContainer",
"./ContentPane",
"dojo/text!./templates/AccordionButton.html"
], function(require, array, declare, event, fx, dom, domAttr, domClass, domConstruct, domGeometry,
kernel, keys, lang, has, topic, focus, manager, ready,
_Widget, _Container, _TemplatedMixin, _CssStateMixin, StackContainer, ContentPane, template){
/*=====
var _Widget = dijit._Widget;
var _Container = dijit._Container;
var _TemplatedMixin = dijit._TemplatedMixin;
var _CssStateMixin = dijit._CssStateMixin;
var StackContainer = dijit.layout.StackContainer;
var ContentPane = dijit.layout.ContentPane;
=====*/
// module:
// dijit/layout/AccordionContainer
// summary:
// Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time,
// and switching between panes is visualized by sliding the other panes up/down.
// Design notes:
//
// An AccordionContainer is a StackContainer, but each child (typically ContentPane)
// is wrapped in a _AccordionInnerContainer. This is hidden from the caller.
//
// The resulting markup will look like:
//
// <div class=dijitAccordionContainer>
// <div class=dijitAccordionInnerContainer> (one pane)
// <div class=dijitAccordionTitle> (title bar) ... </div>
// <div class=dijtAccordionChildWrapper> (content pane) </div>
// </div>
// </div>
//
// Normally the dijtAccordionChildWrapper is hidden for all but one child (the shown
// child), so the space for the content pane is all the title bars + the one dijtAccordionChildWrapper,
// which on claro has a 1px border plus a 2px bottom margin.
//
// During animation there are two dijtAccordionChildWrapper's shown, so we need
// to compensate for that.
var AccordionButton = declare("dijit.layout._AccordionButton", [_Widget, _TemplatedMixin, _CssStateMixin], {
// summary:
// The title bar to click to open up an accordion pane.
// Internal widget used by AccordionContainer.
// tags:
// private
templateString: template,
// label: String
// Title of the pane
label: "",
_setLabelAttr: {node: "titleTextNode", type: "innerHTML" },
// title: String
// Tooltip that appears on hover
title: "",
_setTitleAttr: {node: "titleTextNode", type: "attribute", attribute: "title"},
// iconClassAttr: String
// CSS class for icon to left of label
iconClassAttr: "",
_setIconClassAttr: { node: "iconNode", type: "class" },
baseClass: "dijitAccordionTitle",
getParent: function(){
// summary:
// Returns the AccordionContainer parent.
// tags:
// private
return this.parent;
},
buildRendering: function(){
this.inherited(arguments);
var titleTextNodeId = this.id.replace(' ','_');
domAttr.set(this.titleTextNode, "id", titleTextNodeId+"_title");
this.focusNode.setAttribute("aria-labelledby", domAttr.get(this.titleTextNode, "id"));
dom.setSelectable(this.domNode, false);
},
getTitleHeight: function(){
// summary:
// Returns the height of the title dom node.
return domGeometry.getMarginSize(this.domNode).h; // Integer
},
// TODO: maybe the parent should set these methods directly rather than forcing the code
// into the button widget?
_onTitleClick: function(){
// summary:
// Callback when someone clicks my title.
var parent = this.getParent();
parent.selectChild(this.contentWidget, true);
focus.focus(this.focusNode);
},
_onTitleKeyPress: function(/*Event*/ evt){
return this.getParent()._onKeyPress(evt, this.contentWidget);
},
_setSelectedAttr: function(/*Boolean*/ isSelected){
this._set("selected", isSelected);
this.focusNode.setAttribute("aria-expanded", isSelected);
this.focusNode.setAttribute("aria-selected", isSelected);
this.focusNode.setAttribute("tabIndex", isSelected ? "0" : "-1");
}
});
var AccordionInnerContainer = declare("dijit.layout._AccordionInnerContainer", [_Widget, _CssStateMixin], {
// summary:
// Internal widget placed as direct child of AccordionContainer.containerNode.
// When other widgets are added as children to an AccordionContainer they are wrapped in
// this widget.
/*=====
// buttonWidget: Function || String
// Class to use to instantiate title
// (Wish we didn't have a separate widget for just the title but maintaining it
// for backwards compatibility, is it worth it?)
buttonWidget: null,
=====*/
/*=====
// contentWidget: dijit._Widget
// Pointer to the real child widget
contentWidget: null,
=====*/
baseClass: "dijitAccordionInnerContainer",
// tell nested layout widget that we will take care of sizing
isLayoutContainer: true,
buildRendering: function(){
// Builds a template like:
// <div class=dijitAccordionInnerContainer>
// Button
// <div class=dijitAccordionChildWrapper>
// ContentPane
// </div>
// </div>
// Create wrapper div, placed where the child is now
this.domNode = domConstruct.place("<div class='" + this.baseClass +
"' role='presentation'>", this.contentWidget.domNode, "after");
// wrapper div's first child is the button widget (ie, the title bar)
var child = this.contentWidget,
cls = lang.isString(this.buttonWidget) ? lang.getObject(this.buttonWidget) : this.buttonWidget;
this.button = child._buttonWidget = (new cls({
contentWidget: child,
label: child.title,
title: child.tooltip,
dir: child.dir,
lang: child.lang,
textDir: child.textDir,
iconClass: child.iconClass,
id: child.id + "_button",
parent: this.parent
})).placeAt(this.domNode);
// and then the actual content widget (changing it from prior-sibling to last-child),
// wrapped by a <div class=dijitAccordionChildWrapper>
this.containerNode = domConstruct.place("<div class='dijitAccordionChildWrapper' style='display:none'>", this.domNode);
domConstruct.place(this.contentWidget.domNode, this.containerNode);
},
postCreate: function(){
this.inherited(arguments);
// Map changes in content widget's title etc. to changes in the button
var button = this.button;
this._contentWidgetWatches = [
this.contentWidget.watch('title', lang.hitch(this, function(name, oldValue, newValue){
button.set("label", newValue);
})),
this.contentWidget.watch('tooltip', lang.hitch(this, function(name, oldValue, newValue){
button.set("title", newValue);
})),
this.contentWidget.watch('iconClass', lang.hitch(this, function(name, oldValue, newValue){
button.set("iconClass", newValue);
}))
];
},
_setSelectedAttr: function(/*Boolean*/ isSelected){
this._set("selected", isSelected);
this.button.set("selected", isSelected);
if(isSelected){
var cw = this.contentWidget;
if(cw.onSelected){ cw.onSelected(); }
}
},
startup: function(){
// Called by _Container.addChild()
this.contentWidget.startup();
},
destroy: function(){
this.button.destroyRecursive();
array.forEach(this._contentWidgetWatches || [], function(w){ w.unwatch(); });
delete this.contentWidget._buttonWidget;
delete this.contentWidget._wrapperWidget;
this.inherited(arguments);
},
destroyDescendants: function(/*Boolean*/ preserveDom){
// since getChildren isn't working for me, have to code this manually
this.contentWidget.destroyRecursive(preserveDom);
}
});
var AccordionContainer = declare("dijit.layout.AccordionContainer", StackContainer, {
// summary:
// Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time,
// and switching between panes is visualized by sliding the other panes up/down.
// example:
// | <div data-dojo-type="dijit.layout.AccordionContainer">
// | <div data-dojo-type="dijit.layout.ContentPane" title="pane 1">
// | </div>
// | <div data-dojo-type="dijit.layout.ContentPane" title="pane 2">
// | <p>This is some text</p>
// | </div>
// | </div>
// duration: Integer
// Amount of time (in ms) it takes to slide panes
duration: manager.defaultDuration,
// buttonWidget: [const] String
// The name of the widget used to display the title of each pane
buttonWidget: AccordionButton,
/*=====
// _verticalSpace: Number
// Pixels of space available for the open pane
// (my content box size minus the cumulative size of all the title bars)
_verticalSpace: 0,
=====*/
baseClass: "dijitAccordionContainer",
buildRendering: function(){
this.inherited(arguments);
this.domNode.style.overflow = "hidden"; // TODO: put this in dijit.css
this.domNode.setAttribute("role", "tablist"); // TODO: put this in template
},
startup: function(){
if(this._started){ return; }
this.inherited(arguments);
if(this.selectedChildWidget){
var style = this.selectedChildWidget.containerNode.style;
style.display = "";
style.overflow = "auto";
this.selectedChildWidget._wrapperWidget.set("selected", true);
}
},
layout: function(){
// Implement _LayoutWidget.layout() virtual method.
// Set the height of the open pane based on what room remains.
var openPane = this.selectedChildWidget;
if(!openPane){ return;}
// space taken up by title, plus wrapper div (with border/margin) for open pane
var wrapperDomNode = openPane._wrapperWidget.domNode,
wrapperDomNodeMargin = domGeometry.getMarginExtents(wrapperDomNode),
wrapperDomNodePadBorder = domGeometry.getPadBorderExtents(wrapperDomNode),
wrapperContainerNode = openPane._wrapperWidget.containerNode,
wrapperContainerNodeMargin = domGeometry.getMarginExtents(wrapperContainerNode),
wrapperContainerNodePadBorder = domGeometry.getPadBorderExtents(wrapperContainerNode),
mySize = this._contentBox;
// get cumulative height of all the unselected title bars
var totalCollapsedHeight = 0;
array.forEach(this.getChildren(), function(child){
if(child != openPane){
// Using domGeometry.getMarginSize() rather than domGeometry.position() since claro has 1px bottom margin
// to separate accordion panes. Not sure that works perfectly, it's probably putting a 1px
// margin below the bottom pane (even though we don't want one).
totalCollapsedHeight += domGeometry.getMarginSize(child._wrapperWidget.domNode).h;
}
});
this._verticalSpace = mySize.h - totalCollapsedHeight - wrapperDomNodeMargin.h
- wrapperDomNodePadBorder.h - wrapperContainerNodeMargin.h - wrapperContainerNodePadBorder.h
- openPane._buttonWidget.getTitleHeight();
// Memo size to make displayed child
this._containerContentBox = {
h: this._verticalSpace,
w: this._contentBox.w - wrapperDomNodeMargin.w - wrapperDomNodePadBorder.w
- wrapperContainerNodeMargin.w - wrapperContainerNodePadBorder.w
};
if(openPane){
openPane.resize(this._containerContentBox);
}
},
_setupChild: function(child){
// Overrides _LayoutWidget._setupChild().
// Put wrapper widget around the child widget, showing title
child._wrapperWidget = AccordionInnerContainer({
contentWidget: child,
buttonWidget: this.buttonWidget,
id: child.id + "_wrapper",
dir: child.dir,
lang: child.lang,
textDir: child.textDir,
parent: this
});
this.inherited(arguments);
},
addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
if(this._started){
// Adding a child to a started Accordion is complicated because children have
// wrapper widgets. Default code path (calling this.inherited()) would add
// the new child inside another child's wrapper.
// First add in child as a direct child of this AccordionContainer
var refNode = this.containerNode;
if(insertIndex && typeof insertIndex == "number"){
var children = _Widget.prototype.getChildren.call(this); // get wrapper panes
if(children && children.length >= insertIndex){
refNode = children[insertIndex-1].domNode;
insertIndex = "after";
}
}
domConstruct.place(child.domNode, refNode, insertIndex);
if(!child._started){
child.startup();
}
// Then stick the wrapper widget around the child widget
this._setupChild(child);
// Code below copied from StackContainer
topic.publish(this.id+"-addChild", child, insertIndex); // publish
this.layout();
if(!this.selectedChildWidget){
this.selectChild(child);
}
}else{
// We haven't been started yet so just add in the child widget directly,
// and the wrapper will be created on startup()
this.inherited(arguments);
}
},
removeChild: function(child){
// Overrides _LayoutWidget.removeChild().
// Destroy wrapper widget first, before StackContainer.getChildren() call.
// Replace wrapper widget with true child widget (ContentPane etc.).
// This step only happens if the AccordionContainer has been started; otherwise there's no wrapper.
if(child._wrapperWidget){
domConstruct.place(child.domNode, child._wrapperWidget.domNode, "after");
child._wrapperWidget.destroy();
delete child._wrapperWidget;
}
domClass.remove(child.domNode, "dijitHidden");
this.inherited(arguments);
},
getChildren: function(){
// Overrides _Container.getChildren() to return content panes rather than internal AccordionInnerContainer panes
return array.map(this.inherited(arguments), function(child){
return child.declaredClass == "dijit.layout._AccordionInnerContainer" ? child.contentWidget : child;
}, this);
},
destroy: function(){
if(this._animation){
this._animation.stop();
}
array.forEach(this.getChildren(), function(child){
// If AccordionContainer has been started, then each child has a wrapper widget which
// also needs to be destroyed.
if(child._wrapperWidget){
child._wrapperWidget.destroy();
}else{
child.destroyRecursive();
}
});
this.inherited(arguments);
},
_showChild: function(child){
// Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
child._wrapperWidget.containerNode.style.display="block";
return this.inherited(arguments);
},
_hideChild: function(child){
// Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
child._wrapperWidget.containerNode.style.display="none";
this.inherited(arguments);
},
_transition: function(/*dijit._Widget?*/ newWidget, /*dijit._Widget?*/ oldWidget, /*Boolean*/ animate){
// Overrides StackContainer._transition() to provide sliding of title bars etc.
if(has("ie") < 8){
// workaround animation bugs by not animating; not worth supporting animation for IE6 & 7
animate = false;
}
if(this._animation){
// there's an in-progress animation. speedily end it so we can do the newly requested one
this._animation.stop(true);
delete this._animation;
}
var self = this;
if(newWidget){
newWidget._wrapperWidget.set("selected", true);
var d = this._showChild(newWidget); // prepare widget to be slid in
// Size the new widget, in case this is the first time it's being shown,
// or I have been resized since the last time it was shown.
// Note that page must be visible for resizing to work.
if(this.doLayout && newWidget.resize){
newWidget.resize(this._containerContentBox);
}
}
if(oldWidget){
oldWidget._wrapperWidget.set("selected", false);
if(!animate){
this._hideChild(oldWidget);
}
}
if(animate){
var newContents = newWidget._wrapperWidget.containerNode,
oldContents = oldWidget._wrapperWidget.containerNode;
// During the animation we will be showing two dijitAccordionChildWrapper nodes at once,
// which on claro takes up 4px extra space (compared to stable AccordionContainer).
// Have to compensate for that by immediately shrinking the pane being closed.
var wrapperContainerNode = newWidget._wrapperWidget.containerNode,
wrapperContainerNodeMargin = domGeometry.getMarginExtents(wrapperContainerNode),
wrapperContainerNodePadBorder = domGeometry.getPadBorderExtents(wrapperContainerNode),
animationHeightOverhead = wrapperContainerNodeMargin.h + wrapperContainerNodePadBorder.h;
oldContents.style.height = (self._verticalSpace - animationHeightOverhead) + "px";
this._animation = new fx.Animation({
node: newContents,
duration: this.duration,
curve: [1, this._verticalSpace - animationHeightOverhead - 1],
onAnimate: function(value){
value = Math.floor(value); // avoid fractional values
newContents.style.height = value + "px";
oldContents.style.height = (self._verticalSpace - animationHeightOverhead - value) + "px";
},
onEnd: function(){
delete self._animation;
newContents.style.height = "auto";
oldWidget._wrapperWidget.containerNode.style.display = "none";
oldContents.style.height = "auto";
self._hideChild(oldWidget);
}
});
this._animation.onStop = this._animation.onEnd;
this._animation.play();
}
return d; // If child has an href, promise that fires when the widget has finished loading
},
// note: we are treating the container as controller here
_onKeyPress: function(/*Event*/ e, /*dijit._Widget*/ fromTitle){
// summary:
// Handle keypress events
// description:
// This is called from a handler on AccordionContainer.domNode
// (setup in StackContainer), and is also called directly from
// the click handler for accordion labels
if(this.disabled || e.altKey || !(fromTitle || e.ctrlKey)){
return;
}
var c = e.charOrCode;
if((fromTitle && (c == keys.LEFT_ARROW || c == keys.UP_ARROW)) ||
(e.ctrlKey && c == keys.PAGE_UP)){
this._adjacent(false)._buttonWidget._onTitleClick();
event.stop(e);
}else if((fromTitle && (c == keys.RIGHT_ARROW || c == keys.DOWN_ARROW)) ||
(e.ctrlKey && (c == keys.PAGE_DOWN || c == keys.TAB))){
this._adjacent(true)._buttonWidget._onTitleClick();
event.stop(e);
}
}
});
// Back compat w/1.6, remove for 2.0
if(!kernel.isAsync){
ready(0, function(){
var requires = ["dijit/layout/AccordionPane"];
require(requires); // use indirection so modules not rolled into a build
});
}
// For monkey patching
AccordionContainer._InnerContainer = AccordionInnerContainer;
AccordionContainer._Button = AccordionButton;
return AccordionContainer;
});
},
'dijit/layout/SplitContainer':function(){
define("dijit/layout/SplitContainer", [
"dojo/_base/array", // array.forEach array.indexOf array.some
"dojo/cookie", // cookie
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"dojo/dom-class", // domClass.add
"dojo/dom-construct", // domConstruct.create domConstruct.destroy
"dojo/dom-geometry", // domGeometry.marginBox domGeometry.position
"dojo/dom-style", // domStyle.style
"dojo/_base/event", // event.stop
"dojo/_base/kernel", // kernel.deprecated
"dojo/_base/lang", // lang.extend lang.hitch
"dojo/on",
"dojo/_base/sniff", // has("mozilla")
"dojo/_base/window", // win.doc.createElement win.doc.documentElement
"../registry", // registry.getUniqueId()
"../_WidgetBase",
"./_LayoutWidget"
], function(array, cookie, declare, dom, domClass, domConstruct, domGeometry, domStyle,
event, kernel, lang, on, has, win, registry, _WidgetBase, _LayoutWidget){
/*=====
var _WidgetBase = dijit._WidgetBase;
var _LayoutWidget = dijit.layout._LayoutWidget;
=====*/
// module:
// dijit/layout/SplitContainer
// summary:
// Deprecated. Use `dijit.layout.BorderContainer` instead.
//
// FIXME: make it prettier
// FIXME: active dragging upwards doesn't always shift other bars (direction calculation is wrong in this case)
// FIXME: sizeWidth should be a CSS attribute (at 7 because css wants it to be 7 until we fix to css)
//
// These arguments can be specified for the children of a SplitContainer.
// Since any widget can be specified as a SplitContainer child, mix them
// into the base widget class. (This is a hack, but it's effective.)
lang.extend(_WidgetBase, {
// sizeMin: [deprecated] Integer
// Deprecated. Parameter for children of `dijit.layout.SplitContainer`.
// Minimum size (width or height) of a child of a SplitContainer.
// The value is relative to other children's sizeShare properties.
sizeMin: 10,
// sizeShare: [deprecated] Integer
// Deprecated. Parameter for children of `dijit.layout.SplitContainer`.
// Size (width or height) of a child of a SplitContainer.
// The value is relative to other children's sizeShare properties.
// For example, if there are two children and each has sizeShare=10, then
// each takes up 50% of the available space.
sizeShare: 10
});
return declare("dijit.layout.SplitContainer", _LayoutWidget, {
// summary:
// Deprecated. Use `dijit.layout.BorderContainer` instead.
// description:
// A Container widget with sizing handles in-between each child.
// Contains multiple children widgets, all of which are displayed side by side
// (either horizontally or vertically); there's a bar between each of the children,
// and you can adjust the relative size of each child by dragging the bars.
//
// You must specify a size (width and height) for the SplitContainer.
// tags:
// deprecated
constructor: function(){
kernel.deprecated("dijit.layout.SplitContainer is deprecated", "use BorderContainer with splitter instead", 2.0);
},
// activeSizing: Boolean
// If true, the children's size changes as you drag the bar;
// otherwise, the sizes don't change until you drop the bar (by mouse-up)
activeSizing: false,
// sizerWidth: Integer
// Size in pixels of the bar between each child
sizerWidth: 7,
// orientation: String
// either 'horizontal' or vertical; indicates whether the children are
// arranged side-by-side or up/down.
orientation: 'horizontal',
// persist: Boolean
// Save splitter positions in a cookie
persist: true,
baseClass: "dijitSplitContainer",
postMixInProperties: function(){
this.inherited("postMixInProperties",arguments);
this.isHorizontal = (this.orientation == 'horizontal');
},
postCreate: function(){
this.inherited(arguments);
this.sizers = [];
// overflow has to be explicitly hidden for splitContainers using gekko (trac #1435)
// to keep other combined css classes from inadvertantly making the overflow visible
if(has("mozilla")){
this.domNode.style.overflow = '-moz-scrollbars-none'; // hidden doesn't work
}
// create the fake dragger
if(typeof this.sizerWidth == "object"){
try{ //FIXME: do this without a try/catch
this.sizerWidth = parseInt(this.sizerWidth.toString());
}catch(e){ this.sizerWidth = 7; }
}
var sizer = win.doc.createElement('div');
this.virtualSizer = sizer;
sizer.style.position = 'relative';
// #1681: work around the dreaded 'quirky percentages in IE' layout bug
// If the splitcontainer's dimensions are specified in percentages, it
// will be resized when the virtualsizer is displayed in _showSizingLine
// (typically expanding its bounds unnecessarily). This happens because
// we use position: relative for .dijitSplitContainer.
// The workaround: instead of changing the display style attribute,
// switch to changing the zIndex (bring to front/move to back)
sizer.style.zIndex = 10;
sizer.className = this.isHorizontal ? 'dijitSplitContainerVirtualSizerH' : 'dijitSplitContainerVirtualSizerV';
this.domNode.appendChild(sizer);
dom.setSelectable(sizer, false);
},
destroy: function(){
delete this.virtualSizer;
if(this._ownconnects){
var h;
while(h = this._ownconnects.pop()){ h.remove(); }
}
this.inherited(arguments);
},
startup: function(){
if(this._started){ return; }
array.forEach(this.getChildren(), function(child, i, children){
// attach the children and create the draggers
this._setupChild(child);
if(i < children.length-1){
this._addSizer();
}
}, this);
if(this.persist){
this._restoreState();
}
this.inherited(arguments);
},
_setupChild: function(/*dijit._Widget*/ child){
this.inherited(arguments);
child.domNode.style.position = "absolute";
domClass.add(child.domNode, "dijitSplitPane");
},
_onSizerMouseDown: function(e){
if(e.target.id){
for(var i=0;i<this.sizers.length;i++){
if(this.sizers[i].id == e.target.id){
break;
}
}
if(i<this.sizers.length){
this.beginSizing(e,i);
}
}
},
_addSizer: function(index){
index = index === undefined ? this.sizers.length : index;
// TODO: use a template for this!!!
var sizer = win.doc.createElement('div');
sizer.id=registry.getUniqueId('dijit_layout_SplitterContainer_Splitter');
this.sizers.splice(index,0,sizer);
this.domNode.appendChild(sizer);
sizer.className = this.isHorizontal ? 'dijitSplitContainerSizerH' : 'dijitSplitContainerSizerV';
// add the thumb div
var thumb = win.doc.createElement('div');
thumb.className = 'thumb';
sizer.appendChild(thumb);
// FIXME: are you serious? why aren't we using mover start/stop combo?
this.connect(sizer, "onmousedown", '_onSizerMouseDown');
dom.setSelectable(sizer, false);
},
removeChild: function(widget){
// summary:
// Remove sizer, but only if widget is really our child and
// we have at least one sizer to throw away
if(this.sizers.length){
var i = array.indexOf(this.getChildren(), widget);
if(i != -1){
if(i == this.sizers.length){
i--;
}
domConstruct.destroy(this.sizers[i]);
this.sizers.splice(i,1);
}
}
// Remove widget and repaint
this.inherited(arguments);
if(this._started){
this.layout();
}
},
addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
// summary:
// Add a child widget to the container
// child:
// a widget to add
// insertIndex:
// postion in the "stack" to add the child widget
this.inherited(arguments);
if(this._started){
// Do the stuff that startup() does for each widget
var children = this.getChildren();
if(children.length > 1){
this._addSizer(insertIndex);
}
// and then reposition (ie, shrink) every pane to make room for the new guy
this.layout();
}
},
layout: function(){
// summary:
// Do layout of panels
// base class defines this._contentBox on initial creation and also
// on resize
this.paneWidth = this._contentBox.w;
this.paneHeight = this._contentBox.h;
var children = this.getChildren();
if(!children.length){ return; }
//
// calculate space
//
var space = this.isHorizontal ? this.paneWidth : this.paneHeight;
if(children.length > 1){
space -= this.sizerWidth * (children.length - 1);
}
//
// calculate total of SizeShare values
//
var outOf = 0;
array.forEach(children, function(child){
outOf += child.sizeShare;
});
//
// work out actual pixels per sizeshare unit
//
var pixPerUnit = space / outOf;
//
// set the SizeActual member of each pane
//
var totalSize = 0;
array.forEach(children.slice(0, children.length - 1), function(child){
var size = Math.round(pixPerUnit * child.sizeShare);
child.sizeActual = size;
totalSize += size;
});
children[children.length-1].sizeActual = space - totalSize;
//
// make sure the sizes are ok
//
this._checkSizes();
//
// now loop, positioning each pane and letting children resize themselves
//
var pos = 0;
var size = children[0].sizeActual;
this._movePanel(children[0], pos, size);
children[0].position = pos;
pos += size;
// if we don't have any sizers, our layout method hasn't been called yet
// so bail until we are called..TODO: REVISIT: need to change the startup
// algorithm to guaranteed the ordering of calls to layout method
if(!this.sizers){
return;
}
array.some(children.slice(1), function(child, i){
// error-checking
if(!this.sizers[i]){
return true;
}
// first we position the sizing handle before this pane
this._moveSlider(this.sizers[i], pos, this.sizerWidth);
this.sizers[i].position = pos;
pos += this.sizerWidth;
size = child.sizeActual;
this._movePanel(child, pos, size);
child.position = pos;
pos += size;
}, this);
},
_movePanel: function(panel, pos, size){
var box;
if(this.isHorizontal){
panel.domNode.style.left = pos + 'px'; // TODO: resize() takes l and t parameters too, don't need to set manually
panel.domNode.style.top = 0;
box = {w: size, h: this.paneHeight};
if(panel.resize){
panel.resize(box);
}else{
domGeometry.setMarginBox(panel.domNode, box);
}
}else{
panel.domNode.style.left = 0; // TODO: resize() takes l and t parameters too, don't need to set manually
panel.domNode.style.top = pos + 'px';
box = {w: this.paneWidth, h: size};
if(panel.resize){
panel.resize(box);
}else{
domGeometry.setMarginBox(panel.domNode, box);
}
}
},
_moveSlider: function(slider, pos, size){
if(this.isHorizontal){
slider.style.left = pos + 'px';
slider.style.top = 0;
domGeometry.setMarginBox(slider, { w: size, h: this.paneHeight });
}else{
slider.style.left = 0;
slider.style.top = pos + 'px';
domGeometry.setMarginBox(slider, { w: this.paneWidth, h: size });
}
},
_growPane: function(growth, pane){
if(growth > 0){
if(pane.sizeActual > pane.sizeMin){
if((pane.sizeActual - pane.sizeMin) > growth){
// stick all the growth in this pane
pane.sizeActual = pane.sizeActual - growth;
growth = 0;
}else{
// put as much growth in here as we can
growth -= pane.sizeActual - pane.sizeMin;
pane.sizeActual = pane.sizeMin;
}
}
}
return growth;
},
_checkSizes: function(){
var totalMinSize = 0;
var totalSize = 0;
var children = this.getChildren();
array.forEach(children, function(child){
totalSize += child.sizeActual;
totalMinSize += child.sizeMin;
});
// only make adjustments if we have enough space for all the minimums
if(totalMinSize <= totalSize){
var growth = 0;
array.forEach(children, function(child){
if(child.sizeActual < child.sizeMin){
growth += child.sizeMin - child.sizeActual;
child.sizeActual = child.sizeMin;
}
});
if(growth > 0){
var list = this.isDraggingLeft ? children.reverse() : children;
array.forEach(list, function(child){
growth = this._growPane(growth, child);
}, this);
}
}else{
array.forEach(children, function(child){
child.sizeActual = Math.round(totalSize * (child.sizeMin / totalMinSize));
});
}
},
beginSizing: function(e, i){
var children = this.getChildren();
this.paneBefore = children[i];
this.paneAfter = children[i+1];
this.isSizing = true;
this.sizingSplitter = this.sizers[i];
if(!this.cover){
this.cover = domConstruct.create('div', {
style: {
position:'absolute',
zIndex:5,
top: 0,
left: 0,
width: "100%",
height: "100%"
}
}, this.domNode);
}else{
this.cover.style.zIndex = 5;
}
this.sizingSplitter.style.zIndex = 6;
// TODO: REVISIT - we want MARGIN_BOX and core hasn't exposed that yet (but can't we use it anyway if we pay attention? we do elsewhere.)
this.originPos = domGeometry.position(children[0].domNode, true);
var client, screen;
if(this.isHorizontal){
client = e.layerX || e.offsetX || 0;
screen = e.pageX;
this.originPos = this.originPos.x;
}else{
client = e.layerY || e.offsetY || 0;
screen = e.pageY;
this.originPos = this.originPos.y;
}
this.startPoint = this.lastPoint = screen;
this.screenToClientOffset = screen - client;
this.dragOffset = this.lastPoint - this.paneBefore.sizeActual - this.originPos - this.paneBefore.position;
if(!this.activeSizing){
this._showSizingLine();
}
//
// attach mouse events
//
this._ownconnects = [
on(win.doc.documentElement, "mousemove", lang.hitch(this, "changeSizing")),
on(win.doc.documentElement, "mouseup", lang.hitch(this, "endSizing"))
];
event.stop(e);
},
changeSizing: function(e){
if(!this.isSizing){ return; }
this.lastPoint = this.isHorizontal ? e.pageX : e.pageY;
this.movePoint();
if(this.activeSizing){
this._updateSize();
}else{
this._moveSizingLine();
}
event.stop(e);
},
endSizing: function(){
if(!this.isSizing){ return; }
if(this.cover){
this.cover.style.zIndex = -1;
}
if(!this.activeSizing){
this._hideSizingLine();
}
this._updateSize();
this.isSizing = false;
if(this.persist){
this._saveState(this);
}
var h;
while(h = this._ownconnects.pop()){ h.remove(); }
},
movePoint: function(){
// make sure lastPoint is a legal point to drag to
var p = this.lastPoint - this.screenToClientOffset;
var a = p - this.dragOffset;
a = this.legaliseSplitPoint(a);
p = a + this.dragOffset;
this.lastPoint = p + this.screenToClientOffset;
},
legaliseSplitPoint: function(a){
a += this.sizingSplitter.position;
this.isDraggingLeft = !!(a > 0);
if(!this.activeSizing){
var min = this.paneBefore.position + this.paneBefore.sizeMin;
if(a < min){
a = min;
}
var max = this.paneAfter.position + (this.paneAfter.sizeActual - (this.sizerWidth + this.paneAfter.sizeMin));
if(a > max){
a = max;
}
}
a -= this.sizingSplitter.position;
this._checkSizes();
return a;
},
_updateSize: function(){
//FIXME: sometimes this.lastPoint is NaN
var pos = this.lastPoint - this.dragOffset - this.originPos;
var start_region = this.paneBefore.position;
var end_region = this.paneAfter.position + this.paneAfter.sizeActual;
this.paneBefore.sizeActual = pos - start_region;
this.paneAfter.position = pos + this.sizerWidth;
this.paneAfter.sizeActual = end_region - this.paneAfter.position;
array.forEach(this.getChildren(), function(child){
child.sizeShare = child.sizeActual;
});
if(this._started){
this.layout();
}
},
_showSizingLine: function(){
this._moveSizingLine();
domGeometry.setMarginBox(this.virtualSizer,
this.isHorizontal ? { w: this.sizerWidth, h: this.paneHeight } : { w: this.paneWidth, h: this.sizerWidth });
this.virtualSizer.style.display = 'block';
},
_hideSizingLine: function(){
this.virtualSizer.style.display = 'none';
},
_moveSizingLine: function(){
var pos = (this.lastPoint - this.startPoint) + this.sizingSplitter.position;
domStyle.set(this.virtualSizer,(this.isHorizontal ? "left" : "top"),pos+"px");
// this.virtualSizer.style[ this.isHorizontal ? "left" : "top" ] = pos + 'px'; // FIXME: remove this line if the previous is better
},
_getCookieName: function(i){
return this.id + "_" + i;
},
_restoreState: function(){
array.forEach(this.getChildren(), function(child, i){
var cookieName = this._getCookieName(i);
var cookieValue = cookie(cookieName);
if(cookieValue){
var pos = parseInt(cookieValue);
if(typeof pos == "number"){
child.sizeShare = pos;
}
}
}, this);
},
_saveState: function(){
if(!this.persist){
return;
}
array.forEach(this.getChildren(), function(child, i){
cookie(this._getCookieName(i), child.sizeShare, {expires:365});
}, this);
}
});
});
},
'url:dijit/templates/Calendar.html':"<table cellspacing=\"0\" cellpadding=\"0\" class=\"dijitCalendarContainer\" role=\"grid\" aria-labelledby=\"${id}_mddb ${id}_year\">\n\t<thead>\n\t\t<tr class=\"dijitReset dijitCalendarMonthContainer\" valign=\"top\">\n\t\t\t<th class='dijitReset dijitCalendarArrow' data-dojo-attach-point=\"decrementMonth\">\n\t\t\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitCalendarIncrementControl dijitCalendarDecrease\" role=\"presentation\"/>\n\t\t\t\t<span data-dojo-attach-point=\"decreaseArrowNode\" class=\"dijitA11ySideArrow\">-</span>\n\t\t\t</th>\n\t\t\t<th class='dijitReset' colspan=\"5\">\n\t\t\t\t<div data-dojo-attach-point=\"monthNode\">\n\t\t\t\t</div>\n\t\t\t</th>\n\t\t\t<th class='dijitReset dijitCalendarArrow' data-dojo-attach-point=\"incrementMonth\">\n\t\t\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitCalendarIncrementControl dijitCalendarIncrease\" role=\"presentation\"/>\n\t\t\t\t<span data-dojo-attach-point=\"increaseArrowNode\" class=\"dijitA11ySideArrow\">+</span>\n\t\t\t</th>\n\t\t</tr>\n\t\t<tr>\n\t\t\t${!dayCellsHtml}\n\t\t</tr>\n\t</thead>\n\t<tbody data-dojo-attach-point=\"dateRowsNode\" data-dojo-attach-event=\"onclick: _onDayClick\" class=\"dijitReset dijitCalendarBodyContainer\">\n\t\t\t${!dateRowsHtml}\n\t</tbody>\n\t<tfoot class=\"dijitReset dijitCalendarYearContainer\">\n\t\t<tr>\n\t\t\t<td class='dijitReset' valign=\"top\" colspan=\"7\" role=\"presentation\">\n\t\t\t\t<div class=\"dijitCalendarYearLabel\">\n\t\t\t\t\t<span data-dojo-attach-point=\"previousYearLabelNode\" class=\"dijitInline dijitCalendarPreviousYear\" role=\"button\"></span>\n\t\t\t\t\t<span data-dojo-attach-point=\"currentYearLabelNode\" class=\"dijitInline dijitCalendarSelectedYear\" role=\"button\" id=\"${id}_year\"></span>\n\t\t\t\t\t<span data-dojo-attach-point=\"nextYearLabelNode\" class=\"dijitInline dijitCalendarNextYear\" role=\"button\"></span>\n\t\t\t\t</div>\n\t\t\t</td>\n\t\t</tr>\n\t</tfoot>\n</table>\n",
'dijit/form/_AutoCompleterMixin':function(){
define([
"dojo/_base/connect", // keys keys.SHIFT
"dojo/data/util/filter", // patternToRegExp
"dojo/_base/declare", // declare
"dojo/_base/Deferred", // Deferred.when
"dojo/dom-attr", // domAttr.get
"dojo/_base/event", // event.stop
"dojo/keys",
"dojo/_base/lang", // lang.clone lang.hitch
"dojo/query", // query
"dojo/regexp", // regexp.escapeString
"dojo/_base/sniff", // has("ie")
"dojo/string", // string.substitute
"dojo/_base/window", // win.doc.selection.createRange
"./DataList",
"../registry", // registry.byId
"./_TextBoxMixin" // defines _TextBoxMixin.selectInputText
], function(connect, filter, declare, Deferred, domAttr, event, keys, lang, query, regexp, has, string, win,
DataList, registry, _TextBoxMixin){
// module:
// dijit/form/_AutoCompleterMixin
// summary:
// A mixin that implements the base functionality for `dijit.form.ComboBox`/`dijit.form.FilteringSelect`
return declare("dijit.form._AutoCompleterMixin", null, {
// summary:
// A mixin that implements the base functionality for `dijit.form.ComboBox`/`dijit.form.FilteringSelect`
// description:
// All widgets that mix in dijit.form._AutoCompleterMixin must extend `dijit.form._FormValueWidget`.
// tags:
// protected
// item: Object
// This is the item returned by the dojo.data.store implementation that
// provides the data for this ComboBox, it's the currently selected item.
item: null,
// pageSize: Integer
// Argument to data provider.
// Specifies number of search results per page (before hitting "next" button)
pageSize: Infinity,
// store: [const] dojo.store.api.Store
// Reference to data provider object used by this ComboBox
store: null,
// fetchProperties: Object
// Mixin to the store's fetch.
// For example, to set the sort order of the ComboBox menu, pass:
// | { sort: [{attribute:"name",descending: true}] }
// To override the default queryOptions so that deep=false, do:
// | { queryOptions: {ignoreCase: true, deep: false} }
fetchProperties:{},
// query: Object
// A query that can be passed to 'store' to initially filter the items,
// before doing further filtering based on `searchAttr` and the key.
// Any reference to the `searchAttr` is ignored.
query: {},
// autoComplete: Boolean
// If user types in a partial string, and then tab out of the `<input>` box,
// automatically copy the first entry displayed in the drop down list to
// the `<input>` field
autoComplete: true,
// highlightMatch: String
// One of: "first", "all" or "none".
//
// If the ComboBox/FilteringSelect opens with the search results and the searched
// string can be found, it will be highlighted. If set to "all"
// then will probably want to change `queryExpr` parameter to '*${0}*'
//
// Highlighting is only performed when `labelType` is "text", so as to not
// interfere with any HTML markup an HTML label might contain.
highlightMatch: "first",
// searchDelay: Integer
// Delay in milliseconds between when user types something and we start
// searching based on that value
searchDelay: 100,
// searchAttr: String
// Search for items in the data store where this attribute (in the item)
// matches what the user typed
searchAttr: "name",
// labelAttr: String?
// The entries in the drop down list come from this attribute in the
// dojo.data items.
// If not specified, the searchAttr attribute is used instead.
labelAttr: "",
// labelType: String
// Specifies how to interpret the labelAttr in the data store items.
// Can be "html" or "text".
labelType: "text",
// queryExpr: String
// This specifies what query ComboBox/FilteringSelect sends to the data store,
// based on what the user has typed. Changing this expression will modify
// whether the drop down shows only exact matches, a "starting with" match,
// etc. Use it in conjunction with highlightMatch.
// dojo.data query expression pattern.
// `${0}` will be substituted for the user text.
// `*` is used for wildcards.
// `${0}*` means "starts with", `*${0}*` means "contains", `${0}` means "is"
queryExpr: "${0}*",
// ignoreCase: Boolean
// Set true if the ComboBox/FilteringSelect should ignore case when matching possible items
ignoreCase: true,
// Flags to _HasDropDown to limit height of drop down to make it fit in viewport
maxHeight: -1,
// For backwards compatibility let onClick events propagate, even clicks on the down arrow button
_stopClickEvents: false,
_getCaretPos: function(/*DomNode*/ element){
// khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22
var pos = 0;
if(typeof(element.selectionStart) == "number"){
// FIXME: this is totally borked on Moz < 1.3. Any recourse?
pos = element.selectionStart;
}else if(has("ie")){
// in the case of a mouse click in a popup being handled,
// then the win.doc.selection is not the textarea, but the popup
// var r = win.doc.selection.createRange();
// hack to get IE 6 to play nice. What a POS browser.
var tr = win.doc.selection.createRange().duplicate();
var ntr = element.createTextRange();
tr.move("character",0);
ntr.move("character",0);
try{
// If control doesn't have focus, you get an exception.
// Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes).
// There appears to be no workaround for this - googled for quite a while.
ntr.setEndPoint("EndToEnd", tr);
pos = String(ntr.text).replace(/\r/g,"").length;
}catch(e){
// If focus has shifted, 0 is fine for caret pos.
}
}
return pos;
},
_setCaretPos: function(/*DomNode*/ element, /*Number*/ location){
location = parseInt(location);
_TextBoxMixin.selectInputText(element, location, location);
},
_setDisabledAttr: function(/*Boolean*/ value){
// Additional code to set disabled state of ComboBox node.
// Overrides _FormValueWidget._setDisabledAttr() or ValidationTextBox._setDisabledAttr().
this.inherited(arguments);
this.domNode.setAttribute("aria-disabled", value);
},
_abortQuery: function(){
// stop in-progress query
if(this.searchTimer){
clearTimeout(this.searchTimer);
this.searchTimer = null;
}
if(this._fetchHandle){
if(this._fetchHandle.cancel){
this._cancelingQuery = true;
this._fetchHandle.cancel();
this._cancelingQuery = false;
}
this._fetchHandle = null;
}
},
_onInput: function(/*Event*/ evt){
// summary:
// Handles paste events
this.inherited(arguments);
if(evt.charOrCode == 229){ // IME or cut/paste event
this._onKey(evt);
}
},
_onKey: function(/*Event*/ evt){
// summary:
// Handles keyboard events
var key = evt.charOrCode;
// except for cutting/pasting case - ctrl + x/v
if(evt.altKey || ((evt.ctrlKey || evt.metaKey) && (key != 'x' && key != 'v')) || key == keys.SHIFT){
return; // throw out weird key combinations and spurious events
}
var doSearch = false;
var pw = this.dropDown;
var highlighted = null;
this._prev_key_backspace = false;
this._abortQuery();
// _HasDropDown will do some of the work:
// 1. when drop down is not yet shown:
// - if user presses the down arrow key, call loadDropDown()
// 2. when drop down is already displayed:
// - on ESC key, call closeDropDown()
// - otherwise, call dropDown.handleKey() to process the keystroke
this.inherited(arguments);
if(this._opened){
highlighted = pw.getHighlightedOption();
}
switch(key){
case keys.PAGE_DOWN:
case keys.DOWN_ARROW:
case keys.PAGE_UP:
case keys.UP_ARROW:
// Keystroke caused ComboBox_menu to move to a different item.
// Copy new item to <input> box.
if(this._opened){
this._announceOption(highlighted);
}
event.stop(evt);
break;
case keys.ENTER:
// prevent submitting form if user presses enter. Also
// prevent accepting the value if either Next or Previous
// are selected
if(highlighted){
// only stop event on prev/next
if(highlighted == pw.nextButton){
this._nextSearch(1);
event.stop(evt);
break;
}else if(highlighted == pw.previousButton){
this._nextSearch(-1);
event.stop(evt);
break;
}
}else{
// Update 'value' (ex: KY) according to currently displayed text
this._setBlurValue(); // set value if needed
this._setCaretPos(this.focusNode, this.focusNode.value.length); // move cursor to end and cancel highlighting
}
// default case:
// if enter pressed while drop down is open, or for FilteringSelect,
// if we are in the middle of a query to convert a directly typed in value to an item,
// prevent submit
if(this._opened || this._fetchHandle){
event.stop(evt);
}
// fall through
case keys.TAB:
var newvalue = this.get('displayedValue');
// if the user had More Choices selected fall into the
// _onBlur handler
if(pw && (
newvalue == pw._messages["previousMessage"] ||
newvalue == pw._messages["nextMessage"])
){
break;
}
if(highlighted){
this._selectOption(highlighted);
}
// fall through
case keys.ESCAPE:
if(this._opened){
this._lastQuery = null; // in case results come back later
this.closeDropDown();
}
break;
case ' ':
if(highlighted){
// user is effectively clicking a choice in the drop down menu
event.stop(evt);
this._selectOption(highlighted);
this.closeDropDown();
}else{
// user typed a space into the input box, treat as normal character
doSearch = true;
}
break;
case keys.DELETE:
case keys.BACKSPACE:
this._prev_key_backspace = true;
doSearch = true;
break;
default:
// Non char keys (F1-F12 etc..) shouldn't open list.
// Ascii characters and IME input (Chinese, Japanese etc.) should.
//IME input produces keycode == 229.
doSearch = typeof key == 'string' || key == 229;
}
if(doSearch){
// need to wait a tad before start search so that the event
// bubbles through DOM and we have value visible
this.item = undefined; // undefined means item needs to be set
this.searchTimer = setTimeout(lang.hitch(this, "_startSearchFromInput"),1);
}
},
_autoCompleteText: function(/*String*/ text){
// summary:
// Fill in the textbox with the first item from the drop down
// list, and highlight the characters that were
// auto-completed. For example, if user typed "CA" and the
// drop down list appeared, the textbox would be changed to
// "California" and "ifornia" would be highlighted.
var fn = this.focusNode;
// IE7: clear selection so next highlight works all the time
_TextBoxMixin.selectInputText(fn, fn.value.length);
// does text autoComplete the value in the textbox?
var caseFilter = this.ignoreCase? 'toLowerCase' : 'substr';
if(text[caseFilter](0).indexOf(this.focusNode.value[caseFilter](0)) == 0){
var cpos = this.autoComplete ? this._getCaretPos(fn) : fn.value.length;
// only try to extend if we added the last character at the end of the input
if((cpos+1) > fn.value.length){
// only add to input node as we would overwrite Capitalisation of chars
// actually, that is ok
fn.value = text;//.substr(cpos);
// visually highlight the autocompleted characters
_TextBoxMixin.selectInputText(fn, cpos);
}
}else{
// text does not autoComplete; replace the whole value and highlight
fn.value = text;
_TextBoxMixin.selectInputText(fn);
}
},
_openResultList: function(/*Object*/ results, /*Object*/ query, /*Object*/ options){
// summary:
// Callback when a search completes.
// description:
// 1. generates drop-down list and calls _showResultList() to display it
// 2. if this result list is from user pressing "more choices"/"previous choices"
// then tell screen reader to announce new option
this._fetchHandle = null;
if( this.disabled ||
this.readOnly ||
(query[this.searchAttr] !== this._lastQuery) // TODO: better way to avoid getting unwanted notify
){
return;
}
var wasSelected = this.dropDown.getHighlightedOption();
this.dropDown.clearResultList();
if(!results.length && options.start == 0){ // if no results and not just the previous choices button
this.closeDropDown();
return;
}
// Fill in the textbox with the first item from the drop down list,
// and highlight the characters that were auto-completed. For
// example, if user typed "CA" and the drop down list appeared, the
// textbox would be changed to "California" and "ifornia" would be
// highlighted.
var nodes = this.dropDown.createOptions(
results,
options,
lang.hitch(this, "_getMenuLabelFromItem")
);
// show our list (only if we have content, else nothing)
this._showResultList();
// #4091:
// tell the screen reader that the paging callback finished by
// shouting the next choice
if(options.direction){
if(1 == options.direction){
this.dropDown.highlightFirstOption();
}else if(-1 == options.direction){
this.dropDown.highlightLastOption();
}
if(wasSelected){
this._announceOption(this.dropDown.getHighlightedOption());
}
}else if(this.autoComplete && !this._prev_key_backspace
// when the user clicks the arrow button to show the full list,
// startSearch looks for "*".
// it does not make sense to autocomplete
// if they are just previewing the options available.
&& !/^[*]+$/.test(query[this.searchAttr].toString())){
this._announceOption(nodes[1]); // 1st real item
}
},
_showResultList: function(){
// summary:
// Display the drop down if not already displayed, or if it is displayed, then
// reposition it if necessary (reposition may be necessary if drop down's height changed).
this.closeDropDown(true);
this.openDropDown();
this.domNode.setAttribute("aria-expanded", "true");
},
loadDropDown: function(/*Function*/ /*===== callback =====*/){
// Overrides _HasDropDown.loadDropDown().
// This is called when user has pressed button icon or pressed the down arrow key
// to open the drop down.
this._startSearchAll();
},
isLoaded: function(){
// signal to _HasDropDown that it needs to call loadDropDown() to load the
// drop down asynchronously before displaying it
return false;
},
closeDropDown: function(){
// Overrides _HasDropDown.closeDropDown(). Closes the drop down (assuming that it's open).
// This method is the callback when the user types ESC or clicking
// the button icon while the drop down is open. It's also called by other code.
this._abortQuery();
if(this._opened){
this.inherited(arguments);
this.domNode.setAttribute("aria-expanded", "false");
this.focusNode.removeAttribute("aria-activedescendant");
}
},
_setBlurValue: function(){
// if the user clicks away from the textbox OR tabs away, set the
// value to the textbox value
// #4617:
// if value is now more choices or previous choices, revert
// the value
var newvalue = this.get('displayedValue');
var pw = this.dropDown;
if(pw && (
newvalue == pw._messages["previousMessage"] ||
newvalue == pw._messages["nextMessage"]
)
){
this._setValueAttr(this._lastValueReported, true);
}else if(typeof this.item == "undefined"){
// Update 'value' (ex: KY) according to currently displayed text
this.item = null;
this.set('displayedValue', newvalue);
}else{
if(this.value != this._lastValueReported){
this._handleOnChange(this.value, true);
}
this._refreshState();
}
},
_setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
// summary:
// Set the displayed valued in the input box, and the hidden value
// that gets submitted, based on a dojo.data store item.
// description:
// Users shouldn't call this function; they should be calling
// set('item', value)
// tags:
// private
var value = '';
if(item){
if(!displayedValue){
displayedValue = this.store._oldAPI ? // remove getValue() for 2.0 (old dojo.data API)
this.store.getValue(item, this.searchAttr) : item[this.searchAttr];
}
value = this._getValueField() != this.searchAttr ? this.store.getIdentity(item) : displayedValue;
}
this.set('value', value, priorityChange, displayedValue, item);
},
_announceOption: function(/*Node*/ node){
// summary:
// a11y code that puts the highlighted option in the textbox.
// This way screen readers will know what is happening in the
// menu.
if(!node){
return;
}
// pull the text value from the item attached to the DOM node
var newValue;
if(node == this.dropDown.nextButton ||
node == this.dropDown.previousButton){
newValue = node.innerHTML;
this.item = undefined;
this.value = '';
}else{
newValue = (this.store._oldAPI ? // remove getValue() for 2.0 (old dojo.data API)
this.store.getValue(node.item, this.searchAttr) : node.item[this.searchAttr]).toString();
this.set('item', node.item, false, newValue);
}
// get the text that the user manually entered (cut off autocompleted text)
this.focusNode.value = this.focusNode.value.substring(0, this._lastInput.length);
// set up ARIA activedescendant
this.focusNode.setAttribute("aria-activedescendant", domAttr.get(node, "id"));
// autocomplete the rest of the option to announce change
this._autoCompleteText(newValue);
},
_selectOption: function(/*DomNode*/ target){
// summary:
// Menu callback function, called when an item in the menu is selected.
this.closeDropDown();
if(target){
this._announceOption(target);
}
this._setCaretPos(this.focusNode, this.focusNode.value.length);
this._handleOnChange(this.value, true);
},
_startSearchAll: function(){
this._startSearch('');
},
_startSearchFromInput: function(){
this._startSearch(this.focusNode.value.replace(/([\\\*\?])/g, "\\$1"));
},
_getQueryString: function(/*String*/ text){
return string.substitute(this.queryExpr, [text]);
},
_startSearch: function(/*String*/ key){
// summary:
// Starts a search for elements matching key (key=="" means to return all items),
// and calls _openResultList() when the search completes, to display the results.
if(!this.dropDown){
var popupId = this.id + "_popup",
dropDownConstructor = lang.isString(this.dropDownClass) ?
lang.getObject(this.dropDownClass, false) : this.dropDownClass;
this.dropDown = new dropDownConstructor({
onChange: lang.hitch(this, this._selectOption),
id: popupId,
dir: this.dir,
textDir: this.textDir
});
this.focusNode.removeAttribute("aria-activedescendant");
this.textbox.setAttribute("aria-owns",popupId); // associate popup with textbox
}
this._lastInput = key; // Store exactly what was entered by the user.
// Setup parameters to be passed to store.query().
// Create a new query to prevent accidentally querying for a hidden
// value from FilteringSelect's keyField
var query = lang.clone(this.query); // #5970
var options = {
start: 0,
count: this.pageSize,
queryOptions: { // remove for 2.0
ignoreCase: this.ignoreCase,
deep: true
}
};
lang.mixin(options, this.fetchProperties);
// Generate query
var qs = this._getQueryString(key), q;
if(this.store._oldAPI){
// remove this branch for 2.0
q = qs;
}else{
// Query on searchAttr is a regex for benefit of dojo.store.Memory,
// but with a toString() method to help dojo.store.JsonRest.
// Search string like "Co*" converted to regex like /^Co.*$/i.
q = filter.patternToRegExp(qs, this.ignoreCase);
q.toString = function(){ return qs; };
}
this._lastQuery = query[this.searchAttr] = q;
// Function to run the query, wait for the results, and then call _openResultList()
var _this = this,
startQuery = function(){
var resPromise = _this._fetchHandle = _this.store.query(query, options);
Deferred.when(resPromise, function(res){
_this._fetchHandle = null;
res.total = resPromise.total;
_this._openResultList(res, query, options);
}, function(err){
_this._fetchHandle = null;
if(!_this._cancelingQuery){ // don't treat canceled query as an error
console.error(_this.declaredClass + ' ' + err.toString());
_this.closeDropDown();
}
});
};
// #5970: set _lastQuery, *then* start the timeout
// otherwise, if the user types and the last query returns before the timeout,
// _lastQuery won't be set and their input gets rewritten
this.searchTimer = setTimeout(lang.hitch(this, function(query, _this){
this.searchTimer = null;
startQuery();
// Setup method to handle clicking next/previous buttons to page through results
this._nextSearch = this.dropDown.onPage = function(direction){
options.start += options.count * direction;
// tell callback the direction of the paging so the screen
// reader knows which menu option to shout
options.direction = direction;
startQuery();
_this.focus();
};
}, query, this), this.searchDelay);
},
_getValueField: function(){
// summary:
// Helper for postMixInProperties() to set this.value based on data inlined into the markup.
// Returns the attribute name in the item (in dijit.form._ComboBoxDataStore) to use as the value.
return this.searchAttr;
},
//////////// INITIALIZATION METHODS ///////////////////////////////////////
constructor: function(){
this.query={};
this.fetchProperties={};
},
postMixInProperties: function(){
if(!this.store){
var srcNodeRef = this.srcNodeRef;
var list = this.list;
if(list){
this.store = registry.byId(list);
}else{
// if user didn't specify store, then assume there are option tags
this.store = new DataList({}, srcNodeRef);
}
// if there is no value set and there is an option list, set
// the value to the first value to be consistent with native Select
// Firefox and Safari set value
// IE6 and Opera set selectedIndex, which is automatically set
// by the selected attribute of an option tag
// IE6 does not set value, Opera sets value = selectedIndex
if(!("value" in this.params)){
var item = (this.item = this.store.fetchSelectedItem());
if(item){
var valueField = this._getValueField();
// remove getValue() for 2.0 (old dojo.data API)
this.value = this.store._oldAPI ? this.store.getValue(item, valueField) : item[valueField];
}
}
}
this.inherited(arguments);
},
postCreate: function(){
// summary:
// Subclasses must call this method from their postCreate() methods
// tags:
// protected
// find any associated label element and add to ComboBox node.
var label=query('label[for="'+this.id+'"]');
if(label.length){
label[0].id = (this.id+"_label");
this.domNode.setAttribute("aria-labelledby", label[0].id);
}
this.inherited(arguments);
},
_getMenuLabelFromItem: function(/*Item*/ item){
var label = this.labelFunc(item, this.store),
labelType = this.labelType;
// If labelType is not "text" we don't want to screw any markup ot whatever.
if(this.highlightMatch != "none" && this.labelType == "text" && this._lastInput){
label = this.doHighlight(label, this._escapeHtml(this._lastInput));
labelType = "html";
}
return {html: labelType == "html", label: label};
},
doHighlight: function(/*String*/ label, /*String*/ find){
// summary:
// Highlights the string entered by the user in the menu. By default this
// highlights the first occurrence found. Override this method
// to implement your custom highlighting.
// tags:
// protected
var
// Add (g)lobal modifier when this.highlightMatch == "all" and (i)gnorecase when this.ignoreCase == true
modifiers = (this.ignoreCase ? "i" : "") + (this.highlightMatch == "all" ? "g" : ""),
i = this.queryExpr.indexOf("${0}");
find = regexp.escapeString(find); // escape regexp special chars
return this._escapeHtml(label).replace(
// prepend ^ when this.queryExpr == "${0}*" and append $ when this.queryExpr == "*${0}"
new RegExp((i == 0 ? "^" : "") + "("+ find +")" + (i == (this.queryExpr.length - 4) ? "$" : ""), modifiers),
'<span class="dijitComboBoxHighlightMatch">$1</span>'
); // returns String, (almost) valid HTML (entities encoded)
},
_escapeHtml: function(/*String*/ str){
// TODO Should become dojo.html.entities(), when exists use instead
// summary:
// Adds escape sequences for special characters in XML: &<>"'
str = String(str).replace(/&/gm, "&amp;").replace(/</gm, "&lt;")
.replace(/>/gm, "&gt;").replace(/"/gm, "&quot;"); //balance"
return str; // string
},
reset: function(){
// Overrides the _FormWidget.reset().
// Additionally reset the .item (to clean up).
this.item = null;
this.inherited(arguments);
},
labelFunc: function(/*item*/ item, /*dojo.store.api.Store*/ store){
// summary:
// Computes the label to display based on the dojo.data store item.
// returns:
// The label that the ComboBox should display
// tags:
// private
// Use toString() because XMLStore returns an XMLItem whereas this
// method is expected to return a String (#9354).
// Remove getValue() for 2.0 (old dojo.data API)
return (store._oldAPI ? store.getValue(item, this.labelAttr || this.searchAttr) :
item[this.labelAttr || this.searchAttr]).toString(); // String
},
_setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange, /*String?*/ displayedValue, /*item?*/ item){
// summary:
// Hook so set('value', value) works.
// description:
// Sets the value of the select.
this._set("item", item||null); // value not looked up in store
if(!value){ value = ''; } // null translates to blank
this.inherited(arguments);
},
_setTextDirAttr: function(/*String*/ textDir){
// summary:
// Setter for textDir, needed for the dropDown's textDir update.
// description:
// Users shouldn't call this function; they should be calling
// set('textDir', value)
// tags:
// private
this.inherited(arguments);
// update the drop down also (_ComboBoxMenuMixin)
if(this.dropDown){
this.dropDown._set("textDir", textDir);
}
}
});
});
},
'url:dijit/templates/ColorPalette.html':"<div class=\"dijitInline dijitColorPalette\">\n\t<table dojoAttachPoint=\"paletteTableNode\" class=\"dijitPaletteTable\" cellSpacing=\"0\" cellPadding=\"0\" role=\"grid\">\n\t\t<tbody data-dojo-attach-point=\"gridNode\"></tbody>\n\t</table>\n</div>\n",
'url:dijit/layout/templates/_ScrollingTabControllerButton.html':"<div data-dojo-attach-event=\"onclick:_onClick\">\n\t<div role=\"presentation\" class=\"dijitTabInnerDiv\" data-dojo-attach-point=\"innerDiv,focusNode\">\n\t\t<div role=\"presentation\" class=\"dijitTabContent dijitButtonContents\" data-dojo-attach-point=\"tabContent\">\n\t\t\t<img role=\"presentation\" alt=\"\" src=\"${_blankGif}\" class=\"dijitTabStripIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t\t\t<span data-dojo-attach-point=\"containerNode,titleNode\" class=\"dijitButtonText\"></span>\n\t\t</div>\n\t</div>\n</div>",
'dijit/form/MappedTextBox':function(){
define([
"dojo/_base/declare", // declare
"dojo/dom-construct", // domConstruct.place
"./ValidationTextBox"
], function(declare, domConstruct, ValidationTextBox){
/*=====
var ValidationTextBox = dijit.form.ValidationTextBox;
=====*/
// module:
// dijit/form/MappedTextBox
// summary:
// A dijit.form.ValidationTextBox subclass which provides a base class for widgets that have
// a visible formatted display value, and a serializable
// value in a hidden input field which is actually sent to the server.
return declare("dijit.form.MappedTextBox", ValidationTextBox, {
// summary:
// A dijit.form.ValidationTextBox subclass which provides a base class for widgets that have
// a visible formatted display value, and a serializable
// value in a hidden input field which is actually sent to the server.
// description:
// The visible display may
// be locale-dependent and interactive. The value sent to the server is stored in a hidden
// input field which uses the `name` attribute declared by the original widget. That value sent
// to the server is defined by the dijit.form.MappedTextBox.serialize method and is typically
// locale-neutral.
// tags:
// protected
postMixInProperties: function(){
this.inherited(arguments);
// we want the name attribute to go to the hidden <input>, not the displayed <input>,
// so override _FormWidget.postMixInProperties() setting of nameAttrSetting
this.nameAttrSetting = "";
},
// Override default behavior to assign name to focusNode
_setNameAttr: null,
serialize: function(val /*=====, options =====*/){
// summary:
// Overridable function used to convert the get('value') result to a canonical
// (non-localized) string. For example, will print dates in ISO format, and
// numbers the same way as they are represented in javascript.
// val: anything
// options: Object?
// tags:
// protected extension
return val.toString ? val.toString() : ""; // String
},
toString: function(){
// summary:
// Returns widget as a printable string using the widget's value
// tags:
// protected
var val = this.filter(this.get('value')); // call filter in case value is nonstring and filter has been customized
return val != null ? (typeof val == "string" ? val : this.serialize(val, this.constraints)) : ""; // String
},
validate: function(){
// Overrides `dijit.form.TextBox.validate`
this.valueNode.value = this.toString();
return this.inherited(arguments);
},
buildRendering: function(){
// Overrides `dijit._TemplatedMixin.buildRendering`
this.inherited(arguments);
// Create a hidden <input> node with the serialized value used for submit
// (as opposed to the displayed value).
// Passing in name as markup rather than calling domConstruct.create() with an attrs argument
// to make query(input[name=...]) work on IE. (see #8660)
this.valueNode = domConstruct.place("<input type='hidden'" + (this.name ? " name='" + this.name.replace(/'/g, "&quot;") + "'" : "") + "/>", this.textbox, "after");
},
reset: function(){
// Overrides `dijit.form.ValidationTextBox.reset` to
// reset the hidden textbox value to ''
this.valueNode.value = '';
this.inherited(arguments);
}
});
});
},
'dijit/form/ComboBoxMixin':function(){
define([
"dojo/_base/declare", // declare
"dojo/_base/Deferred",
"dojo/_base/kernel", // kernel.deprecated
"dojo/_base/lang", // lang.mixin
"dojo/store/util/QueryResults", // dojo.store.util.QueryResults
"./_AutoCompleterMixin",
"./_ComboBoxMenu",
"../_HasDropDown",
"dojo/text!./templates/DropDownBox.html"
], function(declare, Deferred, kernel, lang, QueryResults, _AutoCompleterMixin, _ComboBoxMenu, _HasDropDown, template){
/*=====
var _AutoCompleterMixin = dijit.form._AutoCompleterMixin;
var _ComboBoxMenu = dijit.form._ComboBoxMenu;
var _HasDropDown = dijit._HasDropDown;
=====*/
// module:
// dijit/form/ComboBoxMixin
// summary:
// Provides main functionality of ComboBox widget
return declare("dijit.form.ComboBoxMixin", [_HasDropDown, _AutoCompleterMixin], {
// summary:
// Provides main functionality of ComboBox widget
// dropDownClass: [protected extension] Function String
// Dropdown widget class used to select a date/time.
// Subclasses should specify this.
dropDownClass: _ComboBoxMenu,
// hasDownArrow: Boolean
// Set this textbox to have a down arrow button, to display the drop down list.
// Defaults to true.
hasDownArrow: true,
templateString: template,
baseClass: "dijitTextBox dijitComboBox",
/*=====
// store: [const] dojo.store.api.Store || dojo.data.api.Read
// Reference to data provider object used by this ComboBox.
//
// Should be dojo.store.api.Store, but dojo.data.api.Read supported
// for backwards compatibility.
store: null,
=====*/
// Set classes like dijitDownArrowButtonHover depending on
// mouse action over button node
cssStateNodes: {
"_buttonNode": "dijitDownArrowButton"
},
_setHasDownArrowAttr: function(/*Boolean*/ val){
this._set("hasDownArrow", val);
this._buttonNode.style.display = val ? "" : "none";
},
_showResultList: function(){
// hide the tooltip
this.displayMessage("");
this.inherited(arguments);
},
_setStoreAttr: function(store){
// For backwards-compatibility, accept dojo.data store in addition to dojo.store.store. Remove in 2.0.
if(!store.get){
lang.mixin(store, {
_oldAPI: true,
get: function(id){
// summary:
// Retrieves an object by it's identity. This will trigger a fetchItemByIdentity.
// Like dojo.store.DataStore.get() except returns native item.
var deferred = new Deferred();
this.fetchItemByIdentity({
identity: id,
onItem: function(object){
deferred.resolve(object);
},
onError: function(error){
deferred.reject(error);
}
});
return deferred.promise;
},
query: function(query, options){
// summary:
// Queries the store for objects. Like dojo.store.DataStore.query()
// except returned Deferred contains array of native items.
var deferred = new Deferred(function(){ fetchHandle.abort && fetchHandle.abort(); });
var fetchHandle = this.fetch(lang.mixin({
query: query,
onBegin: function(count){
deferred.total = count;
},
onComplete: function(results){
deferred.resolve(results);
},
onError: function(error){
deferred.reject(error);
}
}, options));
return QueryResults(deferred);
}
});
}
this._set("store", store);
},
postMixInProperties: function(){
// Since _setValueAttr() depends on this.store, _setStoreAttr() needs to execute first.
// Unfortunately, without special code, it ends up executing second.
if(this.params.store){
this._setStoreAttr(this.params.store);
}
this.inherited(arguments);
// User may try to access this.store.getValue() etc. in a custom labelFunc() function.
// It's not available with the new data store for handling inline <option> tags, so add it.
if(!this.params.store){
var clazz = this.declaredClass;
lang.mixin(this.store, {
getValue: function(item, attr){
kernel.deprecated(clazz + ".store.getValue(item, attr) is deprecated for builtin store. Use item.attr directly", "", "2.0");
return item[attr];
},
getLabel: function(item){
kernel.deprecated(clazz + ".store.getLabel(item) is deprecated for builtin store. Use item.label directly", "", "2.0");
return item.name;
},
fetch: function(args){
kernel.deprecated(clazz + ".store.fetch() is deprecated for builtin store.", "Use store.query()", "2.0");
var shim = ["dojo/data/ObjectStore"]; // indirection so it doesn't get rolled into a build
require(shim, lang.hitch(this, function(ObjectStore){
new ObjectStore({objectStore: this}).fetch(args);
}));
}
});
}
}
});
});
},
'dijit/form/_TextBoxMixin':function(){
define([
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom", // dom.byId
"dojo/_base/event", // event.stop
"dojo/keys", // keys.ALT keys.CAPS_LOCK keys.CTRL keys.META keys.SHIFT
"dojo/_base/lang", // lang.mixin
".." // for exporting dijit._setSelectionRange, dijit.selectInputText
], function(array, declare, dom, event, keys, lang, dijit){
// module:
// dijit/form/_TextBoxMixin
// summary:
// A mixin for textbox form input widgets
var _TextBoxMixin = declare("dijit.form._TextBoxMixin", null, {
// summary:
// A mixin for textbox form input widgets
// trim: Boolean
// Removes leading and trailing whitespace if true. Default is false.
trim: false,
// uppercase: Boolean
// Converts all characters to uppercase if true. Default is false.
uppercase: false,
// lowercase: Boolean
// Converts all characters to lowercase if true. Default is false.
lowercase: false,
// propercase: Boolean
// Converts the first character of each word to uppercase if true.
propercase: false,
// maxLength: String
// HTML INPUT tag maxLength declaration.
maxLength: "",
// selectOnClick: [const] Boolean
// If true, all text will be selected when focused with mouse
selectOnClick: false,
// placeHolder: String
// Defines a hint to help users fill out the input field (as defined in HTML 5).
// This should only contain plain text (no html markup).
placeHolder: "",
_getValueAttr: function(){
// summary:
// Hook so get('value') works as we like.
// description:
// For `dijit.form.TextBox` this basically returns the value of the <input>.
//
// For `dijit.form.MappedTextBox` subclasses, which have both
// a "displayed value" and a separate "submit value",
// This treats the "displayed value" as the master value, computing the
// submit value from it via this.parse().
return this.parse(this.get('displayedValue'), this.constraints);
},
_setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
// summary:
// Hook so set('value', ...) works.
//
// description:
// Sets the value of the widget to "value" which can be of
// any type as determined by the widget.
//
// value:
// The visual element value is also set to a corresponding,
// but not necessarily the same, value.
//
// formattedValue:
// If specified, used to set the visual element value,
// otherwise a computed visual value is used.
//
// priorityChange:
// If true, an onChange event is fired immediately instead of
// waiting for the next blur event.
var filteredValue;
if(value !== undefined){
// TODO: this is calling filter() on both the display value and the actual value.
// I added a comment to the filter() definition about this, but it should be changed.
filteredValue = this.filter(value);
if(typeof formattedValue != "string"){
if(filteredValue !== null && ((typeof filteredValue != "number") || !isNaN(filteredValue))){
formattedValue = this.filter(this.format(filteredValue, this.constraints));
}else{ formattedValue = ''; }
}
}
if(formattedValue != null && formattedValue != undefined && ((typeof formattedValue) != "number" || !isNaN(formattedValue)) && this.textbox.value != formattedValue){
this.textbox.value = formattedValue;
this._set("displayedValue", this.get("displayedValue"));
}
if(this.textDir == "auto"){
this.applyTextDir(this.focusNode, formattedValue);
}
this.inherited(arguments, [filteredValue, priorityChange]);
},
// displayedValue: String
// For subclasses like ComboBox where the displayed value
// (ex: Kentucky) and the serialized value (ex: KY) are different,
// this represents the displayed value.
//
// Setting 'displayedValue' through set('displayedValue', ...)
// updates 'value', and vice-versa. Otherwise 'value' is updated
// from 'displayedValue' periodically, like onBlur etc.
//
// TODO: move declaration to MappedTextBox?
// Problem is that ComboBox references displayedValue,
// for benefit of FilteringSelect.
displayedValue: "",
_getDisplayedValueAttr: function(){
// summary:
// Hook so get('displayedValue') works.
// description:
// Returns the displayed value (what the user sees on the screen),
// after filtering (ie, trimming spaces etc.).
//
// For some subclasses of TextBox (like ComboBox), the displayed value
// is different from the serialized value that's actually
// sent to the server (see dijit.form.ValidationTextBox.serialize)
// TODO: maybe we should update this.displayedValue on every keystroke so that we don't need
// this method
// TODO: this isn't really the displayed value when the user is typing
return this.filter(this.textbox.value);
},
_setDisplayedValueAttr: function(/*String*/ value){
// summary:
// Hook so set('displayedValue', ...) works.
// description:
// Sets the value of the visual element to the string "value".
// The widget value is also set to a corresponding,
// but not necessarily the same, value.
if(value === null || value === undefined){ value = '' }
else if(typeof value != "string"){ value = String(value) }
this.textbox.value = value;
// sets the serialized value to something corresponding to specified displayedValue
// (if possible), and also updates the textbox.value, for example converting "123"
// to "123.00"
this._setValueAttr(this.get('value'), undefined);
this._set("displayedValue", this.get('displayedValue'));
// textDir support
if(this.textDir == "auto"){
this.applyTextDir(this.focusNode, value);
}
},
format: function(value /*=====, constraints =====*/){
// summary:
// Replaceable function to convert a value to a properly formatted string.
// value: String
// constraints: Object
// tags:
// protected extension
return ((value == null || value == undefined) ? "" : (value.toString ? value.toString() : value));
},
parse: function(value /*=====, constraints =====*/){
// summary:
// Replaceable function to convert a formatted string to a value
// value: String
// constraints: Object
// tags:
// protected extension
return value; // String
},
_refreshState: function(){
// summary:
// After the user types some characters, etc., this method is
// called to check the field for validity etc. The base method
// in `dijit.form.TextBox` does nothing, but subclasses override.
// tags:
// protected
},
/*=====
onInput: function(event){
// summary:
// Connect to this function to receive notifications of various user data-input events.
// Return false to cancel the event and prevent it from being processed.
// event:
// keydown | keypress | cut | paste | input
// tags:
// callback
},
=====*/
onInput: function(){},
__skipInputEvent: false,
_onInput: function(){
// summary:
// Called AFTER the input event has happened
// set text direction according to textDir that was defined in creation
if(this.textDir == "auto"){
this.applyTextDir(this.focusNode, this.focusNode.value);
}
this._refreshState();
// In case someone is watch()'ing for changes to displayedValue
this._set("displayedValue", this.get("displayedValue"));
},
postCreate: function(){
// setting the value here is needed since value="" in the template causes "undefined"
// and setting in the DOM (instead of the JS object) helps with form reset actions
this.textbox.setAttribute("value", this.textbox.value); // DOM and JS values should be the same
this.inherited(arguments);
// normalize input events to reduce spurious event processing
// onkeydown: do not forward modifier keys
// set charOrCode to numeric keycode
// onkeypress: do not forward numeric charOrCode keys (already sent through onkeydown)
// onpaste & oncut: set charOrCode to 229 (IME)
// oninput: if primary event not already processed, set charOrCode to 229 (IME), else do not forward
var handleEvent = function(e){
var charCode = e.charOrCode || e.keyCode || 229;
if(e.type == "keydown"){
switch(charCode){ // ignore "state" keys
case keys.SHIFT:
case keys.ALT:
case keys.CTRL:
case keys.META:
case keys.CAPS_LOCK:
return;
default:
if(charCode >= 65 && charCode <= 90){ return; } // keydown for A-Z can be processed with keypress
}
}
if(e.type == "keypress" && typeof charCode != "string"){ return; }
if(e.type == "input"){
if(this.__skipInputEvent){ // duplicate event
this.__skipInputEvent = false;
return;
}
}else{
this.__skipInputEvent = true;
}
// create fake event to set charOrCode and to know if preventDefault() was called
var faux = lang.mixin({}, e, {
charOrCode: charCode,
wasConsumed: false,
preventDefault: function(){
faux.wasConsumed = true;
e.preventDefault();
},
stopPropagation: function(){ e.stopPropagation(); }
});
// give web page author a chance to consume the event
if(this.onInput(faux) === false){
event.stop(faux); // return false means stop
}
if(faux.wasConsumed){ return; } // if preventDefault was called
setTimeout(lang.hitch(this, "_onInput", faux), 0); // widget notification after key has posted
};
array.forEach([ "onkeydown", "onkeypress", "onpaste", "oncut", "oninput" ], function(event){
this.connect(this.textbox, event, handleEvent);
}, this);
},
_blankValue: '', // if the textbox is blank, what value should be reported
filter: function(val){
// summary:
// Auto-corrections (such as trimming) that are applied to textbox
// value on blur or form submit.
// description:
// For MappedTextBox subclasses, this is called twice
// - once with the display value
// - once the value as set/returned by set('value', ...)
// and get('value'), ex: a Number for NumberTextBox.
//
// In the latter case it does corrections like converting null to NaN. In
// the former case the NumberTextBox.filter() method calls this.inherited()
// to execute standard trimming code in TextBox.filter().
//
// TODO: break this into two methods in 2.0
//
// tags:
// protected extension
if(val === null){ return this._blankValue; }
if(typeof val != "string"){ return val; }
if(this.trim){
val = lang.trim(val);
}
if(this.uppercase){
val = val.toUpperCase();
}
if(this.lowercase){
val = val.toLowerCase();
}
if(this.propercase){
val = val.replace(/[^\s]+/g, function(word){
return word.substring(0,1).toUpperCase() + word.substring(1);
});
}
return val;
},
_setBlurValue: function(){
this._setValueAttr(this.get('value'), true);
},
_onBlur: function(e){
if(this.disabled){ return; }
this._setBlurValue();
this.inherited(arguments);
if(this._selectOnClickHandle){
this.disconnect(this._selectOnClickHandle);
}
},
_isTextSelected: function(){
return this.textbox.selectionStart == this.textbox.selectionEnd;
},
_onFocus: function(/*String*/ by){
if(this.disabled || this.readOnly){ return; }
// Select all text on focus via click if nothing already selected.
// Since mouse-up will clear the selection need to defer selection until after mouse-up.
// Don't do anything on focus by tabbing into the widget since there's no associated mouse-up event.
if(this.selectOnClick && by == "mouse"){
this._selectOnClickHandle = this.connect(this.domNode, "onmouseup", function(){
// Only select all text on first click; otherwise users would have no way to clear
// the selection.
this.disconnect(this._selectOnClickHandle);
// Check if the user selected some text manually (mouse-down, mouse-move, mouse-up)
// and if not, then select all the text
if(this._isTextSelected()){
_TextBoxMixin.selectInputText(this.textbox);
}
});
}
// call this.inherited() before refreshState(), since this.inherited() will possibly scroll the viewport
// (to scroll the TextBox into view), which will affect how _refreshState() positions the tooltip
this.inherited(arguments);
this._refreshState();
},
reset: function(){
// Overrides dijit._FormWidget.reset().
// Additionally resets the displayed textbox value to ''
this.textbox.value = '';
this.inherited(arguments);
},
_setTextDirAttr: function(/*String*/ textDir){
// summary:
// Setter for textDir.
// description:
// Users shouldn't call this function; they should be calling
// set('textDir', value)
// tags:
// private
// only if new textDir is different from the old one
// and on widgets creation.
if(!this._created
|| this.textDir != textDir){
this._set("textDir", textDir);
// so the change of the textDir will take place immediately.
this.applyTextDir(this.focusNode, this.focusNode.value);
}
}
});
_TextBoxMixin._setSelectionRange = dijit._setSelectionRange = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){
if(element.setSelectionRange){
element.setSelectionRange(start, stop);
}
};
_TextBoxMixin.selectInputText = dijit.selectInputText = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){
// summary:
// Select text in the input element argument, from start (default 0), to stop (default end).
// TODO: use functions in _editor/selection.js?
element = dom.byId(element);
if(isNaN(start)){ start = 0; }
if(isNaN(stop)){ stop = element.value ? element.value.length : 0; }
try{
element.focus();
_TextBoxMixin._setSelectionRange(element, start, stop);
}catch(e){ /* squelch random errors (esp. on IE) from unexpected focus changes or DOM nodes being hidden */ }
};
return _TextBoxMixin;
});
},
'dijit/form/SimpleTextarea':function(){
define("dijit/form/SimpleTextarea", [
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.add
"dojo/_base/sniff", // has("ie") has("opera")
"dojo/_base/window", // win.doc.selection win.doc.selection.createRange
"./TextBox"
], function(declare, domClass, has, win, TextBox){
/*=====
var TextBox = dijit.form.TextBox;
=====*/
// module:
// dijit/form/SimpleTextarea
// summary:
// A simple textarea that degrades, and responds to
// minimal LayoutContainer usage, and works with dijit.form.Form.
// Doesn't automatically size according to input, like Textarea.
return declare("dijit.form.SimpleTextarea", TextBox, {
// summary:
// A simple textarea that degrades, and responds to
// minimal LayoutContainer usage, and works with dijit.form.Form.
// Doesn't automatically size according to input, like Textarea.
//
// example:
// | <textarea data-dojo-type="dijit.form.SimpleTextarea" name="foo" value="bar" rows=30 cols=40></textarea>
//
// example:
// | new dijit.form.SimpleTextarea({ rows:20, cols:30 }, "foo");
baseClass: "dijitTextBox dijitTextArea",
// rows: Number
// The number of rows of text.
rows: "3",
// rows: Number
// The number of characters per line.
cols: "20",
templateString: "<textarea ${!nameAttrSetting} data-dojo-attach-point='focusNode,containerNode,textbox' autocomplete='off'></textarea>",
postMixInProperties: function(){
// Copy value from srcNodeRef, unless user specified a value explicitly (or there is no srcNodeRef)
// TODO: parser will handle this in 2.0
if(!this.value && this.srcNodeRef){
this.value = this.srcNodeRef.value;
}
this.inherited(arguments);
},
buildRendering: function(){
this.inherited(arguments);
if(has("ie") && this.cols){ // attribute selectors is not supported in IE6
domClass.add(this.textbox, "dijitTextAreaCols");
}
},
filter: function(/*String*/ value){
// Override TextBox.filter to deal with newlines... specifically (IIRC) this is for IE which writes newlines
// as \r\n instead of just \n
if(value){
value = value.replace(/\r/g,"");
}
return this.inherited(arguments);
},
_onInput: function(/*Event?*/ e){
// Override TextBox._onInput() to enforce maxLength restriction
if(this.maxLength){
var maxLength = parseInt(this.maxLength);
var value = this.textbox.value.replace(/\r/g,'');
var overflow = value.length - maxLength;
if(overflow > 0){
var textarea = this.textbox;
if(textarea.selectionStart){
var pos = textarea.selectionStart;
var cr = 0;
if(has("opera")){
cr = (this.textbox.value.substring(0,pos).match(/\r/g) || []).length;
}
this.textbox.value = value.substring(0,pos-overflow-cr)+value.substring(pos-cr);
textarea.setSelectionRange(pos-overflow, pos-overflow);
}else if(win.doc.selection){ //IE
textarea.focus();
var range = win.doc.selection.createRange();
// delete overflow characters
range.moveStart("character", -overflow);
range.text = '';
// show cursor
range.select();
}
}
}
this.inherited(arguments);
}
});
});
},
'url:dijit/layout/templates/_TabButton.html':"<div role=\"presentation\" data-dojo-attach-point=\"titleNode\" data-dojo-attach-event='onclick:onClick'>\n <div role=\"presentation\" class='dijitTabInnerDiv' data-dojo-attach-point='innerDiv'>\n <div role=\"presentation\" class='dijitTabContent' data-dojo-attach-point='tabContent'>\n \t<div role=\"presentation\" data-dojo-attach-point='focusNode'>\n\t\t <img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitTabButtonIcon\" data-dojo-attach-point='iconNode' />\n\t\t <span data-dojo-attach-point='containerNode' class='tabLabel'></span>\n\t\t <span class=\"dijitInline dijitTabCloseButton dijitTabCloseIcon\" data-dojo-attach-point='closeNode'\n\t\t \t\tdata-dojo-attach-event='onclick: onClickCloseButton' role=\"presentation\">\n\t\t <span data-dojo-attach-point='closeText' class='dijitTabCloseText'>[x]</span\n\t\t ></span>\n\t\t\t</div>\n </div>\n </div>\n</div>\n",
'dijit/PopupMenuItem':function(){
define([
"dojo/_base/declare", // declare
"dojo/dom-style", // domStyle.set
"dojo/query", // query
"dojo/_base/window", // win.body
"./registry", // registry.byNode
"./MenuItem",
"./hccss"
], function(declare, domStyle, query, win, registry, MenuItem){
/*=====
var MenuItem = dijit.MenuItem;
=====*/
// module:
// dijit/PopupMenuItem
// summary:
// An item in a Menu that spawn a drop down (usually a drop down menu)
return declare("dijit.PopupMenuItem", MenuItem, {
// summary:
// An item in a Menu that spawn a drop down (usually a drop down menu)
_fillContent: function(){
// summary:
// When Menu is declared in markup, this code gets the menu label and
// the popup widget from the srcNodeRef.
// description:
// srcNodeRefinnerHTML contains both the menu item text and a popup widget
// The first part holds the menu item text and the second part is the popup
// example:
// | <div data-dojo-type="dijit.PopupMenuItem">
// | <span>pick me</span>
// | <popup> ... </popup>
// | </div>
// tags:
// protected
if(this.srcNodeRef){
var nodes = query("*", this.srcNodeRef);
this.inherited(arguments, [nodes[0]]);
// save pointer to srcNode so we can grab the drop down widget after it's instantiated
this.dropDownContainer = this.srcNodeRef;
}
},
startup: function(){
if(this._started){ return; }
this.inherited(arguments);
// we didn't copy the dropdown widget from the this.srcNodeRef, so it's in no-man's
// land now. move it to win.doc.body.
if(!this.popup){
var node = query("[widgetId]", this.dropDownContainer)[0];
this.popup = registry.byNode(node);
}
win.body().appendChild(this.popup.domNode);
this.popup.startup();
this.popup.domNode.style.display="none";
if(this.arrowWrapper){
domStyle.set(this.arrowWrapper, "visibility", "");
}
this.focusNode.setAttribute("aria-haspopup", "true");
},
destroyDescendants: function(/*Boolean*/ preserveDom){
if(this.popup){
// Destroy the popup, unless it's already been destroyed. This can happen because
// the popup is a direct child of <body> even though it's logically my child.
if(!this.popup._destroyed){
this.popup.destroyRecursive(preserveDom);
}
delete this.popup;
}
this.inherited(arguments);
}
});
});
},
'dijit/_TimePicker':function(){
define([
"dojo/_base/array", // array.forEach
"dojo/date", // date.compare
"dojo/date/locale", // locale.format
"dojo/date/stamp", // stamp.fromISOString stamp.toISOString
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.add domClass.contains domClass.toggle
"dojo/dom-construct", // domConstruct.create
"dojo/_base/event", // event.stop
"dojo/_base/kernel", // deprecated
"dojo/keys", // keys
"dojo/_base/lang", // lang.mixin
"dojo/_base/sniff", // has("ie")
"dojo/query", // query
"dijit/typematic",
"./_Widget",
"./_TemplatedMixin",
"./form/_FormValueWidget",
"dojo/text!./templates/TimePicker.html"
], function(array, ddate, locale, stamp, declare, domClass, domConstruct, event, kernel, keys, lang, has, query,
typematic, _Widget, _TemplatedMixin, _FormValueWidget, template){
/*=====
var _Widget = dijit._Widget;
var _TemplatedMixin = dijit._TemplatedMixin;
var _FormValueWidget = dijit.form._FormValueWidget;
=====*/
// module:
// dijit/_TimePicker
// summary:
// A graphical time picker.
/*=====
declare(
"dijit._TimePicker.__Constraints",
locale.__FormatOptions,
{
// clickableIncrement: String
// See `dijit._TimePicker.clickableIncrement`
clickableIncrement: "T00:15:00",
// visibleIncrement: String
// See `dijit._TimePicker.visibleIncrement`
visibleIncrement: "T01:00:00",
// visibleRange: String
// See `dijit._TimePicker.visibleRange`
visibleRange: "T05:00:00"
}
);
=====*/
return declare("dijit._TimePicker", [_Widget, _TemplatedMixin], {
// summary:
// A graphical time picker.
// This widget is used internally by other widgets and is not available
// as a standalone widget due to lack of accessibility support.
templateString: template,
// baseClass: [protected] String
// The root className to use for the various states of this widget
baseClass: "dijitTimePicker",
// clickableIncrement: String
// ISO-8601 string representing the amount by which
// every clickable element in the time picker increases.
// Set in local time, without a time zone.
// Example: `T00:15:00` creates 15 minute increments
// Must divide dijit._TimePicker.visibleIncrement evenly
clickableIncrement: "T00:15:00",
// visibleIncrement: String
// ISO-8601 string representing the amount by which
// every element with a visible time in the time picker increases.
// Set in local time, without a time zone.
// Example: `T01:00:00` creates text in every 1 hour increment
visibleIncrement: "T01:00:00",
// visibleRange: String
// ISO-8601 string representing the range of this TimePicker.
// The TimePicker will only display times in this range.
// Example: `T05:00:00` displays 5 hours of options
visibleRange: "T05:00:00",
// value: String
// Date to display.
// Defaults to current time and date.
// Can be a Date object or an ISO-8601 string.
// If you specify the GMT time zone (`-01:00`),
// the time will be converted to the local time in the local time zone.
// Otherwise, the time is considered to be in the local time zone.
// If you specify the date and isDate is true, the date is used.
// Example: if your local time zone is `GMT -05:00`,
// `T10:00:00` becomes `T10:00:00-05:00` (considered to be local time),
// `T10:00:00-01:00` becomes `T06:00:00-05:00` (4 hour difference),
// `T10:00:00Z` becomes `T05:00:00-05:00` (5 hour difference between Zulu and local time)
// `yyyy-mm-ddThh:mm:ss` is the format to set the date and time
// Example: `2007-06-01T09:00:00`
value: new Date(),
_visibleIncrement:2,
_clickableIncrement:1,
_totalIncrements:10,
// constraints: dijit._TimePicker.__Constraints
// Specifies valid range of times (start time, end time)
constraints:{},
/*=====
serialize: function(val, options){
// summary:
// User overridable function used to convert the attr('value') result to a String
// val: Date
// The current value
// options: Object?
// tags:
// protected
},
=====*/
serialize: stamp.toISOString,
/*=====
// filterString: string
// The string to filter by
filterString: "",
=====*/
setValue: function(/*Date*/ value){
// summary:
// Deprecated. Used set('value') instead.
// tags:
// deprecated
kernel.deprecated("dijit._TimePicker:setValue() is deprecated. Use set('value', ...) instead.", "", "2.0");
this.set('value', value);
},
_setValueAttr: function(/*Date*/ date){
// summary:
// Hook so set('value', ...) works.
// description:
// Set the value of the TimePicker.
// Redraws the TimePicker around the new date.
// tags:
// protected
this._set("value", date);
this._showText();
},
_setFilterStringAttr: function(val){
// summary:
// Called by TimeTextBox to filter the values shown in my list
this._set("filterString", val);
this._showText();
},
isDisabledDate: function(/*===== dateObject, locale =====*/){
// summary:
// May be overridden to disable certain dates in the TimePicker e.g. `isDisabledDate=locale.isWeekend`
// dateObject: Date
// locale: String?
// type:
// extension
return false; // Boolean
},
_getFilteredNodes: function(/*number*/ start, /*number*/ maxNum, /*Boolean*/ before, /*DOMnode*/ lastNode){
// summary:
// Returns an array of nodes with the filter applied. At most maxNum nodes
// will be returned - but fewer may be returned as well. If the
// before parameter is set to true, then it will return the elements
// before the given index
// tags:
// private
var
nodes = [],
lastValue = lastNode ? lastNode.date : this._refDate,
n,
i = start,
max = this._maxIncrement + Math.abs(i),
chk = before ? -1 : 1,
dec = before ? 1 : 0,
inc = 1 - dec;
do{
i -= dec;
n = this._createOption(i);
if(n){
if((before && n.date > lastValue) || (!before && n.date < lastValue)){
break; // don't wrap
}
nodes[before ? "unshift" : "push"](n);
lastValue = n.date;
}
i += inc;
}while(nodes.length < maxNum && (i*chk) < max);
return nodes;
},
_showText: function(){
// summary:
// Displays the relevant choices in the drop down list
// tags:
// private
var fromIso = stamp.fromISOString;
this.timeMenu.innerHTML = "";
this._clickableIncrementDate=fromIso(this.clickableIncrement);
this._visibleIncrementDate=fromIso(this.visibleIncrement);
this._visibleRangeDate=fromIso(this.visibleRange);
// get the value of the increments and the range in seconds (since 00:00:00) to find out how many divs to create
var
sinceMidnight = function(/*Date*/ date){
return date.getHours() * 60 * 60 + date.getMinutes() * 60 + date.getSeconds();
},
clickableIncrementSeconds = sinceMidnight(this._clickableIncrementDate),
visibleIncrementSeconds = sinceMidnight(this._visibleIncrementDate),
visibleRangeSeconds = sinceMidnight(this._visibleRangeDate),
// round reference date to previous visible increment
time = (this.value || this.currentFocus).getTime();
this._refDate = new Date(time - time % (clickableIncrementSeconds*1000));
this._refDate.setFullYear(1970,0,1); // match parse defaults
// assume clickable increment is the smallest unit
this._clickableIncrement = 1;
// divide the visible range by the clickable increment to get the number of divs to create
// example: 10:00:00/00:15:00 -> display 40 divs
this._totalIncrements = visibleRangeSeconds / clickableIncrementSeconds;
// divide the visible increments by the clickable increments to get how often to display the time inline
// example: 01:00:00/00:15:00 -> display the time every 4 divs
this._visibleIncrement = visibleIncrementSeconds / clickableIncrementSeconds;
// divide the number of seconds in a day by the clickable increment in seconds to get the
// absolute max number of increments.
this._maxIncrement = (60 * 60 * 24) / clickableIncrementSeconds;
var
// Find the nodes we should display based on our filter.
// Limit to 10 nodes displayed as a half-hearted attempt to stop drop down from overlapping <input>.
count = Math.min(this._totalIncrements, 10),
after = this._getFilteredNodes(0, (count >> 1) + 1, false),
moreAfter = [],
estBeforeLength = count - after.length,
before = this._getFilteredNodes(0, estBeforeLength, true, after[0]);
if(before.length < estBeforeLength && after.length > 0){
moreAfter = this._getFilteredNodes(after.length, estBeforeLength - before.length, false, after[after.length-1]);
}
array.forEach(before.concat(after, moreAfter), function(n){ this.timeMenu.appendChild(n); }, this);
},
constructor: function(){
this.constraints = {}; // create instance object
},
postMixInProperties: function(){
this.inherited(arguments);
this._setConstraintsAttr(this.constraints); // this needs to happen now (and later) due to codependency on _set*Attr calls
},
_setConstraintsAttr: function(/* Object */ constraints){
// brings in visibleRange, increments, etc.
lang.mixin(this, constraints);
// locale needs the lang in the constraints as locale
if(!constraints.locale){
constraints.locale = this.lang;
}
},
postCreate: function(){
// assign typematic mouse listeners to the arrow buttons
this.connect(this.timeMenu, has("ie") ? "onmousewheel" : 'DOMMouseScroll', "_mouseWheeled");
this._connects.push(typematic.addMouseListener(this.upArrow, this, "_onArrowUp", 33, 250));
this._connects.push(typematic.addMouseListener(this.downArrow, this, "_onArrowDown", 33, 250));
this.inherited(arguments);
},
_buttonMouse: function(/*Event*/ e){
// summary:
// Handler for hover (and unhover) on up/down arrows
// tags:
// private
// in non-IE browser the "mouseenter" event will become "mouseover",
// but in IE it's still "mouseenter"
domClass.toggle(e.currentTarget, e.currentTarget == this.upArrow ? "dijitUpArrowHover" : "dijitDownArrowHover",
e.type == "mouseenter" || e.type == "mouseover");
},
_createOption: function(/*Number*/ index){
// summary:
// Creates a clickable time option
// tags:
// private
var date = new Date(this._refDate);
var incrementDate = this._clickableIncrementDate;
date.setHours(date.getHours() + incrementDate.getHours() * index,
date.getMinutes() + incrementDate.getMinutes() * index,
date.getSeconds() + incrementDate.getSeconds() * index);
if(this.constraints.selector == "time"){
date.setFullYear(1970,0,1); // make sure each time is for the same date
}
var dateString = locale.format(date, this.constraints);
if(this.filterString && dateString.toLowerCase().indexOf(this.filterString) !== 0){
// Doesn't match the filter - return null
return null;
}
var div = domConstruct.create("div", {"class": this.baseClass+"Item"});
div.date = date;
div.index = index;
domConstruct.create('div',{
"class": this.baseClass + "ItemInner",
innerHTML: dateString
}, div);
if(index%this._visibleIncrement<1 && index%this._visibleIncrement>-1){
domClass.add(div, this.baseClass+"Marker");
}else if(!(index%this._clickableIncrement)){
domClass.add(div, this.baseClass+"Tick");
}
if(this.isDisabledDate(date)){
// set disabled
domClass.add(div, this.baseClass+"ItemDisabled");
}
if(this.value && !ddate.compare(this.value, date, this.constraints.selector)){
div.selected = true;
domClass.add(div, this.baseClass+"ItemSelected");
if(domClass.contains(div, this.baseClass+"Marker")){
domClass.add(div, this.baseClass+"MarkerSelected");
}else{
domClass.add(div, this.baseClass+"TickSelected");
}
// Initially highlight the current value. User can change highlight by up/down arrow keys
// or mouse movement.
this._highlightOption(div, true);
}
return div;
},
_onOptionSelected: function(/*Object*/ tgt){
// summary:
// Called when user clicks an option in the drop down list
// tags:
// private
var tdate = tgt.target.date || tgt.target.parentNode.date;
if(!tdate || this.isDisabledDate(tdate)){ return; }
this._highlighted_option = null;
this.set('value', tdate);
this.onChange(tdate);
},
onChange: function(/*Date*/ /*===== time =====*/){
// summary:
// Notification that a time was selected. It may be the same as the previous value.
// tags:
// public
},
_highlightOption: function(/*node*/ node, /*Boolean*/ highlight){
// summary:
// Turns on/off highlight effect on a node based on mouse out/over event
// tags:
// private
if(!node){return;}
if(highlight){
if(this._highlighted_option){
this._highlightOption(this._highlighted_option, false);
}
this._highlighted_option = node;
}else if(this._highlighted_option !== node){
return;
}else{
this._highlighted_option = null;
}
domClass.toggle(node, this.baseClass+"ItemHover", highlight);
if(domClass.contains(node, this.baseClass+"Marker")){
domClass.toggle(node, this.baseClass+"MarkerHover", highlight);
}else{
domClass.toggle(node, this.baseClass+"TickHover", highlight);
}
},
onmouseover: function(/*Event*/ e){
// summary:
// Handler for onmouseover event
// tags:
// private
this._keyboardSelected = null;
var tgr = (e.target.parentNode === this.timeMenu) ? e.target : e.target.parentNode;
// if we aren't targeting an item, then we return
if(!domClass.contains(tgr, this.baseClass+"Item")){return;}
this._highlightOption(tgr, true);
},
onmouseout: function(/*Event*/ e){
// summary:
// Handler for onmouseout event
// tags:
// private
this._keyboardSelected = null;
var tgr = (e.target.parentNode === this.timeMenu) ? e.target : e.target.parentNode;
this._highlightOption(tgr, false);
},
_mouseWheeled: function(/*Event*/ e){
// summary:
// Handle the mouse wheel events
// tags:
// private
this._keyboardSelected = null;
event.stop(e);
// we're not _measuring_ the scroll amount, just direction
var scrollAmount = (has("ie") ? e.wheelDelta : -e.detail);
this[(scrollAmount>0 ? "_onArrowUp" : "_onArrowDown")](); // yes, we're making a new dom node every time you mousewheel, or click
},
_onArrowUp: function(count){
// summary:
// Handler for up arrow key.
// description:
// Removes the bottom time and add one to the top
// tags:
// private
if(typeof count == "number" && count == -1){ return; } // typematic end
if(!this.timeMenu.childNodes.length){ return; }
var index = this.timeMenu.childNodes[0].index;
var divs = this._getFilteredNodes(index, 1, true, this.timeMenu.childNodes[0]);
if(divs.length){
this.timeMenu.removeChild(this.timeMenu.childNodes[this.timeMenu.childNodes.length - 1]);
this.timeMenu.insertBefore(divs[0], this.timeMenu.childNodes[0]);
}
},
_onArrowDown: function(count){
// summary:
// Handler for up arrow key.
// description:
// Remove the top time and add one to the bottom
// tags:
// private
if(typeof count == "number" && count == -1){ return; } // typematic end
if(!this.timeMenu.childNodes.length){ return; }
var index = this.timeMenu.childNodes[this.timeMenu.childNodes.length - 1].index + 1;
var divs = this._getFilteredNodes(index, 1, false, this.timeMenu.childNodes[this.timeMenu.childNodes.length - 1]);
if(divs.length){
this.timeMenu.removeChild(this.timeMenu.childNodes[0]);
this.timeMenu.appendChild(divs[0]);
}
},
handleKey: function(/*Event*/ e){
// summary:
// Called from `dijit.form._DateTimeTextBox` to pass a keypress event
// from the `dijit.form.TimeTextBox` to be handled in this widget
// tags:
// protected
if(e.charOrCode == keys.DOWN_ARROW || e.charOrCode == keys.UP_ARROW){
event.stop(e);
// Figure out which option to highlight now and then highlight it
if(this._highlighted_option && !this._highlighted_option.parentNode){
this._highlighted_option = null;
}
var timeMenu = this.timeMenu,
tgt = this._highlighted_option || query("." + this.baseClass + "ItemSelected", timeMenu)[0];
if(!tgt){
tgt = timeMenu.childNodes[0];
}else if(timeMenu.childNodes.length){
if(e.charOrCode == keys.DOWN_ARROW && !tgt.nextSibling){
this._onArrowDown();
}else if(e.charOrCode == keys.UP_ARROW && !tgt.previousSibling){
this._onArrowUp();
}
if(e.charOrCode == keys.DOWN_ARROW){
tgt = tgt.nextSibling;
}else{
tgt = tgt.previousSibling;
}
}
this._highlightOption(tgt, true);
this._keyboardSelected = tgt;
return false;
}else if(e.charOrCode == keys.ENTER || e.charOrCode === keys.TAB){
// mouse hover followed by TAB is NO selection
if(!this._keyboardSelected && e.charOrCode === keys.TAB){
return true; // true means don't call stopEvent()
}
// Accept the currently-highlighted option as the value
if(this._highlighted_option){
this._onOptionSelected({target: this._highlighted_option});
}
// Call stopEvent() for ENTER key so that form doesn't submit,
// but not for TAB, so that TAB does switch focus
return e.charOrCode === keys.TAB;
}
return undefined;
}
});
});
},
'dijit/form/RadioButton':function(){
define("dijit/form/RadioButton", [
"dojo/_base/declare", // declare
"./CheckBox",
"./_RadioButtonMixin"
], function(declare, CheckBox, _RadioButtonMixin){
/*=====
var CheckBox = dijit.form.CheckBox;
var _RadioButtonMixin = dijit.form._RadioButtonMixin;
=====*/
// module:
// dijit/form/RadioButton
// summary:
// Radio button widget
return declare("dijit.form.RadioButton", [CheckBox, _RadioButtonMixin], {
// summary:
// Same as an HTML radio, but with fancy styling.
baseClass: "dijitRadio"
});
});
},
'url:dijit/form/templates/HorizontalSlider.html':"<table class=\"dijit dijitReset dijitSlider dijitSliderH\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" rules=\"none\" data-dojo-attach-event=\"onkeypress:_onKeyPress,onkeyup:_onKeyUp\"\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\" colspan=\"2\"></td\n\t\t><td data-dojo-attach-point=\"topDecoration\" class=\"dijitReset dijitSliderDecoration dijitSliderDecorationT dijitSliderDecorationH\"></td\n\t\t><td class=\"dijitReset\" colspan=\"2\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset dijitSliderButtonContainer dijitSliderButtonContainerH\"\n\t\t\t><div class=\"dijitSliderDecrementIconH\" style=\"display:none\" data-dojo-attach-point=\"decrementButton\"><span class=\"dijitSliderButtonInner\">-</span></div\n\t\t></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><div class=\"dijitSliderBar dijitSliderBumper dijitSliderBumperH dijitSliderLeftBumper\" data-dojo-attach-event=\"press:_onClkDecBumper\"></div\n\t\t></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><input data-dojo-attach-point=\"valueNode\" type=\"hidden\" ${!nameAttrSetting}\n\t\t\t/><div class=\"dijitReset dijitSliderBarContainerH\" role=\"presentation\" data-dojo-attach-point=\"sliderBarContainer\"\n\t\t\t\t><div role=\"presentation\" data-dojo-attach-point=\"progressBar\" class=\"dijitSliderBar dijitSliderBarH dijitSliderProgressBar dijitSliderProgressBarH\" data-dojo-attach-event=\"press:_onBarClick\"\n\t\t\t\t\t><div class=\"dijitSliderMoveable dijitSliderMoveableH\"\n\t\t\t\t\t\t><div data-dojo-attach-point=\"sliderHandle,focusNode\" class=\"dijitSliderImageHandle dijitSliderImageHandleH\" data-dojo-attach-event=\"press:_onHandleClick\" role=\"slider\" valuemin=\"${minimum}\" valuemax=\"${maximum}\"></div\n\t\t\t\t\t></div\n\t\t\t\t></div\n\t\t\t\t><div role=\"presentation\" data-dojo-attach-point=\"remainingBar\" class=\"dijitSliderBar dijitSliderBarH dijitSliderRemainingBar dijitSliderRemainingBarH\" data-dojo-attach-event=\"press:_onBarClick\"></div\n\t\t\t></div\n\t\t></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><div class=\"dijitSliderBar dijitSliderBumper dijitSliderBumperH dijitSliderRightBumper\" data-dojo-attach-event=\"press:_onClkIncBumper\"></div\n\t\t></td\n\t\t><td class=\"dijitReset dijitSliderButtonContainer dijitSliderButtonContainerH\"\n\t\t\t><div class=\"dijitSliderIncrementIconH\" style=\"display:none\" data-dojo-attach-point=\"incrementButton\"><span class=\"dijitSliderButtonInner\">+</span></div\n\t\t></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\" colspan=\"2\"></td\n\t\t><td data-dojo-attach-point=\"containerNode,bottomDecoration\" class=\"dijitReset dijitSliderDecoration dijitSliderDecorationB dijitSliderDecorationH\"></td\n\t\t><td class=\"dijitReset\" colspan=\"2\"></td\n\t></tr\n></table>\n",
'url:dijit/templates/TimePicker.html':"<div id=\"widget_${id}\" class=\"dijitMenu\"\n ><div data-dojo-attach-point=\"upArrow\" class=\"dijitButtonNode dijitUpArrowButton\" data-dojo-attach-event=\"onmouseenter:_buttonMouse,onmouseleave:_buttonMouse\"\n\t\t><div class=\"dijitReset dijitInline dijitArrowButtonInner\" role=\"presentation\">&#160;</div\n\t\t><div class=\"dijitArrowButtonChar\">&#9650;</div></div\n ><div data-dojo-attach-point=\"timeMenu,focusNode\" data-dojo-attach-event=\"onclick:_onOptionSelected,onmouseover,onmouseout\"></div\n ><div data-dojo-attach-point=\"downArrow\" class=\"dijitButtonNode dijitDownArrowButton\" data-dojo-attach-event=\"onmouseenter:_buttonMouse,onmouseleave:_buttonMouse\"\n\t\t><div class=\"dijitReset dijitInline dijitArrowButtonInner\" role=\"presentation\">&#160;</div\n\t\t><div class=\"dijitArrowButtonChar\">&#9660;</div></div\n></div>\n",
'dijit/InlineEditBox':function(){
define([
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set domAttr.get
"dojo/dom-class", // domClass.add domClass.remove domClass.toggle
"dojo/dom-construct", // domConstruct.create domConstruct.destroy
"dojo/dom-style", // domStyle.getComputedStyle domStyle.set domStyle.get
"dojo/_base/event", // event.stop
"dojo/i18n", // i18n.getLocalization
"dojo/_base/kernel", // kernel.deprecated
"dojo/keys", // keys.ENTER keys.ESCAPE
"dojo/_base/lang", // lang.getObject
"dojo/_base/sniff", // has("ie")
"./focus",
"./_Widget",
"./_TemplatedMixin",
"./_WidgetsInTemplateMixin",
"./_Container",
"./form/Button",
"./form/_TextBoxMixin",
"./form/TextBox",
"dojo/text!./templates/InlineEditBox.html",
"dojo/i18n!./nls/common"
], function(array, declare, domAttr, domClass, domConstruct, domStyle, event, i18n, kernel, keys, lang, has,
fm, _Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _Container, Button, _TextBoxMixin, TextBox, template){
/*=====
var _Widget = dijit._Widget;
var _TemplatedMixin = dijit._TemplatedMixin;
var _WidgetsInTemplateMixin = dijit._WidgetsInTemplateMixin;
var _Container = dijit._Container;
var Button = dijit.form.Button;
var TextBox = dijit.form.TextBox;
=====*/
// module:
// dijit/InlineEditBox
// summary:
// An element with in-line edit capabilities
var InlineEditor = declare("dijit._InlineEditor", [_Widget, _TemplatedMixin, _WidgetsInTemplateMixin], {
// summary:
// Internal widget used by InlineEditBox, displayed when in editing mode
// to display the editor and maybe save/cancel buttons. Calling code should
// connect to save/cancel methods to detect when editing is finished
//
// Has mainly the same parameters as InlineEditBox, plus these values:
//
// style: Object
// Set of CSS attributes of display node, to replicate in editor
//
// value: String
// Value as an HTML string or plain text string, depending on renderAsHTML flag
templateString: template,
postMixInProperties: function(){
this.inherited(arguments);
this.messages = i18n.getLocalization("dijit", "common", this.lang);
array.forEach(["buttonSave", "buttonCancel"], function(prop){
if(!this[prop]){ this[prop] = this.messages[prop]; }
}, this);
},
buildRendering: function(){
this.inherited(arguments);
// Create edit widget in place in the template
var cls = typeof this.editor == "string" ? lang.getObject(this.editor) : this.editor;
// Copy the style from the source
// Don't copy ALL properties though, just the necessary/applicable ones.
// wrapperStyle/destStyle code is to workaround IE bug where getComputedStyle().fontSize
// is a relative value like 200%, rather than an absolute value like 24px, and
// the 200% can refer *either* to a setting on the node or it's ancestor (see #11175)
var srcStyle = this.sourceStyle,
editStyle = "line-height:" + srcStyle.lineHeight + ";",
destStyle = domStyle.getComputedStyle(this.domNode);
array.forEach(["Weight","Family","Size","Style"], function(prop){
var textStyle = srcStyle["font"+prop],
wrapperStyle = destStyle["font"+prop];
if(wrapperStyle != textStyle){
editStyle += "font-"+prop+":"+srcStyle["font"+prop]+";";
}
}, this);
array.forEach(["marginTop","marginBottom","marginLeft", "marginRight"], function(prop){
this.domNode.style[prop] = srcStyle[prop];
}, this);
var width = this.inlineEditBox.width;
if(width == "100%"){
// block mode
editStyle += "width:100%;";
this.domNode.style.display = "block";
}else{
// inline-block mode
editStyle += "width:" + (width + (Number(width) == width ? "px" : "")) + ";";
}
var editorParams = lang.delegate(this.inlineEditBox.editorParams, {
style: editStyle,
dir: this.dir,
lang: this.lang,
textDir: this.textDir
});
editorParams[ "displayedValue" in cls.prototype ? "displayedValue" : "value"] = this.value;
this.editWidget = new cls(editorParams, this.editorPlaceholder);
if(this.inlineEditBox.autoSave){
// Remove the save/cancel buttons since saving is done by simply tabbing away or
// selecting a value from the drop down list
domConstruct.destroy(this.buttonContainer);
}
},
postCreate: function(){
this.inherited(arguments);
var ew = this.editWidget;
if(this.inlineEditBox.autoSave){
// Selecting a value from a drop down list causes an onChange event and then we save
this.connect(ew, "onChange", "_onChange");
// ESC and TAB should cancel and save. Note that edit widgets do a stopEvent() on ESC key (to
// prevent Dialog from closing when the user just wants to revert the value in the edit widget),
// so this is the only way we can see the key press event.
this.connect(ew, "onKeyPress", "_onKeyPress");
}else{
// If possible, enable/disable save button based on whether the user has changed the value
if("intermediateChanges" in ew){
ew.set("intermediateChanges", true);
this.connect(ew, "onChange", "_onIntermediateChange");
this.saveButton.set("disabled", true);
}
}
},
_onIntermediateChange: function(/*===== val =====*/){
// summary:
// Called for editor widgets that support the intermediateChanges=true flag as a way
// to detect when to enable/disabled the save button
this.saveButton.set("disabled", (this.getValue() == this._resetValue) || !this.enableSave());
},
destroy: function(){
this.editWidget.destroy(true); // let the parent wrapper widget clean up the DOM
this.inherited(arguments);
},
getValue: function(){
// summary:
// Return the [display] value of the edit widget
var ew = this.editWidget;
return String(ew.get("displayedValue" in ew ? "displayedValue" : "value"));
},
_onKeyPress: function(e){
// summary:
// Handler for keypress in the edit box in autoSave mode.
// description:
// For autoSave widgets, if Esc/Enter, call cancel/save.
// tags:
// private
if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
if(e.altKey || e.ctrlKey){ return; }
// If Enter/Esc pressed, treat as save/cancel.
if(e.charOrCode == keys.ESCAPE){
event.stop(e);
this.cancel(true); // sets editing=false which short-circuits _onBlur processing
}else if(e.charOrCode == keys.ENTER && e.target.tagName == "INPUT"){
event.stop(e);
this._onChange(); // fire _onBlur and then save
}
// _onBlur will handle TAB automatically by allowing
// the TAB to change focus before we mess with the DOM: #6227
// Expounding by request:
// The current focus is on the edit widget input field.
// save() will hide and destroy this widget.
// We want the focus to jump from the currently hidden
// displayNode, but since it's hidden, it's impossible to
// unhide it, focus it, and then have the browser focus
// away from it to the next focusable element since each
// of these events is asynchronous and the focus-to-next-element
// is already queued.
// So we allow the browser time to unqueue the move-focus event
// before we do all the hide/show stuff.
}
},
_onBlur: function(){
// summary:
// Called when focus moves outside the editor
// tags:
// private
this.inherited(arguments);
if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
if(this.getValue() == this._resetValue){
this.cancel(false);
}else if(this.enableSave()){
this.save(false);
}
}
},
_onChange: function(){
// summary:
// Called when the underlying widget fires an onChange event,
// such as when the user selects a value from the drop down list of a ComboBox,
// which means that the user has finished entering the value and we should save.
// tags:
// private
if(this.inlineEditBox.autoSave && this.inlineEditBox.editing && this.enableSave()){
fm.focus(this.inlineEditBox.displayNode); // fires _onBlur which will save the formatted value
}
},
enableSave: function(){
// summary:
// User overridable function returning a Boolean to indicate
// if the Save button should be enabled or not - usually due to invalid conditions
// tags:
// extension
return (
this.editWidget.isValid
? this.editWidget.isValid()
: true
);
},
focus: function(){
// summary:
// Focus the edit widget.
// tags:
// protected
this.editWidget.focus();
setTimeout(lang.hitch(this, function(){
if(this.editWidget.focusNode && this.editWidget.focusNode.tagName == "INPUT"){
_TextBoxMixin.selectInputText(this.editWidget.focusNode);
}
}), 0);
}
});
var InlineEditBox = declare("dijit.InlineEditBox", _Widget, {
// summary:
// An element with in-line edit capabilities
//
// description:
// Behavior for an existing node (`<p>`, `<div>`, `<span>`, etc.) so that
// when you click it, an editor shows up in place of the original
// text. Optionally, Save and Cancel button are displayed below the edit widget.
// When Save is clicked, the text is pulled from the edit
// widget and redisplayed and the edit widget is again hidden.
// By default a plain Textarea widget is used as the editor (or for
// inline values a TextBox), but you can specify an editor such as
// dijit.Editor (for editing HTML) or a Slider (for adjusting a number).
// An edit widget must support the following API to be used:
// - displayedValue or value as initialization parameter,
// and available through set('displayedValue') / set('value')
// - void focus()
// - DOM-node focusNode = node containing editable text
// editing: [readonly] Boolean
// Is the node currently in edit mode?
editing: false,
// autoSave: Boolean
// Changing the value automatically saves it; don't have to push save button
// (and save button isn't even displayed)
autoSave: true,
// buttonSave: String
// Save button label
buttonSave: "",
// buttonCancel: String
// Cancel button label
buttonCancel: "",
// renderAsHtml: Boolean
// Set this to true if the specified Editor's value should be interpreted as HTML
// rather than plain text (ex: `dijit.Editor`)
renderAsHtml: false,
// editor: String|Function
// Class name (or reference to the Class) for Editor widget
editor: TextBox,
// editorWrapper: String|Function
// Class name (or reference to the Class) for widget that wraps the editor widget, displaying save/cancel
// buttons.
editorWrapper: InlineEditor,
// editorParams: Object
// Set of parameters for editor, like {required: true}
editorParams: {},
// disabled: Boolean
// If true, clicking the InlineEditBox to edit it will have no effect.
disabled: false,
onChange: function(/*===== value =====*/){
// summary:
// Set this handler to be notified of changes to value.
// tags:
// callback
},
onCancel: function(){
// summary:
// Set this handler to be notified when editing is cancelled.
// tags:
// callback
},
// width: String
// Width of editor. By default it's width=100% (ie, block mode).
width: "100%",
// value: String
// The display value of the widget in read-only mode
value: "",
// noValueIndicator: [const] String
// The text that gets displayed when there is no value (so that the user has a place to click to edit)
noValueIndicator: has("ie") <= 6 ? // font-family needed on IE6 but it messes up IE8
"<span style='font-family: wingdings; text-decoration: underline;'>&#160;&#160;&#160;&#160;&#x270d;&#160;&#160;&#160;&#160;</span>" :
"<span style='text-decoration: underline;'>&#160;&#160;&#160;&#160;&#x270d;&#160;&#160;&#160;&#160;</span>", // // &#160; == &nbsp;
constructor: function(){
// summary:
// Sets up private arrays etc.
// tags:
// private
this.editorParams = {};
},
postMixInProperties: function(){
this.inherited(arguments);
// save pointer to original source node, since Widget nulls-out srcNodeRef
this.displayNode = this.srcNodeRef;
// connect handlers to the display node
var events = {
ondijitclick: "_onClick",
onmouseover: "_onMouseOver",
onmouseout: "_onMouseOut",
onfocus: "_onMouseOver",
onblur: "_onMouseOut"
};
for(var name in events){
this.connect(this.displayNode, name, events[name]);
}
this.displayNode.setAttribute("role", "button");
if(!this.displayNode.getAttribute("tabIndex")){
this.displayNode.setAttribute("tabIndex", 0);
}
if(!this.value && !("value" in this.params)){ // "" is a good value if specified directly so check params){
this.value = lang.trim(this.renderAsHtml ? this.displayNode.innerHTML :
(this.displayNode.innerText||this.displayNode.textContent||""));
}
if(!this.value){
this.displayNode.innerHTML = this.noValueIndicator;
}
domClass.add(this.displayNode, 'dijitInlineEditBoxDisplayMode');
},
setDisabled: function(/*Boolean*/ disabled){
// summary:
// Deprecated. Use set('disabled', ...) instead.
// tags:
// deprecated
kernel.deprecated("dijit.InlineEditBox.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
this.set('disabled', disabled);
},
_setDisabledAttr: function(/*Boolean*/ disabled){
// summary:
// Hook to make set("disabled", ...) work.
// Set disabled state of widget.
this.domNode.setAttribute("aria-disabled", disabled);
if(disabled){
this.displayNode.removeAttribute("tabIndex");
}else{
this.displayNode.setAttribute("tabIndex", 0);
}
domClass.toggle(this.displayNode, "dijitInlineEditBoxDisplayModeDisabled", disabled);
this._set("disabled", disabled);
},
_onMouseOver: function(){
// summary:
// Handler for onmouseover and onfocus event.
// tags:
// private
if(!this.disabled){
domClass.add(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
}
},
_onMouseOut: function(){
// summary:
// Handler for onmouseout and onblur event.
// tags:
// private
domClass.remove(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
},
_onClick: function(/*Event*/ e){
// summary:
// Handler for onclick event.
// tags:
// private
if(this.disabled){ return; }
if(e){ event.stop(e); }
this._onMouseOut();
// Since FF gets upset if you move a node while in an event handler for that node...
setTimeout(lang.hitch(this, "edit"), 0);
},
edit: function(){
// summary:
// Display the editor widget in place of the original (read only) markup.
// tags:
// private
if(this.disabled || this.editing){ return; }
this._set('editing', true);
// save some display node values that can be restored later
this._savedPosition = domStyle.get(this.displayNode, "position") || "static";
this._savedOpacity = domStyle.get(this.displayNode, "opacity") || "1";
this._savedTabIndex = domAttr.get(this.displayNode, "tabIndex") || "0";
if(this.wrapperWidget){
var ew = this.wrapperWidget.editWidget;
ew.set("displayedValue" in ew ? "displayedValue" : "value", this.value);
}else{
// Placeholder for edit widget
// Put place holder (and eventually editWidget) before the display node so that it's positioned correctly
// when Calendar dropdown appears, which happens automatically on focus.
var placeholder = domConstruct.create("span", null, this.domNode, "before");
// Create the editor wrapper (the thing that holds the editor widget and the save/cancel buttons)
var ewc = typeof this.editorWrapper == "string" ? lang.getObject(this.editorWrapper) : this.editorWrapper;
this.wrapperWidget = new ewc({
value: this.value,
buttonSave: this.buttonSave,
buttonCancel: this.buttonCancel,
dir: this.dir,
lang: this.lang,
tabIndex: this._savedTabIndex,
editor: this.editor,
inlineEditBox: this,
sourceStyle: domStyle.getComputedStyle(this.displayNode),
save: lang.hitch(this, "save"),
cancel: lang.hitch(this, "cancel"),
textDir: this.textDir
}, placeholder);
if(!this._started){
this.startup();
}
}
var ww = this.wrapperWidget;
// to avoid screen jitter, we first create the editor with position:absolute, visibility:hidden,
// and then when it's finished rendering, we switch from display mode to editor
// position:absolute releases screen space allocated to the display node
// opacity:0 is the same as visibility:hidden but is still focusable
// visiblity:hidden removes focus outline
domStyle.set(this.displayNode, { position: "absolute", opacity: "0" }); // makes display node invisible, display style used for focus-ability
domStyle.set(ww.domNode, { position: this._savedPosition, visibility: "visible", opacity: "1" });
domAttr.set(this.displayNode, "tabIndex", "-1"); // needed by WebKit for TAB from editor to skip displayNode
// Replace the display widget with edit widget, leaving them both displayed for a brief time so that
// focus can be shifted without incident. (browser may needs some time to render the editor.)
setTimeout(lang.hitch(ww, function(){
this.focus(); // both nodes are showing, so we can switch focus safely
this._resetValue = this.getValue();
}), 0);
},
_onBlur: function(){
// summary:
// Called when focus moves outside the InlineEditBox.
// Performs garbage collection.
// tags:
// private
this.inherited(arguments);
if(!this.editing){
/* causes IE focus problems, see TooltipDialog_a11y.html...
setTimeout(lang.hitch(this, function(){
if(this.wrapperWidget){
this.wrapperWidget.destroy();
delete this.wrapperWidget;
}
}), 0);
*/
}
},
destroy: function(){
if(this.wrapperWidget && !this.wrapperWidget._destroyed){
this.wrapperWidget.destroy();
delete this.wrapperWidget;
}
this.inherited(arguments);
},
_showText: function(/*Boolean*/ focus){
// summary:
// Revert to display mode, and optionally focus on display node
// tags:
// private
var ww = this.wrapperWidget;
domStyle.set(ww.domNode, { position: "absolute", visibility: "hidden", opacity: "0" }); // hide the editor from mouse/keyboard events
domStyle.set(this.displayNode, { position: this._savedPosition, opacity: this._savedOpacity }); // make the original text visible
domAttr.set(this.displayNode, "tabIndex", this._savedTabIndex);
if(focus){
fm.focus(this.displayNode);
}
},
save: function(/*Boolean*/ focus){
// summary:
// Save the contents of the editor and revert to display mode.
// focus: Boolean
// Focus on the display mode text
// tags:
// private
if(this.disabled || !this.editing){ return; }
this._set('editing', false);
var ww = this.wrapperWidget;
var value = ww.getValue();
this.set('value', value); // display changed, formatted value
this._showText(focus); // set focus as needed
},
setValue: function(/*String*/ val){
// summary:
// Deprecated. Use set('value', ...) instead.
// tags:
// deprecated
kernel.deprecated("dijit.InlineEditBox.setValue() is deprecated. Use set('value', ...) instead.", "", "2.0");
return this.set("value", val);
},
_setValueAttr: function(/*String*/ val){
// summary:
// Hook to make set("value", ...) work.
// Inserts specified HTML value into this node, or an "input needed" character if node is blank.
val = lang.trim(val);
var renderVal = this.renderAsHtml ? val : val.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;").replace(/\n/g, "<br>");
this.displayNode.innerHTML = renderVal || this.noValueIndicator;
this._set("value", val);
if(this._started){
// tell the world that we have changed
setTimeout(lang.hitch(this, "onChange", val), 0); // setTimeout prevents browser freeze for long-running event handlers
}
// contextual (auto) text direction depends on the text value
if(this.textDir == "auto"){
this.applyTextDir(this.displayNode, this.displayNode.innerText);
}
},
getValue: function(){
// summary:
// Deprecated. Use get('value') instead.
// tags:
// deprecated
kernel.deprecated("dijit.InlineEditBox.getValue() is deprecated. Use get('value') instead.", "", "2.0");
return this.get("value");
},
cancel: function(/*Boolean*/ focus){
// summary:
// Revert to display mode, discarding any changes made in the editor
// tags:
// private
if(this.disabled || !this.editing){ return; }
this._set('editing', false);
// tell the world that we have no changes
setTimeout(lang.hitch(this, "onCancel"), 0); // setTimeout prevents browser freeze for long-running event handlers
this._showText(focus);
},
_setTextDirAttr: function(/*String*/ textDir){
// summary:
// Setter for textDir.
// description:
// Users shouldn't call this function; they should be calling
// set('textDir', value)
// tags:
// private
if(!this._created || this.textDir != textDir){
this._set("textDir", textDir);
this.applyTextDir(this.displayNode, this.displayNode.innerText);
this.displayNode.align = this.dir == "rtl" ? "right" : "left"; //fix the text alignment
}
}
});
InlineEditBox._InlineEditor = InlineEditor; // for monkey patching
return InlineEditBox;
});
},
'dojo/dnd/autoscroll':function(){
define(["../main", "../window"], function(dojo) {
// module:
// dojo/dnd/autoscroll
// summary:
// TODOC
dojo.getObject("dnd", true, dojo);
dojo.dnd.getViewport = dojo.window.getBox;
dojo.dnd.V_TRIGGER_AUTOSCROLL = 32;
dojo.dnd.H_TRIGGER_AUTOSCROLL = 32;
dojo.dnd.V_AUTOSCROLL_VALUE = 16;
dojo.dnd.H_AUTOSCROLL_VALUE = 16;
dojo.dnd.autoScroll = function(e){
// summary:
// a handler for onmousemove event, which scrolls the window, if
// necesary
// e: Event
// onmousemove event
// FIXME: needs more docs!
var v = dojo.window.getBox(), dx = 0, dy = 0;
if(e.clientX < dojo.dnd.H_TRIGGER_AUTOSCROLL){
dx = -dojo.dnd.H_AUTOSCROLL_VALUE;
}else if(e.clientX > v.w - dojo.dnd.H_TRIGGER_AUTOSCROLL){
dx = dojo.dnd.H_AUTOSCROLL_VALUE;
}
if(e.clientY < dojo.dnd.V_TRIGGER_AUTOSCROLL){
dy = -dojo.dnd.V_AUTOSCROLL_VALUE;
}else if(e.clientY > v.h - dojo.dnd.V_TRIGGER_AUTOSCROLL){
dy = dojo.dnd.V_AUTOSCROLL_VALUE;
}
window.scrollBy(dx, dy);
};
dojo.dnd._validNodes = {"div": 1, "p": 1, "td": 1};
dojo.dnd._validOverflow = {"auto": 1, "scroll": 1};
dojo.dnd.autoScrollNodes = function(e){
// summary:
// a handler for onmousemove event, which scrolls the first avaialble
// Dom element, it falls back to dojo.dnd.autoScroll()
// e: Event
// onmousemove event
// FIXME: needs more docs!
var b, t, w, h, rx, ry, dx = 0, dy = 0, oldLeft, oldTop;
for(var n = e.target; n;){
if(n.nodeType == 1 && (n.tagName.toLowerCase() in dojo.dnd._validNodes)){
var s = dojo.getComputedStyle(n),
overflow = (s.overflow.toLowerCase() in dojo.dnd._validOverflow),
overflowX = (s.overflowX.toLowerCase() in dojo.dnd._validOverflow),
overflowY = (s.overflowY.toLowerCase() in dojo.dnd._validOverflow);
if(overflow || overflowX || overflowY){
b = dojo._getContentBox(n, s);
t = dojo.position(n, true);
}
// overflow-x
if(overflow || overflowX){
w = Math.min(dojo.dnd.H_TRIGGER_AUTOSCROLL, b.w / 2);
rx = e.pageX - t.x;
if(dojo.isWebKit || dojo.isOpera){
// FIXME: this code should not be here, it should be taken into account
// either by the event fixing code, or the dojo.position()
// FIXME: this code doesn't work on Opera 9.5 Beta
rx += dojo.body().scrollLeft;
}
dx = 0;
if(rx > 0 && rx < b.w){
if(rx < w){
dx = -w;
}else if(rx > b.w - w){
dx = w;
}
oldLeft = n.scrollLeft;
n.scrollLeft = n.scrollLeft + dx;
}
}
// overflow-y
if(overflow || overflowY){
//console.log(b.l, b.t, t.x, t.y, n.scrollLeft, n.scrollTop);
h = Math.min(dojo.dnd.V_TRIGGER_AUTOSCROLL, b.h / 2);
ry = e.pageY - t.y;
if(dojo.isWebKit || dojo.isOpera){
// FIXME: this code should not be here, it should be taken into account
// either by the event fixing code, or the dojo.position()
// FIXME: this code doesn't work on Opera 9.5 Beta
ry += dojo.body().scrollTop;
}
dy = 0;
if(ry > 0 && ry < b.h){
if(ry < h){
dy = -h;
}else if(ry > b.h - h){
dy = h;
}
oldTop = n.scrollTop;
n.scrollTop = n.scrollTop + dy;
}
}
if(dx || dy){ return; }
}
try{
n = n.parentNode;
}catch(x){
n = null;
}
}
dojo.dnd.autoScroll(e);
};
return dojo.dnd;
});
},
'dijit/form/_RadioButtonMixin':function(){
define([
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/_base/event", // event.stop
"dojo/_base/lang", // lang.hitch
"dojo/query", // query
"dojo/_base/window", // win.doc
"../registry" // registry.getEnclosingWidget
], function(array, declare, domAttr, event, lang, query, win, registry){
// module:
// dijit/form/_RadioButtonMixin
// summary:
// Mixin to provide widget functionality for an HTML radio button
return declare("dijit.form._RadioButtonMixin", null, {
// summary:
// Mixin to provide widget functionality for an HTML radio button
// type: [private] String
// type attribute on <input> node.
// Users should not change this value.
type: "radio",
_getRelatedWidgets: function(){
// Private function needed to help iterate over all radio buttons in a group.
var ary = [];
query("input[type=radio]", this.focusNode.form || win.doc).forEach( // can't use name= since query doesn't support [] in the name
lang.hitch(this, function(inputNode){
if(inputNode.name == this.name && inputNode.form == this.focusNode.form){
var widget = registry.getEnclosingWidget(inputNode);
if(widget){
ary.push(widget);
}
}
})
);
return ary;
},
_setCheckedAttr: function(/*Boolean*/ value){
// If I am being checked then have to deselect currently checked radio button
this.inherited(arguments);
if(!this._created){ return; }
if(value){
array.forEach(this._getRelatedWidgets(), lang.hitch(this, function(widget){
if(widget != this && widget.checked){
widget.set('checked', false);
}
}));
}
},
_onClick: function(/*Event*/ e){
if(this.checked || this.disabled){ // nothing to do
event.stop(e);
return false;
}
if(this.readOnly){ // ignored by some browsers so we have to resync the DOM elements with widget values
event.stop(e);
array.forEach(this._getRelatedWidgets(), lang.hitch(this, function(widget){
domAttr.set(this.focusNode || this.domNode, 'checked', widget.checked);
}));
return false;
}
return this.inherited(arguments);
}
});
});
},
'url:dijit/templates/TreeNode.html':"<div class=\"dijitTreeNode\" role=\"presentation\"\n\t><div data-dojo-attach-point=\"rowNode\" class=\"dijitTreeRow\" role=\"presentation\" data-dojo-attach-event=\"onmouseenter:_onMouseEnter, onmouseleave:_onMouseLeave, onclick:_onClick, ondblclick:_onDblClick\"\n\t\t><img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"expandoNode\" class=\"dijitTreeExpando\" role=\"presentation\"\n\t\t/><span data-dojo-attach-point=\"expandoNodeText\" class=\"dijitExpandoText\" role=\"presentation\"\n\t\t></span\n\t\t><span data-dojo-attach-point=\"contentNode\"\n\t\t\tclass=\"dijitTreeContent\" role=\"presentation\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"iconNode\" class=\"dijitIcon dijitTreeIcon\" role=\"presentation\"\n\t\t\t/><span data-dojo-attach-point=\"labelNode\" class=\"dijitTreeLabel\" role=\"treeitem\" tabindex=\"-1\" aria-selected=\"false\" data-dojo-attach-event=\"onfocus:_onLabelFocus\"></span>\n\t\t</span\n\t></div>\n\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitTreeContainer\" role=\"presentation\" style=\"display: none;\"></div>\n</div>\n",
'dojo/dnd/TimedMoveable':function(){
define(["../main", "./Moveable"], function(dojo) {
// module:
// dojo/dnd/TimedMoveable
// summary:
// TODOC
/*=====
dojo.declare("dojo.dnd.__TimedMoveableArgs", [dojo.dnd.__MoveableArgs], {
// timeout: Number
// delay move by this number of ms,
// accumulating position changes during the timeout
timeout: 0
});
=====*/
// precalculate long expressions
var oldOnMove = dojo.dnd.Moveable.prototype.onMove;
dojo.declare("dojo.dnd.TimedMoveable", dojo.dnd.Moveable, {
// summary:
// A specialized version of Moveable to support an FPS throttling.
// This class puts an upper restriction on FPS, which may reduce
// the CPU load. The additional parameter "timeout" regulates
// the delay before actually moving the moveable object.
// object attributes (for markup)
timeout: 40, // in ms, 40ms corresponds to 25 fps
constructor: function(node, params){
// summary:
// an object that makes a node moveable with a timer
// node: Node||String
// a node (or node's id) to be moved
// params: dojo.dnd.__TimedMoveableArgs
// object with additional parameters.
// sanitize parameters
if(!params){ params = {}; }
if(params.timeout && typeof params.timeout == "number" && params.timeout >= 0){
this.timeout = params.timeout;
}
},
onMoveStop: function(/* dojo.dnd.Mover */ mover){
if(mover._timer){
// stop timer
clearTimeout(mover._timer);
// reflect the last received position
oldOnMove.call(this, mover, mover._leftTop)
}
dojo.dnd.Moveable.prototype.onMoveStop.apply(this, arguments);
},
onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
mover._leftTop = leftTop;
if(!mover._timer){
var _t = this; // to avoid using dojo.hitch()
mover._timer = setTimeout(function(){
// we don't have any pending requests
mover._timer = null;
// reflect the last received position
oldOnMove.call(_t, mover, mover._leftTop);
}, this.timeout);
}
}
});
return dojo.dnd.TimedMoveable;
});
},
'dijit/layout/LinkPane':function(){
define([
"./ContentPane",
"../_TemplatedMixin",
"dojo/_base/declare" // declare
], function(ContentPane, _TemplatedMixin, declare){
/*=====
var _TemplatedMixin = dijit._TemplatedMixin;
var ContentPane = dijit.layout.ContentPane;
=====*/
// module:
// dijit/layout/LinkPane
// summary:
// A ContentPane with an href where (when declared in markup)
// the title is specified as innerHTML rather than as a title attribute.
return declare("dijit.layout.LinkPane", [ContentPane, _TemplatedMixin], {
// summary:
// A ContentPane with an href where (when declared in markup)
// the title is specified as innerHTML rather than as a title attribute.
// description:
// LinkPane is just a ContentPane that is declared in markup similarly
// to an anchor. The anchor's body (the words between `<a>` and `</a>`)
// become the title of the widget (used for TabContainer, AccordionContainer, etc.)
// example:
// | <a href="foo.html">my title</a>
// I'm using a template because the user may specify the input as
// <a href="foo.html">title</a>, in which case we need to get rid of the
// <a> because we don't want a link.
templateString: '<div class="dijitLinkPane" data-dojo-attach-point="containerNode"></div>',
postMixInProperties: function(){
// If user has specified node contents, they become the title
// (the link must be plain text)
if(this.srcNodeRef){
this.title += this.srcNodeRef.innerHTML;
}
this.inherited(arguments);
},
_fillContent: function(){
// Overrides _Templated._fillContent().
// _Templated._fillContent() relocates srcNodeRef innerHTML to templated container node,
// but in our case the srcNodeRef innerHTML is the title, so shouldn't be
// copied
}
});
});
},
'dijit/form/_ListMouseMixin':function(){
define([
"dojo/_base/declare", // declare
"dojo/_base/event", // event.stop
"dojo/touch",
"./_ListBase"
], function(declare, event, touch, _ListBase){
/*=====
var _ListBase = dijit.form._ListBase;
=====*/
// module:
// dijit/form/_ListMouseMixin
// summary:
// a mixin to handle mouse or touch events for a focus-less menu
return declare( "dijit.form._ListMouseMixin", _ListBase, {
// summary:
// a Mixin to handle mouse or touch events for a focus-less menu
// Abstract methods that must be defined externally:
// onClick: item was chosen (mousedown somewhere on the menu and mouseup somewhere on the menu)
// tags:
// private
postCreate: function(){
this.inherited(arguments);
this.connect(this.domNode, touch.press, "_onMouseDown");
this.connect(this.domNode, touch.release, "_onMouseUp");
this.connect(this.domNode, "onmouseover", "_onMouseOver");
this.connect(this.domNode, "onmouseout", "_onMouseOut");
},
_onMouseDown: function(/*Event*/ evt){
event.stop(evt);
if(this._hoveredNode){
this.onUnhover(this._hoveredNode);
this._hoveredNode = null;
}
this._isDragging = true;
this._setSelectedAttr(this._getTarget(evt));
},
_onMouseUp: function(/*Event*/ evt){
event.stop(evt);
this._isDragging = false;
var selectedNode = this._getSelectedAttr();
var target = this._getTarget(evt);
var hoveredNode = this._hoveredNode;
if(selectedNode && target == selectedNode){
this.onClick(selectedNode);
}else if(hoveredNode && target == hoveredNode){ // drag to select
this._setSelectedAttr(hoveredNode);
this.onClick(hoveredNode);
}
},
_onMouseOut: function(/*Event*/ /*===== evt ====*/){
if(this._hoveredNode){
this.onUnhover(this._hoveredNode);
if(this._getSelectedAttr() == this._hoveredNode){
this.onSelect(this._hoveredNode);
}
this._hoveredNode = null;
}
if(this._isDragging){
this._cancelDrag = (new Date()).getTime() + 1000; // cancel in 1 second if no _onMouseOver fires
}
},
_onMouseOver: function(/*Event*/ evt){
if(this._cancelDrag){
var time = (new Date()).getTime();
if(time > this._cancelDrag){
this._isDragging = false;
}
this._cancelDrag = null;
}
var node = this._getTarget(evt);
if(!node){ return; }
if(this._hoveredNode != node){
if(this._hoveredNode){
this._onMouseOut({ target: this._hoveredNode });
}
if(node && node.parentNode == this.containerNode){
if(this._isDragging){
this._setSelectedAttr(node);
}else{
this._hoveredNode = node;
this.onHover(node);
}
}
}
}
});
});
},
'url:dijit/templates/Tree.html':"<div class=\"dijitTree dijitTreeContainer\" role=\"tree\"\n\tdata-dojo-attach-event=\"onkeypress:_onKeyPress\">\n\t<div class=\"dijitInline dijitTreeIndent\" style=\"position: absolute; top: -9999px\" data-dojo-attach-point=\"indentDetector\"></div>\n</div>\n",
'dojo/cldr/monetary':function(){
define(["../main"], function(dojo) {
// module:
// dojo/cldr/monetary
// summary:
// TODOC
dojo.getObject("cldr.monetary", true, dojo);
dojo.cldr.monetary.getData = function(/*String*/code){
// summary: A mapping of currency code to currency-specific formatting information. Returns a unique object with properties: places, round.
// code: an [ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code
// from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/currencyData/fractions
var placesData = {
ADP:0,AFN:0,ALL:0,AMD:0,BHD:3,BIF:0,BYR:0,CLF:0,CLP:0,
COP:0,CRC:0,DJF:0,ESP:0,GNF:0,GYD:0,HUF:0,IDR:0,IQD:0,
IRR:3,ISK:0,ITL:0,JOD:3,JPY:0,KMF:0,KPW:0,KRW:0,KWD:3,
LAK:0,LBP:0,LUF:0,LYD:3,MGA:0,MGF:0,MMK:0,MNT:0,MRO:0,
MUR:0,OMR:3,PKR:0,PYG:0,RSD:0,RWF:0,SLL:0,SOS:0,STD:0,
SYP:0,TMM:0,TND:3,TRL:0,TZS:0,UGX:0,UZS:0,VND:0,VUV:0,
XAF:0,XOF:0,XPF:0,YER:0,ZMK:0,ZWD:0
};
var roundingData = {CHF:5};
var places = placesData[code], round = roundingData[code];
if(typeof places == "undefined"){ places = 2; }
if(typeof round == "undefined"){ round = 0; }
return {places: places, round: round}; // Object
};
return dojo.cldr.monetary;
});
},
'dojo/cookie':function(){
define(["./_base/kernel", "./regexp"], function(dojo, regexp) {
// module:
// dojo/cookie
// summary:
// TODOC
/*=====
dojo.__cookieProps = function(){
// expires: Date|String|Number?
// If a number, the number of days from today at which the cookie
// will expire. If a date, the date past which the cookie will expire.
// If expires is in the past, the cookie will be deleted.
// If expires is omitted or is 0, the cookie will expire when the browser closes.
// path: String?
// The path to use for the cookie.
// domain: String?
// The domain to use for the cookie.
// secure: Boolean?
// Whether to only send the cookie on secure connections
this.expires = expires;
this.path = path;
this.domain = domain;
this.secure = secure;
}
=====*/
dojo.cookie = function(/*String*/name, /*String?*/value, /*dojo.__cookieProps?*/props){
// summary:
// Get or set a cookie.
// description:
// If one argument is passed, returns the value of the cookie
// For two or more arguments, acts as a setter.
// name:
// Name of the cookie
// value:
// Value for the cookie
// props:
// Properties for the cookie
// example:
// set a cookie with the JSON-serialized contents of an object which
// will expire 5 days from now:
// | dojo.cookie("configObj", dojo.toJson(config), { expires: 5 });
//
// example:
// de-serialize a cookie back into a JavaScript object:
// | var config = dojo.fromJson(dojo.cookie("configObj"));
//
// example:
// delete a cookie:
// | dojo.cookie("configObj", null, {expires: -1});
var c = document.cookie, ret;
if(arguments.length == 1){
var matches = c.match(new RegExp("(?:^|; )" + regexp.escapeString(name) + "=([^;]*)"));
ret = matches ? decodeURIComponent(matches[1]) : undefined;
}else{
props = props || {};
// FIXME: expires=0 seems to disappear right away, not on close? (FF3) Change docs?
var exp = props.expires;
if(typeof exp == "number"){
var d = new Date();
d.setTime(d.getTime() + exp*24*60*60*1000);
exp = props.expires = d;
}
if(exp && exp.toUTCString){ props.expires = exp.toUTCString(); }
value = encodeURIComponent(value);
var updatedCookie = name + "=" + value, propName;
for(propName in props){
updatedCookie += "; " + propName;
var propValue = props[propName];
if(propValue !== true){ updatedCookie += "=" + propValue; }
}
document.cookie = updatedCookie;
}
return ret; // String|undefined
};
dojo.cookie.isSupported = function(){
// summary:
// Use to determine if the current browser supports cookies or not.
//
// Returns true if user allows cookies.
// Returns false if user doesn't allow cookies.
if(!("cookieEnabled" in navigator)){
this("__djCookieTest__", "CookiesAllowed");
navigator.cookieEnabled = this("__djCookieTest__") == "CookiesAllowed";
if(navigator.cookieEnabled){
this("__djCookieTest__", "", {expires: -1});
}
}
return navigator.cookieEnabled;
};
return dojo.cookie;
});
},
'url:dijit/form/templates/DropDownBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\"\n\tid=\"widget_${id}\"\n\trole=\"combobox\"\n\t><div class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer'\n\t\tdata-dojo-attach-point=\"_buttonNode, _popupStateNode\" role=\"presentation\"\n\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"&#9660; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t${_buttonInputDisabled}\n\t/></div\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class='dijitReset dijitInputInner' ${!nameAttrSetting} type=\"text\" autocomplete=\"off\"\n\t\t\tdata-dojo-attach-point=\"textbox,focusNode\" role=\"textbox\" aria-haspopup=\"true\"\n\t/></div\n></div>\n",
'dijit/ProgressBar':function(){
define([
"require", // require.toUrl
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.toggle
"dojo/_base/lang", // lang.mixin
"dojo/number", // number.format
"./_Widget",
"./_TemplatedMixin",
"dojo/text!./templates/ProgressBar.html"
], function(require, declare, domClass, lang, number, _Widget, _TemplatedMixin, template){
/*=====
var _Widget = dijit._Widget;
var _TemplatedMixin = dijit._TemplatedMixin;
=====*/
// module:
// dijit/ProgressBar
// summary:
// A progress indication widget, showing the amount completed
// (often the percentage completed) of a task.
return declare("dijit.ProgressBar", [_Widget, _TemplatedMixin], {
// summary:
// A progress indication widget, showing the amount completed
// (often the percentage completed) of a task.
//
// example:
// | <div data-dojo-type="ProgressBar"
// | places="0"
// | value="..." maximum="...">
// | </div>
// progress: [const] String (Percentage or Number)
// Number or percentage indicating amount of task completed.
// Deprecated. Use "value" instead.
progress: "0",
// value: String (Percentage or Number)
// Number or percentage indicating amount of task completed.
// With "%": percentage value, 0% <= progress <= 100%, or
// without "%": absolute value, 0 <= progress <= maximum.
// Infinity means that the progress bar is indeterminate.
value: "",
// maximum: [const] Float
// Max sample number
maximum: 100,
// places: [const] Number
// Number of places to show in values; 0 by default
places: 0,
// indeterminate: [const] Boolean
// If false: show progress value (number or percentage).
// If true: show that a process is underway but that the amount completed is unknown.
// Deprecated. Use "value" instead.
indeterminate: false,
// label: String?
// Label on progress bar. Defaults to percentage for determinate progress bar and
// blank for indeterminate progress bar.
label:"",
// name: String
// this is the field name (for a form) if set. This needs to be set if you want to use
// this widget in a dijit.form.Form widget (such as dijit.Dialog)
name: '',
templateString: template,
// _indeterminateHighContrastImagePath: [private] URL
// URL to image to use for indeterminate progress bar when display is in high contrast mode
_indeterminateHighContrastImagePath:
require.toUrl("./themes/a11y/indeterminate_progress.gif"),
postMixInProperties: function(){
this.inherited(arguments);
if(!("value" in this.params)){
this.value = this.indeterminate ? Infinity : this.progress;
}
},
buildRendering: function(){
this.inherited(arguments);
this.indeterminateHighContrastImage.setAttribute("src",
this._indeterminateHighContrastImagePath.toString());
this.update();
},
update: function(/*Object?*/attributes){
// summary:
// Internal method to change attributes of ProgressBar, similar to set(hash). Users should call
// set("value", ...) rather than calling this method directly.
// attributes:
// May provide progress and/or maximum properties on this parameter;
// see attribute specs for details.
// example:
// | myProgressBar.update({'indeterminate': true});
// | myProgressBar.update({'progress': 80});
// | myProgressBar.update({'indeterminate': true, label:"Loading ..." })
// tags:
// private
// TODO: deprecate this method and use set() instead
lang.mixin(this, attributes || {});
var tip = this.internalProgress, ap = this.domNode;
var percent = 1;
if(this.indeterminate){
ap.removeAttribute("aria-valuenow");
ap.removeAttribute("aria-valuemin");
ap.removeAttribute("aria-valuemax");
}else{
if(String(this.progress).indexOf("%") != -1){
percent = Math.min(parseFloat(this.progress)/100, 1);
this.progress = percent * this.maximum;
}else{
this.progress = Math.min(this.progress, this.maximum);
percent = this.maximum ? this.progress / this.maximum : 0;
}
ap.setAttribute("aria-describedby", this.labelNode.id);
ap.setAttribute("aria-valuenow", this.progress);
ap.setAttribute("aria-valuemin", 0);
ap.setAttribute("aria-valuemax", this.maximum);
}
this.labelNode.innerHTML = this.report(percent);
domClass.toggle(this.domNode, "dijitProgressBarIndeterminate", this.indeterminate);
tip.style.width = (percent * 100) + "%";
this.onChange();
},
_setValueAttr: function(v){
this._set("value", v);
if(v == Infinity){
this.update({indeterminate:true});
}else{
this.update({indeterminate:false, progress:v});
}
},
_setLabelAttr: function(label){
this._set("label", label);
this.update();
},
_setIndeterminateAttr: function(indeterminate){
// Deprecated, use set("value", ...) instead
this.indeterminate = indeterminate;
this.update();
},
report: function(/*float*/percent){
// summary:
// Generates message to show inside progress bar (normally indicating amount of task completed).
// May be overridden.
// tags:
// extension
return this.label ? this.label :
(this.indeterminate ? "&#160;" : number.format(percent, { type: "percent", places: this.places, locale: this.lang }));
},
onChange: function(){
// summary:
// Callback fired when progress updates.
// tags:
// extension
}
});
});
},
'dijit/form/NumberTextBox':function(){
define([
"dojo/_base/declare", // declare
"dojo/_base/lang", // lang.hitch lang.mixin
"dojo/number", // number._realNumberRegexp number.format number.parse number.regexp
"./RangeBoundTextBox"
], function(declare, lang, number, RangeBoundTextBox){
/*=====
var RangeBoundTextBox = dijit.form.RangeBoundTextBox;
=====*/
// module:
// dijit/form/NumberTextBox
// summary:
// A TextBox for entering numbers, with formatting and range checking
/*=====
declare(
"dijit.form.NumberTextBox.__Constraints",
[dijit.form.RangeBoundTextBox.__Constraints, number.__FormatOptions, number.__ParseOptions], {
// summary:
// Specifies both the rules on valid/invalid values (minimum, maximum,
// number of required decimal places), and also formatting options for
// displaying the value when the field is not focused.
// example:
// Minimum/maximum:
// To specify a field between 0 and 120:
// | {min:0,max:120}
// To specify a field that must be an integer:
// | {fractional:false}
// To specify a field where 0 to 3 decimal places are allowed on input:
// | {places:'0,3'}
});
=====*/
var NumberTextBoxMixin = declare("dijit.form.NumberTextBoxMixin", null, {
// summary:
// A mixin for all number textboxes
// tags:
// protected
// Override ValidationTextBox.regExpGen().... we use a reg-ex generating function rather
// than a straight regexp to deal with locale (plus formatting options too?)
regExpGen: number.regexp,
/*=====
// constraints: dijit.form.NumberTextBox.__Constraints
// Despite the name, this parameter specifies both constraints on the input
// (including minimum/maximum allowed values) as well as
// formatting options like places (the number of digits to display after
// the decimal point). See `dijit.form.NumberTextBox.__Constraints` for details.
constraints: {},
======*/
// value: Number
// The value of this NumberTextBox as a Javascript Number (i.e., not a String).
// If the displayed value is blank, the value is NaN, and if the user types in
// an gibberish value (like "hello world"), the value is undefined
// (i.e. get('value') returns undefined).
//
// Symmetrically, set('value', NaN) will clear the displayed value,
// whereas set('value', undefined) will have no effect.
value: NaN,
// editOptions: [protected] Object
// Properties to mix into constraints when the value is being edited.
// This is here because we edit the number in the format "12345", which is
// different than the display value (ex: "12,345")
editOptions: { pattern: '#.######' },
/*=====
_formatter: function(value, options){
// summary:
// _formatter() is called by format(). It's the base routine for formatting a number,
// as a string, for example converting 12345 into "12,345".
// value: Number
// The number to be converted into a string.
// options: dojo.number.__FormatOptions?
// Formatting options
// tags:
// protected extension
return "12345"; // String
},
=====*/
_formatter: number.format,
postMixInProperties: function(){
this.inherited(arguments);
this._set("type", "text"); // in case type="number" was specified which messes up parse/format
},
_setConstraintsAttr: function(/*Object*/ constraints){
var places = typeof constraints.places == "number"? constraints.places : 0;
if(places){ places++; } // decimal rounding errors take away another digit of precision
if(typeof constraints.max != "number"){
constraints.max = 9 * Math.pow(10, 15-places);
}
if(typeof constraints.min != "number"){
constraints.min = -9 * Math.pow(10, 15-places);
}
this.inherited(arguments, [ constraints ]);
if(this.focusNode && this.focusNode.value && !isNaN(this.value)){
this.set('value', this.value);
}
},
_onFocus: function(){
if(this.disabled){ return; }
var val = this.get('value');
if(typeof val == "number" && !isNaN(val)){
var formattedValue = this.format(val, this.constraints);
if(formattedValue !== undefined){
this.textbox.value = formattedValue;
}
}
this.inherited(arguments);
},
format: function(/*Number*/ value, /*dojo.number.__FormatOptions*/ constraints){
// summary:
// Formats the value as a Number, according to constraints.
// tags:
// protected
var formattedValue = String(value);
if(typeof value != "number"){ return formattedValue; }
if(isNaN(value)){ return ""; }
// check for exponential notation that dojo.number.format chokes on
if(!("rangeCheck" in this && this.rangeCheck(value, constraints)) && constraints.exponent !== false && /\de[-+]?\d/i.test(formattedValue)){
return formattedValue;
}
if(this.editOptions && this.focused){
constraints = lang.mixin({}, constraints, this.editOptions);
}
return this._formatter(value, constraints);
},
/*=====
_parser: function(value, constraints){
// summary:
// Parses the string value as a Number, according to constraints.
// value: String
// String representing a number
// constraints: dojo.number.__ParseOptions
// Formatting options
// tags:
// protected
return 123.45; // Number
},
=====*/
_parser: number.parse,
parse: function(/*String*/ value, /*number.__FormatOptions*/ constraints){
// summary:
// Replaceable function to convert a formatted string to a number value
// tags:
// protected extension
var v = this._parser(value, lang.mixin({}, constraints, (this.editOptions && this.focused) ? this.editOptions : {}));
if(this.editOptions && this.focused && isNaN(v)){
v = this._parser(value, constraints); // parse w/o editOptions: not technically needed but is nice for the user
}
return v;
},
_getDisplayedValueAttr: function(){
var v = this.inherited(arguments);
return isNaN(v) ? this.textbox.value : v;
},
filter: function(/*Number*/ value){
// summary:
// This is called with both the display value (string), and the actual value (a number).
// When called with the actual value it does corrections so that '' etc. are represented as NaN.
// Otherwise it dispatches to the superclass's filter() method.
//
// See `dijit.form.TextBox.filter` for more details.
return (value === null || value === '' || value === undefined) ? NaN : this.inherited(arguments); // set('value', null||''||undefined) should fire onChange(NaN)
},
serialize: function(/*Number*/ value, /*Object?*/ options){
// summary:
// Convert value (a Number) into a canonical string (ie, how the number literal is written in javascript/java/C/etc.)
// tags:
// protected
return (typeof value != "number" || isNaN(value)) ? '' : this.inherited(arguments);
},
_setBlurValue: function(){
var val = lang.hitch(lang.mixin({}, this, { focused: true }), "get")('value'); // parse with editOptions
this._setValueAttr(val, true);
},
_setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
// summary:
// Hook so set('value', ...) works.
if(value !== undefined && formattedValue === undefined){
formattedValue = String(value);
if(typeof value == "number"){
if(isNaN(value)){ formattedValue = '' }
// check for exponential notation that number.format chokes on
else if(("rangeCheck" in this && this.rangeCheck(value, this.constraints)) || this.constraints.exponent === false || !/\de[-+]?\d/i.test(formattedValue)){
formattedValue = undefined; // lets format compute a real string value
}
}else if(!value){ // 0 processed in if branch above, ''|null|undefined flows through here
formattedValue = '';
value = NaN;
}else{ // non-numeric values
value = undefined;
}
}
this.inherited(arguments, [value, priorityChange, formattedValue]);
},
_getValueAttr: function(){
// summary:
// Hook so get('value') works.
// Returns Number, NaN for '', or undefined for unparseable text
var v = this.inherited(arguments); // returns Number for all values accepted by parse() or NaN for all other displayed values
// If the displayed value of the textbox is gibberish (ex: "hello world"), this.inherited() above
// returns NaN; this if() branch converts the return value to undefined.
// Returning undefined prevents user text from being overwritten when doing _setValueAttr(_getValueAttr()).
// A blank displayed value is still returned as NaN.
if(isNaN(v) && this.textbox.value !== ''){
if(this.constraints.exponent !== false && /\de[-+]?\d/i.test(this.textbox.value) && (new RegExp("^"+number._realNumberRegexp(lang.mixin({}, this.constraints))+"$").test(this.textbox.value))){ // check for exponential notation that parse() rejected (erroneously?)
var n = Number(this.textbox.value);
return isNaN(n) ? undefined : n; // return exponential Number or undefined for random text (may not be possible to do with the above RegExp check)
}else{
return undefined; // gibberish
}
}else{
return v; // Number or NaN for ''
}
},
isValid: function(/*Boolean*/ isFocused){
// Overrides dijit.form.RangeBoundTextBox.isValid to check that the editing-mode value is valid since
// it may not be formatted according to the regExp validation rules
if(!this.focused || this._isEmpty(this.textbox.value)){
return this.inherited(arguments);
}else{
var v = this.get('value');
if(!isNaN(v) && this.rangeCheck(v, this.constraints)){
if(this.constraints.exponent !== false && /\de[-+]?\d/i.test(this.textbox.value)){ // exponential, parse doesn't like it
return true; // valid exponential number in range
}else{
return this.inherited(arguments);
}
}else{
return false;
}
}
}
});
/*=====
NumberTextBoxMixin = dijit.form.NumberTextBoxMixin;
=====*/
var NumberTextBox = declare("dijit.form.NumberTextBox", [RangeBoundTextBox,NumberTextBoxMixin], {
// summary:
// A TextBox for entering numbers, with formatting and range checking
// description:
// NumberTextBox is a textbox for entering and displaying numbers, supporting
// the following main features:
//
// 1. Enforce minimum/maximum allowed values (as well as enforcing that the user types
// a number rather than a random string)
// 2. NLS support (altering roles of comma and dot as "thousands-separator" and "decimal-point"
// depending on locale).
// 3. Separate modes for editing the value and displaying it, specifically that
// the thousands separator character (typically comma) disappears when editing
// but reappears after the field is blurred.
// 4. Formatting and constraints regarding the number of places (digits after the decimal point)
// allowed on input, and number of places displayed when blurred (see `constraints` parameter).
baseClass: "dijitTextBox dijitNumberTextBox"
});
NumberTextBox.Mixin = NumberTextBoxMixin; // for monkey patching
return NumberTextBox;
});
},
'dijit/form/TimeTextBox':function(){
define("dijit/form/TimeTextBox", [
"dojo/_base/declare", // declare
"dojo/keys", // keys.DOWN_ARROW keys.ENTER keys.ESCAPE keys.TAB keys.UP_ARROW
"dojo/_base/lang", // lang.hitch
"../_TimePicker",
"./_DateTimeTextBox"
], function(declare, keys, lang, _TimePicker, _DateTimeTextBox){
/*=====
var _TimePicker = dijit._TimePicker;
var _DateTimeTextBox = dijit.form._DateTimeTextBox;
=====*/
// module:
// dijit/form/TimeTextBox
// summary:
// A validating, serializable, range-bound time text box with a drop down time picker
/*=====
declare(
"dijit.form.TimeTextBox.__Constraints",
[dijit.form._DateTimeTextBox.__Constraints, dijit._TimePicker.__Constraints]
);
=====*/
return declare("dijit.form.TimeTextBox", _DateTimeTextBox, {
// summary:
// A validating, serializable, range-bound time text box with a drop down time picker
baseClass: "dijitTextBox dijitComboBox dijitTimeTextBox",
popupClass: _TimePicker,
_selector: "time",
/*=====
// constraints: dijit.form.TimeTextBox.__Constraints
constraints:{},
=====*/
// value: Date
// The value of this widget as a JavaScript Date object. Note that the date portion implies time zone and daylight savings rules.
//
// Example:
// | new dijit.form.TimeTextBox({value: stamp.fromISOString("T12:59:59", new Date())})
//
// When passed to the parser in markup, must be specified according to locale-independent
// `stamp.fromISOString` format.
//
// Example:
// | <input data-dojo-type='dijit.form.TimeTextBox' value='T12:34:00'>
value: new Date(""), // value.toString()="NaN"
//FIXME: in markup, you have no control over daylight savings
_onKey: function(evt){
if(this.disabled || this.readOnly){ return; }
this.inherited(arguments);
// If the user has backspaced or typed some numbers, then filter the result list
// by what they typed. Maybe there's a better way to detect this, like _handleOnChange()?
switch(evt.keyCode){
case keys.ENTER:
case keys.TAB:
case keys.ESCAPE:
case keys.DOWN_ARROW:
case keys.UP_ARROW:
// these keys have special meaning
break;
default:
// setTimeout() because the keystroke hasn't yet appeared in the <input>,
// so the get('displayedValue') call below won't give the result we want.
setTimeout(lang.hitch(this, function(){
// set this.filterString to the filter to apply to the drop down list;
// it will be used in openDropDown()
var val = this.get('displayedValue');
this.filterString = (val && !this.parse(val, this.constraints)) ? val.toLowerCase() : "";
// close the drop down and reopen it, in order to filter the items shown in the list
// and also since the drop down may need to be repositioned if the number of list items has changed
// and it's being displayed above the <input>
if(this._opened){
this.closeDropDown();
}
this.openDropDown();
}), 0);
}
}
});
});
},
'dijit/ColorPalette':function(){
define([
"require", // require.toUrl
"dojo/text!./templates/ColorPalette.html",
"./_Widget",
"./_TemplatedMixin",
"./_PaletteMixin",
"dojo/i18n", // i18n.getLocalization
"dojo/_base/Color", // dojo.Color dojo.Color.named
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.contains
"dojo/dom-construct", // domConstruct.place
"dojo/_base/window", // win.body
"dojo/string", // string.substitute
"dojo/i18n!dojo/nls/colors", // translations
"dojo/colors" // extend dojo.Color w/names of other colors
], function(require, template, _Widget, _TemplatedMixin, _PaletteMixin, i18n, Color,
declare, domClass, domConstruct, win, string){
/*=====
var _Widget = dijit._Widget;
var _TemplatedMixin = dijit._TemplatedMixin;
var _PaletteMixin = dijit._PaletteMixin;
=====*/
// module:
// dijit/ColorPalette
// summary:
// A keyboard accessible color-picking widget
var ColorPalette = declare("dijit.ColorPalette", [_Widget, _TemplatedMixin, _PaletteMixin], {
// summary:
// A keyboard accessible color-picking widget
// description:
// Grid showing various colors, so the user can pick a certain color.
// Can be used standalone, or as a popup.
//
// example:
// | <div data-dojo-type="dijit.ColorPalette"></div>
//
// example:
// | var picker = new dijit.ColorPalette({ },srcNode);
// | picker.startup();
// palette: [const] String
// Size of grid, either "7x10" or "3x4".
palette: "7x10",
// _palettes: [protected] Map
// This represents the value of the colors.
// The first level is a hashmap of the different palettes available.
// The next two dimensions represent the columns and rows of colors.
_palettes: {
"7x10": [["white", "seashell", "cornsilk", "lemonchiffon","lightyellow", "palegreen", "paleturquoise", "lightcyan", "lavender", "plum"],
["lightgray", "pink", "bisque", "moccasin", "khaki", "lightgreen", "lightseagreen", "lightskyblue", "cornflowerblue", "violet"],
["silver", "lightcoral", "sandybrown", "orange", "palegoldenrod", "chartreuse", "mediumturquoise", "skyblue", "mediumslateblue","orchid"],
["gray", "red", "orangered", "darkorange", "yellow", "limegreen", "darkseagreen", "royalblue", "slateblue", "mediumorchid"],
["dimgray", "crimson", "chocolate", "coral", "gold", "forestgreen", "seagreen", "blue", "blueviolet", "darkorchid"],
["darkslategray","firebrick","saddlebrown", "sienna", "olive", "green", "darkcyan", "mediumblue","darkslateblue", "darkmagenta" ],
["black", "darkred", "maroon", "brown", "darkolivegreen", "darkgreen", "midnightblue", "navy", "indigo", "purple"]],
"3x4": [["white", "lime", "green", "blue"],
["silver", "yellow", "fuchsia", "navy"],
["gray", "red", "purple", "black"]]
},
// templateString: String
// The template of this widget.
templateString: template,
baseClass: "dijitColorPalette",
_dyeFactory: function(value, row, col){
// Overrides _PaletteMixin._dyeFactory().
return new this._dyeClass(value, row, col);
},
buildRendering: function(){
// Instantiate the template, which makes a skeleton into which we'll insert a bunch of
// <img> nodes
this.inherited(arguments);
// Creates customized constructor for dye class (color of a single cell) for
// specified palette and high-contrast vs. normal mode. Used in _getDye().
this._dyeClass = declare(ColorPalette._Color, {
hc: domClass.contains(win.body(), "dijit_a11y"),
palette: this.palette
});
// Creates <img> nodes in each cell of the template.
this._preparePalette(
this._palettes[this.palette],
i18n.getLocalization("dojo", "colors", this.lang));
}
});
ColorPalette._Color = declare("dijit._Color", Color, {
// summary:
// Object associated with each cell in a ColorPalette palette.
// Implements dijit.Dye.
// Template for each cell in normal (non-high-contrast mode). Each cell contains a wrapper
// node for showing the border (called dijitPaletteImg for back-compat), and dijitColorPaletteSwatch
// for showing the color.
template:
"<span class='dijitInline dijitPaletteImg'>" +
"<img src='${blankGif}' alt='${alt}' class='dijitColorPaletteSwatch' style='background-color: ${color}'/>" +
"</span>",
// Template for each cell in high contrast mode. Each cell contains an image with the whole palette,
// but scrolled and clipped to show the correct color only
hcTemplate:
"<span class='dijitInline dijitPaletteImg' style='position: relative; overflow: hidden; height: 12px; width: 14px;'>" +
"<img src='${image}' alt='${alt}' style='position: absolute; left: ${left}px; top: ${top}px; ${size}'/>" +
"</span>",
// _imagePaths: [protected] Map
// This is stores the path to the palette images used for high-contrast mode display
_imagePaths: {
"7x10": require.toUrl("./themes/a11y/colors7x10.png"),
"3x4": require.toUrl("./themes/a11y/colors3x4.png")
},
constructor: function(/*String*/alias, /*Number*/ row, /*Number*/ col){
this._alias = alias;
this._row = row;
this._col = col;
this.setColor(Color.named[alias]);
},
getValue: function(){
// summary:
// Note that although dijit._Color is initialized with a value like "white" getValue() always
// returns a hex value
return this.toHex();
},
fillCell: function(/*DOMNode*/ cell, /*String*/ blankGif){
var html = string.substitute(this.hc ? this.hcTemplate : this.template, {
// substitution variables for normal mode
color: this.toHex(),
blankGif: blankGif,
alt: this._alias,
// variables used for high contrast mode
image: this._imagePaths[this.palette].toString(),
left: this._col * -20 - 5,
top: this._row * -20 - 5,
size: this.palette == "7x10" ? "height: 145px; width: 206px" : "height: 64px; width: 86px"
});
domConstruct.place(html, cell);
}
});
return ColorPalette;
});
},
'url:dijit/form/templates/Button.html':"<span class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><span class=\"dijitReset dijitInline dijitButtonNode\"\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" role=\"presentation\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"titleNode,focusNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\" data-dojo-attach-point=\"iconNode\"></span\n\t\t\t><span class=\"dijitReset dijitToggleButtonIconChar\">&#x25CF;</span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t\tdata-dojo-attach-point=\"containerNode\"\n\t\t\t></span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\"\n\t\ttabIndex=\"-1\" role=\"presentation\" data-dojo-attach-point=\"valueNode\"\n/></span>\n",
'dijit/form/CurrencyTextBox':function(){
define([
"dojo/currency", // currency._mixInDefaults currency.format currency.parse currency.regexp
"dojo/_base/declare", // declare
"dojo/_base/lang", // lang.hitch
"./NumberTextBox"
], function(currency, declare, lang, NumberTextBox){
/*=====
var NumberTextBox = dijit.form.NumberTextBox;
=====*/
// module:
// dijit/form/CurrencyTextBox
// summary:
// A validating currency textbox
/*=====
declare(
"dijit.form.CurrencyTextBox.__Constraints",
[dijit.form.NumberTextBox.__Constraints, currency.__FormatOptions, currency.__ParseOptions], {
// summary:
// Specifies both the rules on valid/invalid values (minimum, maximum,
// number of required decimal places), and also formatting options for
// displaying the value when the field is not focused (currency symbol,
// etc.)
// description:
// Follows the pattern of `dijit.form.NumberTextBox.constraints`.
// In general developers won't need to set this parameter
// example:
// To ensure that the user types in the cents (for example, 1.00 instead of just 1):
// | {fractional:true}
});
=====*/
return declare("dijit.form.CurrencyTextBox", NumberTextBox, {
// summary:
// A validating currency textbox
// description:
// CurrencyTextBox is similar to `dijit.form.NumberTextBox` but has a few
// extra features related to currency:
//
// 1. After specifying the currency type (american dollars, euros, etc.) it automatically
// sets parse/format options such as how many decimal places to show.
// 2. The currency mark (dollar sign, euro mark, etc.) is displayed when the field is blurred
// but erased during editing, so that the user can just enter a plain number.
// currency: [const] String
// the [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code, a three letter sequence like "USD"
currency: "",
/*=====
// constraints: dijit.form.CurrencyTextBox.__Constraints
// Despite the name, this parameter specifies both constraints on the input
// (including minimum/maximum allowed values) as well as
// formatting options. See `dijit.form.CurrencyTextBox.__Constraints` for details.
constraints: {},
======*/
baseClass: "dijitTextBox dijitCurrencyTextBox",
// Override regExpGen ValidationTextBox.regExpGen().... we use a reg-ex generating function rather
// than a straight regexp to deal with locale (plus formatting options too?)
regExpGen: function(constraints){
// if focused, accept either currency data or NumberTextBox format
return '(' + (this.focused ? this.inherited(arguments, [ lang.mixin({}, constraints, this.editOptions) ]) + '|' : '')
+ currency.regexp(constraints) + ')';
},
// Override NumberTextBox._formatter to deal with currencies, ex: converts "123.45" to "$123.45"
_formatter: currency.format,
_parser: currency.parse,
parse: function(/*String*/ value, /*Object*/ constraints){
// summary:
// Parses string value as a Currency, according to the constraints object
// tags:
// protected extension
var v = this.inherited(arguments);
if(isNaN(v) && /\d+/.test(value)){ // currency parse failed, but it could be because they are using NumberTextBox format so try its parse
v = lang.hitch(lang.mixin({}, this, { _parser: NumberTextBox.prototype._parser }), "inherited")(arguments);
}
return v;
},
_setConstraintsAttr: function(/*Object*/ constraints){
if(!constraints.currency && this.currency){
constraints.currency = this.currency;
}
this.inherited(arguments, [ currency._mixInDefaults(lang.mixin(constraints, { exponent: false })) ]); // get places
}
});
});
},
'url:dijit/templates/MenuItem.html':"<tr class=\"dijitReset dijitMenuItem\" data-dojo-attach-point=\"focusNode\" role=\"menuitem\" tabIndex=\"-1\"\n\t\tdata-dojo-attach-event=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitMenuItemIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" data-dojo-attach-point=\"containerNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" data-dojo-attach-point=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">\n\t\t<div data-dojo-attach-point=\"arrowWrapper\" style=\"visibility: hidden\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuExpand\"/>\n\t\t\t<span class=\"dijitMenuExpandA11y\">+</span>\n\t\t</div>\n\t</td>\n</tr>\n",
'url:dijit/form/templates/CheckBox.html':"<div class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><input\n\t \t${!nameAttrSetting} type=\"${type}\" ${checkedAttrSetting}\n\t\tclass=\"dijitReset dijitCheckBoxInput\"\n\t\tdata-dojo-attach-point=\"focusNode\"\n\t \tdata-dojo-attach-event=\"onclick:_onClick\"\n/></div>\n",
'dojo/cldr/nls/gregorian':function(){
define({ root:
//begin v1.x content
{
"months-format-narrow": [
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12"
],
"quarters-standAlone-narrow": [
"1",
"2",
"3",
"4"
],
"field-weekday": "Day of the Week",
"dateFormatItem-yQQQ": "y QQQ",
"dateFormatItem-yMEd": "EEE, y-M-d",
"dateFormatItem-MMMEd": "E MMM d",
"eraNarrow": [
"BCE",
"CE"
],
"dateTimeFormats-appendItem-Day-Of-Week": "{0} {1}",
"dateFormat-long": "y MMMM d",
"months-format-wide": [
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12"
],
"dateTimeFormat-medium": "{1} {0}",
"dateFormatItem-EEEd": "d EEE",
"dayPeriods-format-wide-pm": "PM",
"dateFormat-full": "EEEE, y MMMM dd",
"dateFormatItem-Md": "M-d",
"dayPeriods-format-abbr-am": "AM",
"dateTimeFormats-appendItem-Second": "{0} ({2}: {1})",
"field-era": "Era",
"dateFormatItem-yM": "y-M",
"months-standAlone-wide": [
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12"
],
"timeFormat-short": "HH:mm",
"quarters-format-wide": [
"Q1",
"Q2",
"Q3",
"Q4"
],
"timeFormat-long": "HH:mm:ss z",
"field-year": "Year",
"dateFormatItem-yMMM": "y MMM",
"dateFormatItem-yQ": "y Q",
"dateTimeFormats-appendItem-Era": "{0} {1}",
"field-hour": "Hour",
"months-format-abbr": [
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12"
],
"timeFormat-full": "HH:mm:ss zzzz",
"dateTimeFormats-appendItem-Week": "{0} ({2}: {1})",
"field-day-relative+0": "Today",
"field-day-relative+1": "Tomorrow",
"dateFormatItem-H": "HH",
"months-standAlone-abbr": [
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12"
],
"quarters-format-abbr": [
"Q1",
"Q2",
"Q3",
"Q4"
],
"quarters-standAlone-wide": [
"Q1",
"Q2",
"Q3",
"Q4"
],
"dateFormatItem-M": "L",
"days-standAlone-wide": [
"1",
"2",
"3",
"4",
"5",
"6",
"7"
],
"timeFormat-medium": "HH:mm:ss",
"dateFormatItem-Hm": "HH:mm",
"quarters-standAlone-abbr": [
"Q1",
"Q2",
"Q3",
"Q4"
],
"eraAbbr": [
"BCE",
"CE"
],
"field-minute": "Minute",
"field-dayperiod": "Dayperiod",
"days-standAlone-abbr": [
"1",
"2",
"3",
"4",
"5",
"6",
"7"
],
"dateFormatItem-d": "d",
"dateFormatItem-ms": "mm:ss",
"quarters-format-narrow": [
"1",
"2",
"3",
"4"
],
"field-day-relative+-1": "Yesterday",
"dateFormatItem-h": "h a",
"dateTimeFormat-long": "{1} {0}",
"dayPeriods-format-narrow-am": "AM",
"dateFormatItem-MMMd": "MMM d",
"dateFormatItem-MEd": "E, M-d",
"dateTimeFormat-full": "{1} {0}",
"field-day": "Day",
"days-format-wide": [
"1",
"2",
"3",
"4",
"5",
"6",
"7"
],
"field-zone": "Zone",
"dateTimeFormats-appendItem-Day": "{0} ({2}: {1})",
"dateFormatItem-y": "y",
"months-standAlone-narrow": [
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12"
],
"dateFormatItem-hm": "h:mm a",
"dateTimeFormats-appendItem-Year": "{0} {1}",
"dateTimeFormats-appendItem-Hour": "{0} ({2}: {1})",
"dayPeriods-format-abbr-pm": "PM",
"days-format-abbr": [
"1",
"2",
"3",
"4",
"5",
"6",
"7"
],
"eraNames": [
"BCE",
"CE"
],
"days-format-narrow": [
"1",
"2",
"3",
"4",
"5",
"6",
"7"
],
"days-standAlone-narrow": [
"1",
"2",
"3",
"4",
"5",
"6",
"7"
],
"dateFormatItem-MMM": "LLL",
"field-month": "Month",
"dateTimeFormats-appendItem-Quarter": "{0} ({2}: {1})",
"dayPeriods-format-wide-am": "AM",
"dateTimeFormats-appendItem-Month": "{0} ({2}: {1})",
"dateTimeFormats-appendItem-Minute": "{0} ({2}: {1})",
"dateFormat-short": "yyyy-MM-dd",
"field-second": "Second",
"dateFormatItem-yMMMEd": "EEE, y MMM d",
"dateTimeFormats-appendItem-Timezone": "{0} {1}",
"field-week": "Week",
"dateFormat-medium": "y MMM d",
"dayPeriods-format-narrow-pm": "PM",
"dateTimeFormat-short": "{1} {0}",
"dateFormatItem-Hms": "HH:mm:ss",
"dateFormatItem-hms": "h:mm:ss a"
}
//end v1.x content
,
"ar": true,
"ca": true,
"cs": true,
"da": true,
"de": true,
"el": true,
"en": true,
"en-au": true,
"en-ca": true,
"en-gb": true,
"es": true,
"fi": true,
"fr": true,
"fr-ch": true,
"he": true,
"hu": true,
"it": true,
"ja": true,
"ko": true,
"nb": true,
"nl": true,
"pl": true,
"pt": true,
"pt-pt": true,
"ro": true,
"ru": true,
"sk": true,
"sl": true,
"sv": true,
"th": true,
"tr": true,
"zh": true,
"zh-hant": true,
"zh-hk": true,
"zh-tw": true
});
},
'url:dijit/form/templates/VerticalSlider.html':"<table class=\"dijit dijitReset dijitSlider dijitSliderV\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" rules=\"none\" data-dojo-attach-event=\"onkeypress:_onKeyPress,onkeyup:_onKeyUp\"\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\"></td\n\t\t><td class=\"dijitReset dijitSliderButtonContainer dijitSliderButtonContainerV\"\n\t\t\t><div class=\"dijitSliderIncrementIconV\" style=\"display:none\" data-dojo-attach-point=\"decrementButton\"><span class=\"dijitSliderButtonInner\">+</span></div\n\t\t></td\n\t\t><td class=\"dijitReset\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\"></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><center><div class=\"dijitSliderBar dijitSliderBumper dijitSliderBumperV dijitSliderTopBumper\" data-dojo-attach-event=\"press:_onClkIncBumper\"></div></center\n\t\t></td\n\t\t><td class=\"dijitReset\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td data-dojo-attach-point=\"leftDecoration\" class=\"dijitReset dijitSliderDecoration dijitSliderDecorationL dijitSliderDecorationV\"></td\n\t\t><td class=\"dijitReset dijitSliderDecorationC\" style=\"height:100%;\"\n\t\t\t><input data-dojo-attach-point=\"valueNode\" type=\"hidden\" ${!nameAttrSetting}\n\t\t\t/><center class=\"dijitReset dijitSliderBarContainerV\" role=\"presentation\" data-dojo-attach-point=\"sliderBarContainer\"\n\t\t\t\t><div role=\"presentation\" data-dojo-attach-point=\"remainingBar\" class=\"dijitSliderBar dijitSliderBarV dijitSliderRemainingBar dijitSliderRemainingBarV\" data-dojo-attach-event=\"press:_onBarClick\"><!--#5629--></div\n\t\t\t\t><div role=\"presentation\" data-dojo-attach-point=\"progressBar\" class=\"dijitSliderBar dijitSliderBarV dijitSliderProgressBar dijitSliderProgressBarV\" data-dojo-attach-event=\"press:_onBarClick\"\n\t\t\t\t\t><div class=\"dijitSliderMoveable dijitSliderMoveableV\" style=\"vertical-align:top;\"\n\t\t\t\t\t\t><div data-dojo-attach-point=\"sliderHandle,focusNode\" class=\"dijitSliderImageHandle dijitSliderImageHandleV\" data-dojo-attach-event=\"press:_onHandleClick\" role=\"slider\" valuemin=\"${minimum}\" valuemax=\"${maximum}\"></div\n\t\t\t\t\t></div\n\t\t\t\t></div\n\t\t\t></center\n\t\t></td\n\t\t><td data-dojo-attach-point=\"containerNode,rightDecoration\" class=\"dijitReset dijitSliderDecoration dijitSliderDecorationR dijitSliderDecorationV\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\"></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><center><div class=\"dijitSliderBar dijitSliderBumper dijitSliderBumperV dijitSliderBottomBumper\" data-dojo-attach-event=\"press:_onClkDecBumper\"></div></center\n\t\t></td\n\t\t><td class=\"dijitReset\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\"></td\n\t\t><td class=\"dijitReset dijitSliderButtonContainer dijitSliderButtonContainerV\"\n\t\t\t><div class=\"dijitSliderDecrementIconV\" style=\"display:none\" data-dojo-attach-point=\"incrementButton\"><span class=\"dijitSliderButtonInner\">-</span></div\n\t\t></td\n\t\t><td class=\"dijitReset\"></td\n\t></tr\n></table>\n",
'dijit/layout/LayoutContainer':function(){
define([
"dojo/_base/kernel", // kernel.deprecated
"dojo/_base/lang",
"dojo/_base/declare", // declare
"../_WidgetBase",
"./_LayoutWidget",
"./utils" // layoutUtils.layoutChildren
], function(kernel, lang, declare, _WidgetBase, _LayoutWidget, layoutUtils){
/*=====
var _WidgetBase = dijit._WidgetBase;
var _LayoutWidget = dijit.layout._LayoutWidget;
=====*/
// module:
// dijit/layout/LayoutContainer
// summary:
// Deprecated. Use `dijit.layout.BorderContainer` instead.
// This argument can be specified for the children of a LayoutContainer.
// Since any widget can be specified as a LayoutContainer child, mix it
// into the base widget class. (This is a hack, but it's effective.)
lang.extend(_WidgetBase, {
// layoutAlign: String
// "none", "left", "right", "bottom", "top", and "client".
// See the LayoutContainer description for details on this parameter.
layoutAlign: 'none'
});
return declare("dijit.layout.LayoutContainer", _LayoutWidget, {
// summary:
// Deprecated. Use `dijit.layout.BorderContainer` instead.
//
// description:
// Provides Delphi-style panel layout semantics.
//
// A LayoutContainer is a box with a specified size (like style="width: 500px; height: 500px;"),
// that contains children widgets marked with "layoutAlign" of "left", "right", "bottom", "top", and "client".
// It takes it's children marked as left/top/bottom/right, and lays them out along the edges of the box,
// and then it takes the child marked "client" and puts it into the remaining space in the middle.
//
// Left/right positioning is similar to CSS's "float: left" and "float: right",
// and top/bottom positioning would be similar to "float: top" and "float: bottom", if there were such
// CSS.
//
// Note that there can only be one client element, but there can be multiple left, right, top,
// or bottom elements.
//
// example:
// | <style>
// | html, body{ height: 100%; width: 100%; }
// | </style>
// | <div data-dojo-type="dijit.layout.LayoutContainer" style="width: 100%; height: 100%">
// | <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="layoutAlign: 'top'">header text</div>
// | <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="layoutAlign: 'left'" style="width: 200px;">table of contents</div>
// | <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="layoutAlign: 'client'">client area</div>
// | </div>
//
// Lays out each child in the natural order the children occur in.
// Basically each child is laid out into the "remaining space", where "remaining space" is initially
// the content area of this widget, but is reduced to a smaller rectangle each time a child is added.
// tags:
// deprecated
baseClass: "dijitLayoutContainer",
constructor: function(){
kernel.deprecated("dijit.layout.LayoutContainer is deprecated", "use BorderContainer instead", 2.0);
},
layout: function(){
layoutUtils.layoutChildren(this.domNode, this._contentBox, this.getChildren());
},
addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
this.inherited(arguments);
if(this._started){
layoutUtils.layoutChildren(this.domNode, this._contentBox, this.getChildren());
}
},
removeChild: function(/*dijit._Widget*/ widget){
this.inherited(arguments);
if(this._started){
layoutUtils.layoutChildren(this.domNode, this._contentBox, this.getChildren());
}
}
});
});
},
'dijit/Tooltip':function(){
define([
"dojo/_base/array", // array.forEach array.indexOf array.map
"dojo/_base/declare", // declare
"dojo/_base/fx", // fx.fadeIn fx.fadeOut
"dojo/dom", // dom.byId
"dojo/dom-class", // domClass.add
"dojo/dom-geometry", // domGeometry.getMarginBox domGeometry.position
"dojo/dom-style", // domStyle.set, domStyle.get
"dojo/_base/lang", // lang.hitch lang.isArrayLike
"dojo/_base/sniff", // has("ie")
"dojo/_base/window", // win.body
"./_base/manager", // manager.defaultDuration
"./place",
"./_Widget",
"./_TemplatedMixin",
"./BackgroundIframe",
"dojo/text!./templates/Tooltip.html",
"." // sets dijit.showTooltip etc. for back-compat
], function(array, declare, fx, dom, domClass, domGeometry, domStyle, lang, has, win,
manager, place, _Widget, _TemplatedMixin, BackgroundIframe, template, dijit){
/*=====
var _Widget = dijit._Widget;
var BackgroundIframe = dijit.BackgroundIframe;
var _TemplatedMixin = dijit._TemplatedMixin;
=====*/
// module:
// dijit/Tooltip
// summary:
// Defines dijit.Tooltip widget (to display a tooltip), showTooltip()/hideTooltip(), and _MasterTooltip
var MasterTooltip = declare("dijit._MasterTooltip", [_Widget, _TemplatedMixin], {
// summary:
// Internal widget that holds the actual tooltip markup,
// which occurs once per page.
// Called by Tooltip widgets which are just containers to hold
// the markup
// tags:
// protected
// duration: Integer
// Milliseconds to fade in/fade out
duration: manager.defaultDuration,
templateString: template,
postCreate: function(){
win.body().appendChild(this.domNode);
this.bgIframe = new BackgroundIframe(this.domNode);
// Setup fade-in and fade-out functions.
this.fadeIn = fx.fadeIn({ node: this.domNode, duration: this.duration, onEnd: lang.hitch(this, "_onShow") });
this.fadeOut = fx.fadeOut({ node: this.domNode, duration: this.duration, onEnd: lang.hitch(this, "_onHide") });
},
show: function(innerHTML, aroundNode, position, rtl, textDir){
// summary:
// Display tooltip w/specified contents to right of specified node
// (To left if there's no space on the right, or if rtl == true)
// innerHTML: String
// Contents of the tooltip
// aroundNode: DomNode || dijit.__Rectangle
// Specifies that tooltip should be next to this node / area
// position: String[]?
// List of positions to try to position tooltip (ex: ["right", "above"])
// rtl: Boolean?
// Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
// means "rtl"; specifies GUI direction, not text direction.
// textDir: String?
// Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
if(this.aroundNode && this.aroundNode === aroundNode && this.containerNode.innerHTML == innerHTML){
return;
}
// reset width; it may have been set by orient() on a previous tooltip show()
this.domNode.width = "auto";
if(this.fadeOut.status() == "playing"){
// previous tooltip is being hidden; wait until the hide completes then show new one
this._onDeck=arguments;
return;
}
this.containerNode.innerHTML=innerHTML;
this.set("textDir", textDir);
this.containerNode.align = rtl? "right" : "left"; //fix the text alignment
var pos = place.around(this.domNode, aroundNode,
position && position.length ? position : Tooltip.defaultPosition, !rtl, lang.hitch(this, "orient"));
// Position the tooltip connector for middle alignment.
// This could not have been done in orient() since the tooltip wasn't positioned at that time.
var aroundNodeCoords = pos.aroundNodePos;
if(pos.corner.charAt(0) == 'M' && pos.aroundCorner.charAt(0) == 'M'){
this.connectorNode.style.top = aroundNodeCoords.y + ((aroundNodeCoords.h - this.connectorNode.offsetHeight) >> 1) - pos.y + "px";
this.connectorNode.style.left = "";
}else if(pos.corner.charAt(1) == 'M' && pos.aroundCorner.charAt(1) == 'M'){
this.connectorNode.style.left = aroundNodeCoords.x + ((aroundNodeCoords.w - this.connectorNode.offsetWidth) >> 1) - pos.x + "px";
}
// show it
domStyle.set(this.domNode, "opacity", 0);
this.fadeIn.play();
this.isShowingNow = true;
this.aroundNode = aroundNode;
},
orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ tooltipCorner, /*Object*/ spaceAvailable, /*Object*/ aroundNodeCoords){
// summary:
// Private function to set CSS for tooltip node based on which position it's in.
// This is called by the dijit popup code. It will also reduce the tooltip's
// width to whatever width is available
// tags:
// protected
this.connectorNode.style.top = ""; //reset to default
//Adjust the spaceAvailable width, without changing the spaceAvailable object
var tooltipSpaceAvaliableWidth = spaceAvailable.w - this.connectorNode.offsetWidth;
node.className = "dijitTooltip " +
{
"MR-ML": "dijitTooltipRight",
"ML-MR": "dijitTooltipLeft",
"TM-BM": "dijitTooltipAbove",
"BM-TM": "dijitTooltipBelow",
"BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
"TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
"BR-TR": "dijitTooltipBelow dijitTooltipABRight",
"TR-BR": "dijitTooltipAbove dijitTooltipABRight",
"BR-BL": "dijitTooltipRight",
"BL-BR": "dijitTooltipLeft"
}[aroundCorner + "-" + tooltipCorner];
// reduce tooltip's width to the amount of width available, so that it doesn't overflow screen
this.domNode.style.width = "auto";
var size = domGeometry.getContentBox(this.domNode);
var width = Math.min((Math.max(tooltipSpaceAvaliableWidth,1)), size.w);
var widthWasReduced = width < size.w;
this.domNode.style.width = width+"px";
//Adjust width for tooltips that have a really long word or a nowrap setting
if(widthWasReduced){
this.containerNode.style.overflow = "auto"; //temp change to overflow to detect if our tooltip needs to be wider to support the content
var scrollWidth = this.containerNode.scrollWidth;
this.containerNode.style.overflow = "visible"; //change it back
if(scrollWidth > width){
scrollWidth = scrollWidth + domStyle.get(this.domNode,"paddingLeft") + domStyle.get(this.domNode,"paddingRight");
this.domNode.style.width = scrollWidth + "px";
}
}
// Reposition the tooltip connector.
if(tooltipCorner.charAt(0) == 'B' && aroundCorner.charAt(0) == 'B'){
var mb = domGeometry.getMarginBox(node);
var tooltipConnectorHeight = this.connectorNode.offsetHeight;
if(mb.h > spaceAvailable.h){
// The tooltip starts at the top of the page and will extend past the aroundNode
var aroundNodePlacement = spaceAvailable.h - ((aroundNodeCoords.h + tooltipConnectorHeight) >> 1);
this.connectorNode.style.top = aroundNodePlacement + "px";
this.connectorNode.style.bottom = "";
}else{
// Align center of connector with center of aroundNode, except don't let bottom
// of connector extend below bottom of tooltip content, or top of connector
// extend past top of tooltip content
this.connectorNode.style.bottom = Math.min(
Math.max(aroundNodeCoords.h/2 - tooltipConnectorHeight/2, 0),
mb.h - tooltipConnectorHeight) + "px";
this.connectorNode.style.top = "";
}
}else{
// reset the tooltip back to the defaults
this.connectorNode.style.top = "";
this.connectorNode.style.bottom = "";
}
return Math.max(0, size.w - tooltipSpaceAvaliableWidth);
},
_onShow: function(){
// summary:
// Called at end of fade-in operation
// tags:
// protected
if(has("ie")){
// the arrow won't show up on a node w/an opacity filter
this.domNode.style.filter="";
}
},
hide: function(aroundNode){
// summary:
// Hide the tooltip
if(this._onDeck && this._onDeck[1] == aroundNode){
// this hide request is for a show() that hasn't even started yet;
// just cancel the pending show()
this._onDeck=null;
}else if(this.aroundNode === aroundNode){
// this hide request is for the currently displayed tooltip
this.fadeIn.stop();
this.isShowingNow = false;
this.aroundNode = null;
this.fadeOut.play();
}else{
// just ignore the call, it's for a tooltip that has already been erased
}
},
_onHide: function(){
// summary:
// Called at end of fade-out operation
// tags:
// protected
this.domNode.style.cssText=""; // to position offscreen again
this.containerNode.innerHTML="";
if(this._onDeck){
// a show request has been queued up; do it now
this.show.apply(this, this._onDeck);
this._onDeck=null;
}
},
_setAutoTextDir: function(/*Object*/node){
// summary:
// Resolve "auto" text direction for children nodes
// tags:
// private
this.applyTextDir(node, has("ie") ? node.outerText : node.textContent);
array.forEach(node.children, function(child){this._setAutoTextDir(child); }, this);
},
_setTextDirAttr: function(/*String*/ textDir){
// summary:
// Setter for textDir.
// description:
// Users shouldn't call this function; they should be calling
// set('textDir', value)
// tags:
// private
this._set("textDir", typeof textDir != 'undefined'? textDir : "");
if (textDir == "auto"){
this._setAutoTextDir(this.containerNode);
}else{
this.containerNode.dir = this.textDir;
}
}
});
dijit.showTooltip = function(innerHTML, aroundNode, position, rtl, textDir){
// summary:
// Static method to display tooltip w/specified contents in specified position.
// See description of dijit.Tooltip.defaultPosition for details on position parameter.
// If position is not specified then dijit.Tooltip.defaultPosition is used.
// innerHTML: String
// Contents of the tooltip
// aroundNode: dijit.__Rectangle
// Specifies that tooltip should be next to this node / area
// position: String[]?
// List of positions to try to position tooltip (ex: ["right", "above"])
// rtl: Boolean?
// Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
// means "rtl"; specifies GUI direction, not text direction.
// textDir: String?
// Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
if(!Tooltip._masterTT){ dijit._masterTT = Tooltip._masterTT = new MasterTooltip(); }
return Tooltip._masterTT.show(innerHTML, aroundNode, position, rtl, textDir);
};
dijit.hideTooltip = function(aroundNode){
// summary:
// Static method to hide the tooltip displayed via showTooltip()
return Tooltip._masterTT && Tooltip._masterTT.hide(aroundNode);
};
var Tooltip = declare("dijit.Tooltip", _Widget, {
// summary:
// Pops up a tooltip (a help message) when you hover over a node.
// label: String
// Text to display in the tooltip.
// Specified as innerHTML when creating the widget from markup.
label: "",
// showDelay: Integer
// Number of milliseconds to wait after hovering over/focusing on the object, before
// the tooltip is displayed.
showDelay: 400,
// connectId: String|String[]
// Id of domNode(s) to attach the tooltip to.
// When user hovers over specified dom node, the tooltip will appear.
connectId: [],
// position: String[]
// See description of `dijit.Tooltip.defaultPosition` for details on position parameter.
position: [],
_setConnectIdAttr: function(/*String|String[]*/ newId){
// summary:
// Connect to specified node(s)
// Remove connections to old nodes (if there are any)
array.forEach(this._connections || [], function(nested){
array.forEach(nested, lang.hitch(this, "disconnect"));
}, this);
// Make array of id's to connect to, excluding entries for nodes that don't exist yet, see startup()
this._connectIds = array.filter(lang.isArrayLike(newId) ? newId : (newId ? [newId] : []),
function(id){ return dom.byId(id); });
// Make connections
this._connections = array.map(this._connectIds, function(id){
var node = dom.byId(id);
return [
this.connect(node, "onmouseenter", "_onHover"),
this.connect(node, "onmouseleave", "_onUnHover"),
this.connect(node, "onfocus", "_onHover"),
this.connect(node, "onblur", "_onUnHover")
];
}, this);
this._set("connectId", newId);
},
addTarget: function(/*DOMNODE || String*/ node){
// summary:
// Attach tooltip to specified node if it's not already connected
// TODO: remove in 2.0 and just use set("connectId", ...) interface
var id = node.id || node;
if(array.indexOf(this._connectIds, id) == -1){
this.set("connectId", this._connectIds.concat(id));
}
},
removeTarget: function(/*DomNode || String*/ node){
// summary:
// Detach tooltip from specified node
// TODO: remove in 2.0 and just use set("connectId", ...) interface
var id = node.id || node, // map from DOMNode back to plain id string
idx = array.indexOf(this._connectIds, id);
if(idx >= 0){
// remove id (modifies original this._connectIds but that's OK in this case)
this._connectIds.splice(idx, 1);
this.set("connectId", this._connectIds);
}
},
buildRendering: function(){
this.inherited(arguments);
domClass.add(this.domNode,"dijitTooltipData");
},
startup: function(){
this.inherited(arguments);
// If this tooltip was created in a template, or for some other reason the specified connectId[s]
// didn't exist during the widget's initialization, then connect now.
var ids = this.connectId;
array.forEach(lang.isArrayLike(ids) ? ids : [ids], this.addTarget, this);
},
_onHover: function(/*Event*/ e){
// summary:
// Despite the name of this method, it actually handles both hover and focus
// events on the target node, setting a timer to show the tooltip.
// tags:
// private
if(!this._showTimer){
var target = e.target;
this._showTimer = setTimeout(lang.hitch(this, function(){this.open(target)}), this.showDelay);
}
},
_onUnHover: function(/*Event*/ /*===== e =====*/){
// summary:
// Despite the name of this method, it actually handles both mouseleave and blur
// events on the target node, hiding the tooltip.
// tags:
// private
// keep a tooltip open if the associated element still has focus (even though the
// mouse moved away)
if(this._focus){ return; }
if(this._showTimer){
clearTimeout(this._showTimer);
delete this._showTimer;
}
this.close();
},
open: function(/*DomNode*/ target){
// summary:
// Display the tooltip; usually not called directly.
// tags:
// private
if(this._showTimer){
clearTimeout(this._showTimer);
delete this._showTimer;
}
Tooltip.show(this.label || this.domNode.innerHTML, target, this.position, !this.isLeftToRight(), this.textDir);
this._connectNode = target;
this.onShow(target, this.position);
},
close: function(){
// summary:
// Hide the tooltip or cancel timer for show of tooltip
// tags:
// private
if(this._connectNode){
// if tooltip is currently shown
Tooltip.hide(this._connectNode);
delete this._connectNode;
this.onHide();
}
if(this._showTimer){
// if tooltip is scheduled to be shown (after a brief delay)
clearTimeout(this._showTimer);
delete this._showTimer;
}
},
onShow: function(/*===== target, position =====*/){
// summary:
// Called when the tooltip is shown
// tags:
// callback
},
onHide: function(){
// summary:
// Called when the tooltip is hidden
// tags:
// callback
},
uninitialize: function(){
this.close();
this.inherited(arguments);
}
});
Tooltip._MasterTooltip = MasterTooltip; // for monkey patching
Tooltip.show = dijit.showTooltip; // export function through module return value
Tooltip.hide = dijit.hideTooltip; // export function through module return value
// dijit.Tooltip.defaultPosition: String[]
// This variable controls the position of tooltips, if the position is not specified to
// the Tooltip widget or *TextBox widget itself. It's an array of strings with the values
// possible for `dijit/place::around()`. The recommended values are:
//
// * before-centered: centers tooltip to the left of the anchor node/widget, or to the right
// in the case of RTL scripts like Hebrew and Arabic
// * after-centered: centers tooltip to the right of the anchor node/widget, or to the left
// in the case of RTL scripts like Hebrew and Arabic
// * above-centered: tooltip is centered above anchor node
// * below-centered: tooltip is centered above anchor node
//
// The list is positions is tried, in order, until a position is found where the tooltip fits
// within the viewport.
//
// Be careful setting this parameter. A value of "above-centered" may work fine until the user scrolls
// the screen so that there's no room above the target node. Nodes with drop downs, like
// DropDownButton or FilteringSelect, are especially problematic, in that you need to be sure
// that the drop down and tooltip don't overlap, even when the viewport is scrolled so that there
// is only room below (or above) the target node, but not both.
Tooltip.defaultPosition = ["after-centered", "before-centered"];
return Tooltip;
});
},
'url:dijit/templates/MenuSeparator.html':"<tr class=\"dijitMenuSeparator\">\n\t<td class=\"dijitMenuSeparatorIconCell\">\n\t\t<div class=\"dijitMenuSeparatorTop\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n\t<td colspan=\"3\" class=\"dijitMenuSeparatorLabelCell\">\n\t\t<div class=\"dijitMenuSeparatorTop dijitMenuSeparatorLabel\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n</tr>",
'dijit/form/VerticalSlider':function(){
define([
"dojo/_base/declare", // declare
"./HorizontalSlider",
"dojo/text!./templates/VerticalSlider.html"
], function(declare, HorizontalSlider, template){
/*=====
var HorizontalSlider = dijit.form.HorizontalSlider;
=====*/
// module:
// dijit/form/VerticalSlider
// summary:
// A form widget that allows one to select a value with a vertically draggable handle
return declare("dijit.form.VerticalSlider", HorizontalSlider, {
// summary:
// A form widget that allows one to select a value with a vertically draggable handle
templateString: template,
_mousePixelCoord: "pageY",
_pixelCount: "h",
_startingPixelCoord: "y",
_handleOffsetCoord: "top",
_progressPixelSize: "height",
// _descending: Boolean
// Specifies if the slider values go from high-on-top (true), or low-on-top (false)
// TODO: expose this in 1.2 - the css progress/remaining bar classes need to be reversed
_descending: true,
_isReversed: function(){
// summary:
// Overrides HorizontalSlider._isReversed.
// Indicates if values are high on top (with low numbers on the bottom).
return this._descending;
}
});
});
},
'dijit/form/DropDownButton':function(){
define([
"dojo/_base/declare", // declare
"dojo/_base/lang", // hitch
"dojo/query", // query
"../registry", // registry.byNode
"../popup", // dijit.popup2.hide
"./Button",
"../_Container",
"../_HasDropDown",
"dojo/text!./templates/DropDownButton.html"
], function(declare, lang, query, registry, popup, Button, _Container, _HasDropDown, template){
/*=====
Button = dijit.form.Button;
_Container = dijit._Container;
_HasDropDown = dijit._HasDropDown;
=====*/
// module:
// dijit/form/DropDownButton
// summary:
// A button with a drop down
return declare("dijit.form.DropDownButton", [Button, _Container, _HasDropDown], {
// summary:
// A button with a drop down
//
// example:
// | <button data-dojo-type="dijit.form.DropDownButton">
// | Hello world
// | <div data-dojo-type="dijit.Menu">...</div>
// | </button>
//
// example:
// | var button1 = new dijit.form.DropDownButton({ label: "hi", dropDown: new dijit.Menu(...) });
// | win.body().appendChild(button1);
//
baseClass : "dijitDropDownButton",
templateString: template,
_fillContent: function(){
// Overrides Button._fillContent().
//
// My inner HTML contains both the button contents and a drop down widget, like
// <DropDownButton> <span>push me</span> <Menu> ... </Menu> </DropDownButton>
// The first node is assumed to be the button content. The widget is the popup.
if(this.srcNodeRef){ // programatically created buttons might not define srcNodeRef
//FIXME: figure out how to filter out the widget and use all remaining nodes as button
// content, not just nodes[0]
var nodes = query("*", this.srcNodeRef);
this.inherited(arguments, [nodes[0]]);
// save pointer to srcNode so we can grab the drop down widget after it's instantiated
this.dropDownContainer = this.srcNodeRef;
}
},
startup: function(){
if(this._started){ return; }
// the child widget from srcNodeRef is the dropdown widget. Insert it in the page DOM,
// make it invisible, and store a reference to pass to the popup code.
if(!this.dropDown && this.dropDownContainer){
var dropDownNode = query("[widgetId]", this.dropDownContainer)[0];
this.dropDown = registry.byNode(dropDownNode);
delete this.dropDownContainer;
}
if(this.dropDown){
popup.hide(this.dropDown);
}
this.inherited(arguments);
},
isLoaded: function(){
// Returns whether or not we are loaded - if our dropdown has an href,
// then we want to check that.
var dropDown = this.dropDown;
return (!!dropDown && (!dropDown.href || dropDown.isLoaded));
},
loadDropDown: function(/*Function*/ callback){
// Default implementation assumes that drop down already exists,
// but hasn't loaded it's data (ex: ContentPane w/href).
// App must override if the drop down is lazy-created.
var dropDown = this.dropDown;
var handler = dropDown.on("load", lang.hitch(this, function(){
handler.remove();
callback();
}));
dropDown.refresh(); // tell it to load
},
isFocusable: function(){
// Overridden so that focus is handled by the _HasDropDown mixin, not by
// the _FormWidget mixin.
return this.inherited(arguments) && !this._mouseDown;
}
});
});
},
'url:dijit/templates/ProgressBar.html':"<div class=\"dijitProgressBar dijitProgressBarEmpty\" role=\"progressbar\"\n\t><div data-dojo-attach-point=\"internalProgress\" class=\"dijitProgressBarFull\"\n\t\t><div class=\"dijitProgressBarTile\" role=\"presentation\"></div\n\t\t><span style=\"visibility:hidden\">&#160;</span\n\t></div\n\t><div data-dojo-attach-point=\"labelNode\" class=\"dijitProgressBarLabel\" id=\"${id}_label\"></div\n\t><img data-dojo-attach-point=\"indeterminateHighContrastImage\" class=\"dijitProgressBarIndeterminateHighContrastImage\" alt=\"\"\n/></div>\n",
'dojo/date':function(){
define(["./_base/kernel", "./_base/lang"], function(dojo, lang) {
// module:
// dojo/date
// summary:
// TODOC
lang.getObject("date", true, dojo);
/*=====
dojo.date = {
// summary: Date manipulation utilities
}
=====*/
dojo.date.getDaysInMonth = function(/*Date*/dateObject){
// summary:
// Returns the number of days in the month used by dateObject
var month = dateObject.getMonth();
var days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
if(month == 1 && dojo.date.isLeapYear(dateObject)){ return 29; } // Number
return days[month]; // Number
};
dojo.date.isLeapYear = function(/*Date*/dateObject){
// summary:
// Determines if the year of the dateObject is a leap year
// description:
// Leap years are years with an additional day YYYY-02-29, where the
// year number is a multiple of four with the following exception: If
// a year is a multiple of 100, then it is only a leap year if it is
// also a multiple of 400. For example, 1900 was not a leap year, but
// 2000 is one.
var year = dateObject.getFullYear();
return !(year%400) || (!(year%4) && !!(year%100)); // Boolean
};
// FIXME: This is not localized
dojo.date.getTimezoneName = function(/*Date*/dateObject){
// summary:
// Get the user's time zone as provided by the browser
// dateObject:
// Needed because the timezone may vary with time (daylight savings)
// description:
// Try to get time zone info from toString or toLocaleString method of
// the Date object -- UTC offset is not a time zone. See
// http://www.twinsun.com/tz/tz-link.htm Note: results may be
// inconsistent across browsers.
var str = dateObject.toString(); // Start looking in toString
var tz = ''; // The result -- return empty string if nothing found
var match;
// First look for something in parentheses -- fast lookup, no regex
var pos = str.indexOf('(');
if(pos > -1){
tz = str.substring(++pos, str.indexOf(')'));
}else{
// If at first you don't succeed ...
// If IE knows about the TZ, it appears before the year
// Capital letters or slash before a 4-digit year
// at the end of string
var pat = /([A-Z\/]+) \d{4}$/;
if((match = str.match(pat))){
tz = match[1];
}else{
// Some browsers (e.g. Safari) glue the TZ on the end
// of toLocaleString instead of putting it in toString
str = dateObject.toLocaleString();
// Capital letters or slash -- end of string,
// after space
pat = / ([A-Z\/]+)$/;
if((match = str.match(pat))){
tz = match[1];
}
}
}
// Make sure it doesn't somehow end up return AM or PM
return (tz == 'AM' || tz == 'PM') ? '' : tz; // String
};
// Utility methods to do arithmetic calculations with Dates
dojo.date.compare = function(/*Date*/date1, /*Date?*/date2, /*String?*/portion){
// summary:
// Compare two date objects by date, time, or both.
// description:
// Returns 0 if equal, positive if a > b, else negative.
// date1:
// Date object
// date2:
// Date object. If not specified, the current Date is used.
// portion:
// A string indicating the "date" or "time" portion of a Date object.
// Compares both "date" and "time" by default. One of the following:
// "date", "time", "datetime"
// Extra step required in copy for IE - see #3112
date1 = new Date(+date1);
date2 = new Date(+(date2 || new Date()));
if(portion == "date"){
// Ignore times and compare dates.
date1.setHours(0, 0, 0, 0);
date2.setHours(0, 0, 0, 0);
}else if(portion == "time"){
// Ignore dates and compare times.
date1.setFullYear(0, 0, 0);
date2.setFullYear(0, 0, 0);
}
if(date1 > date2){ return 1; } // int
if(date1 < date2){ return -1; } // int
return 0; // int
};
dojo.date.add = function(/*Date*/date, /*String*/interval, /*int*/amount){
// summary:
// Add to a Date in intervals of different size, from milliseconds to years
// date: Date
// Date object to start with
// interval:
// A string representing the interval. One of the following:
// "year", "month", "day", "hour", "minute", "second",
// "millisecond", "quarter", "week", "weekday"
// amount:
// How much to add to the date.
var sum = new Date(+date); // convert to Number before copying to accomodate IE (#3112)
var fixOvershoot = false;
var property = "Date";
switch(interval){
case "day":
break;
case "weekday":
//i18n FIXME: assumes Saturday/Sunday weekend, but this is not always true. see dojo.cldr.supplemental
// Divide the increment time span into weekspans plus leftover days
// e.g., 8 days is one 5-day weekspan / and two leftover days
// Can't have zero leftover days, so numbers divisible by 5 get
// a days value of 5, and the remaining days make up the number of weeks
var days, weeks;
var mod = amount % 5;
if(!mod){
days = (amount > 0) ? 5 : -5;
weeks = (amount > 0) ? ((amount-5)/5) : ((amount+5)/5);
}else{
days = mod;
weeks = parseInt(amount/5);
}
// Get weekday value for orig date param
var strt = date.getDay();
// Orig date is Sat / positive incrementer
// Jump over Sun
var adj = 0;
if(strt == 6 && amount > 0){
adj = 1;
}else if(strt == 0 && amount < 0){
// Orig date is Sun / negative incrementer
// Jump back over Sat
adj = -1;
}
// Get weekday val for the new date
var trgt = strt + days;
// New date is on Sat or Sun
if(trgt == 0 || trgt == 6){
adj = (amount > 0) ? 2 : -2;
}
// Increment by number of weeks plus leftover days plus
// weekend adjustments
amount = (7 * weeks) + days + adj;
break;
case "year":
property = "FullYear";
// Keep increment/decrement from 2/29 out of March
fixOvershoot = true;
break;
case "week":
amount *= 7;
break;
case "quarter":
// Naive quarter is just three months
amount *= 3;
// fallthrough...
case "month":
// Reset to last day of month if you overshoot
fixOvershoot = true;
property = "Month";
break;
// case "hour":
// case "minute":
// case "second":
// case "millisecond":
default:
property = "UTC"+interval.charAt(0).toUpperCase() + interval.substring(1) + "s";
}
if(property){
sum["set"+property](sum["get"+property]()+amount);
}
if(fixOvershoot && (sum.getDate() < date.getDate())){
sum.setDate(0);
}
return sum; // Date
};
dojo.date.difference = function(/*Date*/date1, /*Date?*/date2, /*String?*/interval){
// summary:
// Get the difference in a specific unit of time (e.g., number of
// months, weeks, days, etc.) between two dates, rounded to the
// nearest integer.
// date1:
// Date object
// date2:
// Date object. If not specified, the current Date is used.
// interval:
// A string representing the interval. One of the following:
// "year", "month", "day", "hour", "minute", "second",
// "millisecond", "quarter", "week", "weekday"
// Defaults to "day".
date2 = date2 || new Date();
interval = interval || "day";
var yearDiff = date2.getFullYear() - date1.getFullYear();
var delta = 1; // Integer return value
switch(interval){
case "quarter":
var m1 = date1.getMonth();
var m2 = date2.getMonth();
// Figure out which quarter the months are in
var q1 = Math.floor(m1/3) + 1;
var q2 = Math.floor(m2/3) + 1;
// Add quarters for any year difference between the dates
q2 += (yearDiff * 4);
delta = q2 - q1;
break;
case "weekday":
var days = Math.round(dojo.date.difference(date1, date2, "day"));
var weeks = parseInt(dojo.date.difference(date1, date2, "week"));
var mod = days % 7;
// Even number of weeks
if(mod == 0){
days = weeks*5;
}else{
// Weeks plus spare change (< 7 days)
var adj = 0;
var aDay = date1.getDay();
var bDay = date2.getDay();
weeks = parseInt(days/7);
mod = days % 7;
// Mark the date advanced by the number of
// round weeks (may be zero)
var dtMark = new Date(date1);
dtMark.setDate(dtMark.getDate()+(weeks*7));
var dayMark = dtMark.getDay();
// Spare change days -- 6 or less
if(days > 0){
switch(true){
// Range starts on Sat
case aDay == 6:
adj = -1;
break;
// Range starts on Sun
case aDay == 0:
adj = 0;
break;
// Range ends on Sat
case bDay == 6:
adj = -1;
break;
// Range ends on Sun
case bDay == 0:
adj = -2;
break;
// Range contains weekend
case (dayMark + mod) > 5:
adj = -2;
}
}else if(days < 0){
switch(true){
// Range starts on Sat
case aDay == 6:
adj = 0;
break;
// Range starts on Sun
case aDay == 0:
adj = 1;
break;
// Range ends on Sat
case bDay == 6:
adj = 2;
break;
// Range ends on Sun
case bDay == 0:
adj = 1;
break;
// Range contains weekend
case (dayMark + mod) < 0:
adj = 2;
}
}
days += adj;
days -= (weeks*2);
}
delta = days;
break;
case "year":
delta = yearDiff;
break;
case "month":
delta = (date2.getMonth() - date1.getMonth()) + (yearDiff * 12);
break;
case "week":
// Truncate instead of rounding
// Don't use Math.floor -- value may be negative
delta = parseInt(dojo.date.difference(date1, date2, "day")/7);
break;
case "day":
delta /= 24;
// fallthrough
case "hour":
delta /= 60;
// fallthrough
case "minute":
delta /= 60;
// fallthrough
case "second":
delta /= 1000;
// fallthrough
case "millisecond":
delta *= date2.getTime() - date1.getTime();
}
// Round for fractional values and DST leaps
return Math.round(delta); // Number (integer)
};
return dojo.date;
});
},
'dijit/layout/_ContentPaneResizeMixin':function(){
define([
"dojo/_base/array", // array.filter array.forEach
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.has
"dojo/dom-class", // domClass.contains domClass.toggle
"dojo/dom-geometry",// domGeometry.contentBox domGeometry.marginBox
"dojo/_base/lang", // lang.mixin
"dojo/query", // query
"dojo/_base/sniff", // has("ie")
"dojo/_base/window", // win.global
"../registry", // registry.byId
"./utils", // marginBox2contextBox
"../_Contained"
], function(array, declare, domAttr, domClass, domGeometry, lang, query, has, win,
registry, layoutUtils, _Contained){
/*=====
var _Contained = dijit._Contained;
=====*/
// module:
// dijit/layout/_ContentPaneResizeMixin
// summary:
// Resize() functionality of ContentPane. If there's a single layout widget
// child then it will call resize() with the same dimensions as the ContentPane.
// Otherwise just calls resize on each child.
return declare("dijit.layout._ContentPaneResizeMixin", null, {
// summary:
// Resize() functionality of ContentPane. If there's a single layout widget
// child then it will call resize() with the same dimensions as the ContentPane.
// Otherwise just calls resize on each child.
//
// Also implements basic startup() functionality, where starting the parent
// will start the children
// doLayout: Boolean
// - false - don't adjust size of children
// - true - if there is a single visible child widget, set it's size to
// however big the ContentPane is
doLayout: true,
// isLayoutContainer: [protected] Boolean
// Indicates that this widget will call resize() on it's child widgets
// when they become visible.
isLayoutContainer: true,
startup: function(){
// summary:
// See `dijit.layout._LayoutWidget.startup` for description.
// Although ContentPane doesn't extend _LayoutWidget, it does implement
// the same API.
if(this._started){ return; }
var parent = this.getParent();
this._childOfLayoutWidget = parent && parent.isLayoutContainer;
// I need to call resize() on my child/children (when I become visible), unless
// I'm the child of a layout widget in which case my parent will call resize() on me and I'll do it then.
this._needLayout = !this._childOfLayoutWidget;
this.inherited(arguments);
if(this._isShown()){
this._onShow();
}
if(!this._childOfLayoutWidget){
// If my parent isn't a layout container, since my style *may be* width=height=100%
// or something similar (either set directly or via a CSS class),
// monitor when my size changes so that I can re-layout.
// For browsers where I can't directly monitor when my size changes,
// monitor when the viewport changes size, which *may* indicate a size change for me.
this.connect(has("ie") ? this.domNode : win.global, 'onresize', function(){
// Using function(){} closure to ensure no arguments to resize.
this._needLayout = !this._childOfLayoutWidget;
this.resize();
});
}
},
_checkIfSingleChild: function(){
// summary:
// Test if we have exactly one visible widget as a child,
// and if so assume that we are a container for that widget,
// and should propagate startup() and resize() calls to it.
// Skips over things like data stores since they aren't visible.
var childNodes = query("> *", this.containerNode).filter(function(node){
return node.tagName !== "SCRIPT"; // or a regexp for hidden elements like script|area|map|etc..
}),
childWidgetNodes = childNodes.filter(function(node){
return domAttr.has(node, "data-dojo-type") || domAttr.has(node, "dojoType") || domAttr.has(node, "widgetId");
}),
candidateWidgets = array.filter(childWidgetNodes.map(registry.byNode), function(widget){
return widget && widget.domNode && widget.resize;
});
if(
// all child nodes are widgets
childNodes.length == childWidgetNodes.length &&
// all but one are invisible (like dojo.data)
candidateWidgets.length == 1
){
this._singleChild = candidateWidgets[0];
}else{
delete this._singleChild;
}
// So we can set overflow: hidden to avoid a safari bug w/scrollbars showing up (#9449)
domClass.toggle(this.containerNode, this.baseClass + "SingleChild", !!this._singleChild);
},
resize: function(changeSize, resultSize){
// summary:
// See `dijit.layout._LayoutWidget.resize` for description.
// Although ContentPane doesn't extend _LayoutWidget, it does implement
// the same API.
// For the TabContainer --> BorderContainer --> ContentPane case, _onShow() is
// never called, so resize() is our trigger to do the initial href download (see [20099]).
// However, don't load href for closed TitlePanes.
if(!this._wasShown && this.open !== false){
this._onShow();
}
this._resizeCalled = true;
this._scheduleLayout(changeSize, resultSize);
},
_scheduleLayout: function(changeSize, resultSize){
// summary:
// Resize myself, and call resize() on each of my child layout widgets, either now
// (if I'm currently visible) or when I become visible
if(this._isShown()){
this._layout(changeSize, resultSize);
}else{
this._needLayout = true;
this._changeSize = changeSize;
this._resultSize = resultSize;
}
},
_layout: function(changeSize, resultSize){
// summary:
// Resize myself according to optional changeSize/resultSize parameters, like a layout widget.
// Also, since I am a Container widget, each of my children expects me to
// call resize() or layout() on them.
//
// Should be called on initialization and also whenever we get new content
// (from an href, or from set('content', ...))... but deferred until
// the ContentPane is visible
// Set margin box size, unless it wasn't specified, in which case use current size.
if(changeSize){
domGeometry.setMarginBox(this.domNode, changeSize);
}
// Compute content box size of containerNode in case we [later] need to size our single child.
var cn = this.containerNode;
if(cn === this.domNode){
// If changeSize or resultSize was passed to this method and this.containerNode ==
// this.domNode then we can compute the content-box size without querying the node,
// which is more reliable (similar to LayoutWidget.resize) (see for example #9449).
var mb = resultSize || {};
lang.mixin(mb, changeSize || {}); // changeSize overrides resultSize
if(!("h" in mb) || !("w" in mb)){
mb = lang.mixin(domGeometry.getMarginBox(cn), mb); // just use domGeometry.setMarginBox() to fill in missing values
}
this._contentBox = layoutUtils.marginBox2contentBox(cn, mb);
}else{
this._contentBox = domGeometry.getContentBox(cn);
}
this._layoutChildren();
delete this._needLayout;
},
_layoutChildren: function(){
// Call _checkIfSingleChild() again in case app has manually mucked w/the content
// of the ContentPane (rather than changing it through the set("content", ...) API.
if(this.doLayout){
this._checkIfSingleChild();
}
if(this._singleChild && this._singleChild.resize){
var cb = this._contentBox || domGeometry.getContentBox(this.containerNode);
// note: if widget has padding this._contentBox will have l and t set,
// but don't pass them to resize() or it will doubly-offset the child
this._singleChild.resize({w: cb.w, h: cb.h});
}else{
// All my child widgets are independently sized (rather than matching my size),
// but I still need to call resize() on each child to make it layout.
array.forEach(this.getChildren(), function(widget){
if(widget.resize){
widget.resize();
}
});
}
},
_isShown: function(){
// summary:
// Returns true if the content is currently shown.
// description:
// If I am a child of a layout widget then it actually returns true if I've ever been visible,
// not whether I'm currently visible, since that's much faster than tracing up the DOM/widget
// tree every call, and at least solves the performance problem on page load by deferring loading
// hidden ContentPanes until they are first shown
if(this._childOfLayoutWidget){
// If we are TitlePane, etc - we return that only *IF* we've been resized
if(this._resizeCalled && "open" in this){
return this.open;
}
return this._resizeCalled;
}else if("open" in this){
return this.open; // for TitlePane, etc.
}else{
var node = this.domNode, parent = this.domNode.parentNode;
return (node.style.display != 'none') && (node.style.visibility != 'hidden') && !domClass.contains(node, "dijitHidden") &&
parent && parent.style && (parent.style.display != 'none');
}
},
_onShow: function(){
// summary:
// Called when the ContentPane is made visible
// description:
// For a plain ContentPane, this is called on initialization, from startup().
// If the ContentPane is a hidden pane of a TabContainer etc., then it's
// called whenever the pane is made visible.
//
// Does layout/resize of child widget(s)
if(this._needLayout){
// If a layout has been scheduled for when we become visible, do it now
this._layout(this._changeSize, this._resultSize);
}
this.inherited(arguments);
// Need to keep track of whether ContentPane has been shown (which is different than
// whether or not it's currently visible).
this._wasShown = true;
}
});
});
},
'dijit/form/RangeBoundTextBox':function(){
define([
"dojo/_base/declare", // declare
"dojo/i18n", // i18n.getLocalization
"./MappedTextBox"
], function(declare, i18n, MappedTextBox){
/*=====
var MappedTextBox = dijit.form.MappedTextBox;
=====*/
// module:
// dijit/form/RangeBoundTextBox
// summary:
// Base class for textbox form widgets which defines a range of valid values.
/*=====
dijit.form.RangeBoundTextBox.__Constraints = function(){
// min: Number
// Minimum signed value. Default is -Infinity
// max: Number
// Maximum signed value. Default is +Infinity
this.min = min;
this.max = max;
}
=====*/
return declare("dijit.form.RangeBoundTextBox", MappedTextBox, {
// summary:
// Base class for textbox form widgets which defines a range of valid values.
// rangeMessage: String
// The message to display if value is out-of-range
rangeMessage: "",
/*=====
// constraints: dijit.form.RangeBoundTextBox.__Constraints
constraints: {},
======*/
rangeCheck: function(/*Number*/ primitive, /*dijit.form.RangeBoundTextBox.__Constraints*/ constraints){
// summary:
// Overridable function used to validate the range of the numeric input value.
// tags:
// protected
return ("min" in constraints? (this.compare(primitive,constraints.min) >= 0) : true) &&
("max" in constraints? (this.compare(primitive,constraints.max) <= 0) : true); // Boolean
},
isInRange: function(/*Boolean*/ /*===== isFocused =====*/){
// summary:
// Tests if the value is in the min/max range specified in constraints
// tags:
// protected
return this.rangeCheck(this.get('value'), this.constraints);
},
_isDefinitelyOutOfRange: function(){
// summary:
// Returns true if the value is out of range and will remain
// out of range even if the user types more characters
var val = this.get('value');
var isTooLittle = false;
var isTooMuch = false;
if("min" in this.constraints){
var min = this.constraints.min;
min = this.compare(val, ((typeof min == "number") && min >= 0 && val !=0) ? 0 : min);
isTooLittle = (typeof min == "number") && min < 0;
}
if("max" in this.constraints){
var max = this.constraints.max;
max = this.compare(val, ((typeof max != "number") || max > 0) ? max : 0);
isTooMuch = (typeof max == "number") && max > 0;
}
return isTooLittle || isTooMuch;
},
_isValidSubset: function(){
// summary:
// Overrides `dijit.form.ValidationTextBox._isValidSubset`.
// Returns true if the input is syntactically valid, and either within
// range or could be made in range by more typing.
return this.inherited(arguments) && !this._isDefinitelyOutOfRange();
},
isValid: function(/*Boolean*/ isFocused){
// Overrides dijit.form.ValidationTextBox.isValid to check that the value is also in range.
return this.inherited(arguments) &&
((this._isEmpty(this.textbox.value) && !this.required) || this.isInRange(isFocused)); // Boolean
},
getErrorMessage: function(/*Boolean*/ isFocused){
// Overrides dijit.form.ValidationTextBox.getErrorMessage to print "out of range" message if appropriate
var v = this.get('value');
if(v !== null && v !== '' && v !== undefined && (typeof v != "number" || !isNaN(v)) && !this.isInRange(isFocused)){ // don't check isInRange w/o a real value
return this.rangeMessage; // String
}
return this.inherited(arguments);
},
postMixInProperties: function(){
this.inherited(arguments);
if(!this.rangeMessage){
this.messages = i18n.getLocalization("dijit.form", "validate", this.lang);
this.rangeMessage = this.messages.rangeMessage;
}
},
_setConstraintsAttr: function(/*Object*/ constraints){
this.inherited(arguments);
if(this.focusNode){ // not set when called from postMixInProperties
if(this.constraints.min !== undefined){
this.focusNode.setAttribute("aria-valuemin", this.constraints.min);
}else{
this.focusNode.removeAttribute("aria-valuemin");
}
if(this.constraints.max !== undefined){
this.focusNode.setAttribute("aria-valuemax", this.constraints.max);
}else{
this.focusNode.removeAttribute("aria-valuemax");
}
}
},
_setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange){
// summary:
// Hook so set('value', ...) works.
this.focusNode.setAttribute("aria-valuenow", value);
this.inherited(arguments);
},
applyTextDir: function(/*===== element, text =====*/){
// summary:
// The function overridden in the _BidiSupport module,
// originally used for setting element.dir according to this.textDir.
// In this case does nothing.
// element: Object
// text: String
// tags:
// protected.
}
});
});
},
'dijit/_editor/RichText':function(){
define("dijit/_editor/RichText", [
"dojo/_base/array", // array.forEach array.indexOf array.some
"dojo/_base/config", // config
"dojo/_base/declare", // declare
"dojo/_base/Deferred", // Deferred
"dojo/dom", // dom.byId
"dojo/dom-attr", // domAttr.set or get
"dojo/dom-class", // domClass.add domClass.remove
"dojo/dom-construct", // domConstruct.create domConstruct.destroy domConstruct.place
"dojo/dom-geometry", // domGeometry.getMarginBox domGeometry.position
"dojo/dom-style", // domStyle.getComputedStyle domStyle.set
"dojo/_base/event", // event.stop
"dojo/_base/kernel", // kernel.deprecated
"dojo/keys", // keys.BACKSPACE keys.TAB
"dojo/_base/lang", // lang.clone lang.hitch lang.isArray lang.isFunction lang.isString lang.trim
"dojo/on", // on()
"dojo/query", // query
"dojo/ready", // ready
"dojo/_base/sniff", // has("ie") has("mozilla") has("opera") has("safari") has("webkit")
"dojo/topic", // topic.publish() (publish)
"dojo/_base/unload", // unload
"dojo/_base/url", // url
"dojo/_base/window", // win.body win.doc.body.focus win.doc.createElement win.global.location win.withGlobal
"../_Widget",
"../_CssStateMixin",
"./selection",
"./range",
"./html",
"../focus",
".." // dijit._scopeName
], function(array, config, declare, Deferred, dom, domAttr, domClass, domConstruct, domGeometry, domStyle,
event, kernel, keys, lang, on, query, ready, has, topic, unload, _Url, win,
_Widget, _CssStateMixin, selectionapi, rangeapi, htmlapi, focus, dijit){
/*=====
var _Widget = dijit._Widget;
var _CssStateMixin = dijit._CssStateMixin;
=====*/
// module:
// dijit/_editor/RichText
// summary:
// dijit._editor.RichText is the core of dijit.Editor, which provides basic
// WYSIWYG editing features.
// if you want to allow for rich text saving with back/forward actions, you must add a text area to your page with
// the id==dijit._scopeName + "._editor.RichText.value" (typically "dijit._editor.RichText.value). For example,
// something like this will work:
//
// <textarea id="dijit._editor.RichText.value" style="display:none;position:absolute;top:-100px;left:-100px;height:3px;width:3px;overflow:hidden;"></textarea>
//
var RichText = declare("dijit._editor.RichText", [_Widget, _CssStateMixin], {
// summary:
// dijit._editor.RichText is the core of dijit.Editor, which provides basic
// WYSIWYG editing features.
//
// description:
// dijit._editor.RichText is the core of dijit.Editor, which provides basic
// WYSIWYG editing features. It also encapsulates the differences
// of different js engines for various browsers. Do not use this widget
// with an HTML &lt;TEXTAREA&gt; tag, since the browser unescapes XML escape characters,
// like &lt;. This can have unexpected behavior and lead to security issues
// such as scripting attacks.
//
// tags:
// private
constructor: function(params){
// contentPreFilters: Function(String)[]
// Pre content filter function register array.
// these filters will be executed before the actual
// editing area gets the html content.
this.contentPreFilters = [];
// contentPostFilters: Function(String)[]
// post content filter function register array.
// These will be used on the resulting html
// from contentDomPostFilters. The resulting
// content is the final html (returned by getValue()).
this.contentPostFilters = [];
// contentDomPreFilters: Function(DomNode)[]
// Pre content dom filter function register array.
// These filters are applied after the result from
// contentPreFilters are set to the editing area.
this.contentDomPreFilters = [];
// contentDomPostFilters: Function(DomNode)[]
// Post content dom filter function register array.
// These filters are executed on the editing area dom.
// The result from these will be passed to contentPostFilters.
this.contentDomPostFilters = [];
// editingAreaStyleSheets: dojo._URL[]
// array to store all the stylesheets applied to the editing area
this.editingAreaStyleSheets = [];
// Make a copy of this.events before we start writing into it, otherwise we
// will modify the prototype which leads to bad things on pages w/multiple editors
this.events = [].concat(this.events);
this._keyHandlers = {};
if(params && lang.isString(params.value)){
this.value = params.value;
}
this.onLoadDeferred = new Deferred();
},
baseClass: "dijitEditor",
// inheritWidth: Boolean
// whether to inherit the parent's width or simply use 100%
inheritWidth: false,
// focusOnLoad: [deprecated] Boolean
// Focus into this widget when the page is loaded
focusOnLoad: false,
// name: String?
// Specifies the name of a (hidden) <textarea> node on the page that's used to save
// the editor content on page leave. Used to restore editor contents after navigating
// to a new page and then hitting the back button.
name: "",
// styleSheets: [const] String
// semicolon (";") separated list of css files for the editing area
styleSheets: "",
// height: String
// Set height to fix the editor at a specific height, with scrolling.
// By default, this is 300px. If you want to have the editor always
// resizes to accommodate the content, use AlwaysShowToolbar plugin
// and set height="". If this editor is used within a layout widget,
// set height="100%".
height: "300px",
// minHeight: String
// The minimum height that the editor should have.
minHeight: "1em",
// isClosed: [private] Boolean
isClosed: true,
// isLoaded: [private] Boolean
isLoaded: false,
// _SEPARATOR: [private] String
// Used to concat contents from multiple editors into a single string,
// so they can be saved into a single <textarea> node. See "name" attribute.
_SEPARATOR: "@@**%%__RICHTEXTBOUNDRY__%%**@@",
// _NAME_CONTENT_SEP: [private] String
// USed to separate name from content. Just a colon isn't safe.
_NAME_CONTENT_SEP: "@@**%%:%%**@@",
// onLoadDeferred: [readonly] dojo.Deferred
// Deferred which is fired when the editor finishes loading.
// Call myEditor.onLoadDeferred.then(callback) it to be informed
// when the rich-text area initialization is finalized.
onLoadDeferred: null,
// isTabIndent: Boolean
// Make tab key and shift-tab indent and outdent rather than navigating.
// Caution: sing this makes web pages inaccessible to users unable to use a mouse.
isTabIndent: false,
// disableSpellCheck: [const] Boolean
// When true, disables the browser's native spell checking, if supported.
// Works only in Firefox.
disableSpellCheck: false,
postCreate: function(){
if("textarea" === this.domNode.tagName.toLowerCase()){
console.warn("RichText should not be used with the TEXTAREA tag. See dijit._editor.RichText docs.");
}
// Push in the builtin filters now, making them the first executed, but not over-riding anything
// users passed in. See: #6062
this.contentPreFilters = [lang.hitch(this, "_preFixUrlAttributes")].concat(this.contentPreFilters);
if(has("mozilla")){
this.contentPreFilters = [this._normalizeFontStyle].concat(this.contentPreFilters);
this.contentPostFilters = [this._removeMozBogus].concat(this.contentPostFilters);
}
if(has("webkit")){
// Try to clean up WebKit bogus artifacts. The inserted classes
// made by WebKit sometimes messes things up.
this.contentPreFilters = [this._removeWebkitBogus].concat(this.contentPreFilters);
this.contentPostFilters = [this._removeWebkitBogus].concat(this.contentPostFilters);
}
if(has("ie")){
// IE generates <strong> and <em> but we want to normalize to <b> and <i>
this.contentPostFilters = [this._normalizeFontStyle].concat(this.contentPostFilters);
this.contentDomPostFilters = [lang.hitch(this, this._stripBreakerNodes)].concat(this.contentDomPostFilters);
}
this.inherited(arguments);
topic.publish(dijit._scopeName + "._editor.RichText::init", this);
this.open();
this.setupDefaultShortcuts();
},
setupDefaultShortcuts: function(){
// summary:
// Add some default key handlers
// description:
// Overwrite this to setup your own handlers. The default
// implementation does not use Editor commands, but directly
// executes the builtin commands within the underlying browser
// support.
// tags:
// protected
var exec = lang.hitch(this, function(cmd, arg){
return function(){
return !this.execCommand(cmd,arg);
};
});
var ctrlKeyHandlers = {
b: exec("bold"),
i: exec("italic"),
u: exec("underline"),
a: exec("selectall"),
s: function(){ this.save(true); },
m: function(){ this.isTabIndent = !this.isTabIndent; },
"1": exec("formatblock", "h1"),
"2": exec("formatblock", "h2"),
"3": exec("formatblock", "h3"),
"4": exec("formatblock", "h4"),
"\\": exec("insertunorderedlist")
};
if(!has("ie")){
ctrlKeyHandlers.Z = exec("redo"); //FIXME: undo?
}
var key;
for(key in ctrlKeyHandlers){
this.addKeyHandler(key, true, false, ctrlKeyHandlers[key]);
}
},
// events: [private] String[]
// events which should be connected to the underlying editing area
events: ["onKeyPress", "onKeyDown", "onKeyUp"], // onClick handled specially
// captureEvents: [deprecated] String[]
// Events which should be connected to the underlying editing
// area, events in this array will be addListener with
// capture=true.
// TODO: looking at the code I don't see any distinction between events and captureEvents,
// so get rid of this for 2.0 if not sooner
captureEvents: [],
_editorCommandsLocalized: false,
_localizeEditorCommands: function(){
// summary:
// When IE is running in a non-English locale, the API actually changes,
// so that we have to say (for example) danraku instead of p (for paragraph).
// Handle that here.
// tags:
// private
if(RichText._editorCommandsLocalized){
// Use the already generate cache of mappings.
this._local2NativeFormatNames = RichText._local2NativeFormatNames;
this._native2LocalFormatNames = RichText._native2LocalFormatNames;
return;
}
RichText._editorCommandsLocalized = true;
RichText._local2NativeFormatNames = {};
RichText._native2LocalFormatNames = {};
this._local2NativeFormatNames = RichText._local2NativeFormatNames;
this._native2LocalFormatNames = RichText._native2LocalFormatNames;
//in IE, names for blockformat is locale dependent, so we cache the values here
//put p after div, so if IE returns Normal, we show it as paragraph
//We can distinguish p and div if IE returns Normal, however, in order to detect that,
//we have to call this.document.selection.createRange().parentElement() or such, which
//could slow things down. Leave it as it is for now
var formats = ['div', 'p', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul', 'address'];
var localhtml = "", format, i=0;
while((format=formats[i++])){
//append a <br> after each element to separate the elements more reliably
if(format.charAt(1) !== 'l'){
localhtml += "<"+format+"><span>content</span></"+format+"><br/>";
}else{
localhtml += "<"+format+"><li>content</li></"+format+"><br/>";
}
}
// queryCommandValue returns empty if we hide editNode, so move it out of screen temporary
// Also, IE9 does weird stuff unless we do it inside the editor iframe.
var style = { position: "absolute", top: "0px", zIndex: 10, opacity: 0.01 };
var div = domConstruct.create('div', {style: style, innerHTML: localhtml});
win.body().appendChild(div);
// IE9 has a timing issue with doing this right after setting
// the inner HTML, so put a delay in.
var inject = lang.hitch(this, function(){
var node = div.firstChild;
while(node){
try{
selectionapi.selectElement(node.firstChild);
var nativename = node.tagName.toLowerCase();
this._local2NativeFormatNames[nativename] = document.queryCommandValue("formatblock");
this._native2LocalFormatNames[this._local2NativeFormatNames[nativename]] = nativename;
node = node.nextSibling.nextSibling;
//console.log("Mapped: ", nativename, " to: ", this._local2NativeFormatNames[nativename]);
}catch(e){ /*Sqelch the occasional IE9 error */ }
}
div.parentNode.removeChild(div);
div.innerHTML = "";
});
setTimeout(inject, 0);
},
open: function(/*DomNode?*/ element){
// summary:
// Transforms the node referenced in this.domNode into a rich text editing
// node.
// description:
// Sets up the editing area asynchronously. This will result in
// the creation and replacement with an iframe.
// tags:
// private
if(!this.onLoadDeferred || this.onLoadDeferred.fired >= 0){
this.onLoadDeferred = new Deferred();
}
if(!this.isClosed){ this.close(); }
topic.publish(dijit._scopeName + "._editor.RichText::open", this);
if(arguments.length === 1 && element.nodeName){ // else unchanged
this.domNode = element;
}
var dn = this.domNode;
// "html" will hold the innerHTML of the srcNodeRef and will be used to
// initialize the editor.
var html;
if(lang.isString(this.value)){
// Allow setting the editor content programmatically instead of
// relying on the initial content being contained within the target
// domNode.
html = this.value;
delete this.value;
dn.innerHTML = "";
}else if(dn.nodeName && dn.nodeName.toLowerCase() == "textarea"){
// if we were created from a textarea, then we need to create a
// new editing harness node.
var ta = (this.textarea = dn);
this.name = ta.name;
html = ta.value;
dn = this.domNode = win.doc.createElement("div");
dn.setAttribute('widgetId', this.id);
ta.removeAttribute('widgetId');
dn.cssText = ta.cssText;
dn.className += " " + ta.className;
domConstruct.place(dn, ta, "before");
var tmpFunc = lang.hitch(this, function(){
//some browsers refuse to submit display=none textarea, so
//move the textarea off screen instead
domStyle.set(ta, {
display: "block",
position: "absolute",
top: "-1000px"
});
if(has("ie")){ //nasty IE bug: abnormal formatting if overflow is not hidden
var s = ta.style;
this.__overflow = s.overflow;
s.overflow = "hidden";
}
});
if(has("ie")){
setTimeout(tmpFunc, 10);
}else{
tmpFunc();
}
if(ta.form){
var resetValue = ta.value;
this.reset = function(){
var current = this.getValue();
if(current !== resetValue){
this.replaceValue(resetValue);
}
};
on(ta.form, "submit", lang.hitch(this, function(){
// Copy value to the <textarea> so it gets submitted along with form.
// FIXME: should we be calling close() here instead?
domAttr.set(ta, 'disabled', this.disabled); // don't submit the value if disabled
ta.value = this.getValue();
}));
}
}else{
html = htmlapi.getChildrenHtml(dn);
dn.innerHTML = "";
}
this.value = html;
// If we're a list item we have to put in a blank line to force the
// bullet to nicely align at the top of text
if(dn.nodeName && dn.nodeName === "LI"){
dn.innerHTML = " <br>";
}
// Construct the editor div structure.
this.header = dn.ownerDocument.createElement("div");
dn.appendChild(this.header);
this.editingArea = dn.ownerDocument.createElement("div");
dn.appendChild(this.editingArea);
this.footer = dn.ownerDocument.createElement("div");
dn.appendChild(this.footer);
if(!this.name){
this.name = this.id + "_AUTOGEN";
}
// User has pressed back/forward button so we lost the text in the editor, but it's saved
// in a hidden <textarea> (which contains the data for all the editors on this page),
// so get editor value from there
if(this.name !== "" && (!config["useXDomain"] || config["allowXdRichTextSave"])){
var saveTextarea = dom.byId(dijit._scopeName + "._editor.RichText.value");
if(saveTextarea && saveTextarea.value !== ""){
var datas = saveTextarea.value.split(this._SEPARATOR), i=0, dat;
while((dat=datas[i++])){
var data = dat.split(this._NAME_CONTENT_SEP);
if(data[0] === this.name){
html = data[1];
datas = datas.splice(i, 1);
saveTextarea.value = datas.join(this._SEPARATOR);
break;
}
}
}
if(!RichText._globalSaveHandler){
RichText._globalSaveHandler = {};
unload.addOnUnload(function(){
var id;
for(id in RichText._globalSaveHandler){
var f = RichText._globalSaveHandler[id];
if(lang.isFunction(f)){
f();
}
}
});
}
RichText._globalSaveHandler[this.id] = lang.hitch(this, "_saveContent");
}
this.isClosed = false;
var ifr = (this.editorObject = this.iframe = win.doc.createElement('iframe'));
ifr.id = this.id+"_iframe";
this._iframeSrc = this._getIframeDocTxt();
ifr.style.border = "none";
ifr.style.width = "100%";
if(this._layoutMode){
// iframe should be 100% height, thus getting it's height from surrounding
// <div> (which has the correct height set by Editor)
ifr.style.height = "100%";
}else{
if(has("ie") >= 7){
if(this.height){
ifr.style.height = this.height;
}
if(this.minHeight){
ifr.style.minHeight = this.minHeight;
}
}else{
ifr.style.height = this.height ? this.height : this.minHeight;
}
}
ifr.frameBorder = 0;
ifr._loadFunc = lang.hitch( this, function(w){
this.window = w;
this.document = this.window.document;
if(has("ie")){
this._localizeEditorCommands();
}
// Do final setup and set initial contents of editor
this.onLoad(html);
});
// Set the iframe's initial (blank) content.
var iframeSrcRef = 'parent.' + dijit._scopeName + '.byId("'+this.id+'")._iframeSrc';
var s = 'javascript:(function(){try{return ' + iframeSrcRef + '}catch(e){document.open();document.domain="' +
document.domain + '";document.write(' + iframeSrcRef + ');document.close();}})()';
ifr.setAttribute('src', s);
this.editingArea.appendChild(ifr);
if(has("safari") <= 4){
var src = ifr.getAttribute("src");
if(!src || src.indexOf("javascript") === -1){
// Safari 4 and earlier sometimes act oddly
// So we have to set it again.
setTimeout(function(){ifr.setAttribute('src', s);},0);
}
}
// TODO: this is a guess at the default line-height, kinda works
if(dn.nodeName === "LI"){
dn.lastChild.style.marginTop = "-1.2em";
}
domClass.add(this.domNode, this.baseClass);
},
//static cache variables shared among all instance of this class
_local2NativeFormatNames: {},
_native2LocalFormatNames: {},
_getIframeDocTxt: function(){
// summary:
// Generates the boilerplate text of the document inside the iframe (ie, <html><head>...</head><body/></html>).
// Editor content (if not blank) should be added afterwards.
// tags:
// private
var _cs = domStyle.getComputedStyle(this.domNode);
// The contents inside of <body>. The real contents are set later via a call to setValue().
var html = "";
var setBodyId = true;
if(has("ie") || has("webkit") || (!this.height && !has("mozilla"))){
// In auto-expand mode, need a wrapper div for AlwaysShowToolbar plugin to correctly
// expand/contract the editor as the content changes.
html = "<div id='dijitEditorBody'></div>";
setBodyId = false;
}else if(has("mozilla")){
// workaround bug where can't select then delete text (until user types something
// into the editor)... and/or issue where typing doesn't erase selected text
this._cursorToStart = true;
html = "&#160;"; // &nbsp;
}
var font = [ _cs.fontWeight, _cs.fontSize, _cs.fontFamily ].join(" ");
// line height is tricky - applying a units value will mess things up.
// if we can't get a non-units value, bail out.
var lineHeight = _cs.lineHeight;
if(lineHeight.indexOf("px") >= 0){
lineHeight = parseFloat(lineHeight)/parseFloat(_cs.fontSize);
// console.debug(lineHeight);
}else if(lineHeight.indexOf("em")>=0){
lineHeight = parseFloat(lineHeight);
}else{
// If we can't get a non-units value, just default
// it to the CSS spec default of 'normal'. Seems to
// work better, esp on IE, than '1.0'
lineHeight = "normal";
}
var userStyle = "";
var self = this;
this.style.replace(/(^|;)\s*(line-|font-?)[^;]+/ig, function(match){
match = match.replace(/^;/ig,"") + ';';
var s = match.split(":")[0];
if(s){
s = lang.trim(s);
s = s.toLowerCase();
var i;
var sC = "";
for(i = 0; i < s.length; i++){
var c = s.charAt(i);
switch(c){
case "-":
i++;
c = s.charAt(i).toUpperCase();
default:
sC += c;
}
}
domStyle.set(self.domNode, sC, "");
}
userStyle += match + ';';
});
// need to find any associated label element and update iframe document title
var label=query('label[for="'+this.id+'"]');
return [
this.isLeftToRight() ? "<html>\n<head>\n" : "<html dir='rtl'>\n<head>\n",
(has("mozilla") && label.length ? "<title>" + label[0].innerHTML + "</title>\n" : ""),
"<meta http-equiv='Content-Type' content='text/html'>\n",
"<style>\n",
"\tbody,html {\n",
"\t\tbackground:transparent;\n",
"\t\tpadding: 1px 0 0 0;\n",
"\t\tmargin: -1px 0 0 0;\n", // remove extraneous vertical scrollbar on safari and firefox
// Set the html/body sizing. Webkit always needs this, other browsers
// only set it when height is defined (not auto-expanding), otherwise
// scrollers do not appear.
((has("webkit"))?"\t\twidth: 100%;\n":""),
((has("webkit"))?"\t\theight: 100%;\n":""),
"\t}\n",
// TODO: left positioning will cause contents to disappear out of view
// if it gets too wide for the visible area
"\tbody{\n",
"\t\ttop:0px;\n",
"\t\tleft:0px;\n",
"\t\tright:0px;\n",
"\t\tfont:", font, ";\n",
((this.height||has("opera")) ? "" : "\t\tposition: fixed;\n"),
// FIXME: IE 6 won't understand min-height?
"\t\tmin-height:", this.minHeight, ";\n",
"\t\tline-height:", lineHeight,";\n",
"\t}\n",
"\tp{ margin: 1em 0; }\n",
// Determine how scrollers should be applied. In autoexpand mode (height = "") no scrollers on y at all.
// But in fixed height mode we want both x/y scrollers. Also, if it's using wrapping div and in auto-expand
// (Mainly IE) we need to kill the y scroller on body and html.
(!setBodyId && !this.height ? "\tbody,html {overflow-y: hidden;}\n" : ""),
"\t#dijitEditorBody{overflow-x: auto; overflow-y:" + (this.height ? "auto;" : "hidden;") + " outline: 0px;}\n",
"\tli > ul:-moz-first-node, li > ol:-moz-first-node{ padding-top: 1.2em; }\n",
// Can't set min-height in IE9, it puts layout on li, which puts move/resize handles.
(!has("ie") ? "\tli{ min-height:1.2em; }\n" : ""),
"</style>\n",
this._applyEditingAreaStyleSheets(),"\n",
"</head>\n<body ",
(setBodyId?"id='dijitEditorBody' ":""),
"onload='frameElement._loadFunc(window,document)' style='"+userStyle+"'>", html, "</body>\n</html>"
].join(""); // String
},
_applyEditingAreaStyleSheets: function(){
// summary:
// apply the specified css files in styleSheets
// tags:
// private
var files = [];
if(this.styleSheets){
files = this.styleSheets.split(';');
this.styleSheets = '';
}
//empty this.editingAreaStyleSheets here, as it will be filled in addStyleSheet
files = files.concat(this.editingAreaStyleSheets);
this.editingAreaStyleSheets = [];
var text='', i=0, url;
while((url=files[i++])){
var abstring = (new _Url(win.global.location, url)).toString();
this.editingAreaStyleSheets.push(abstring);
text += '<link rel="stylesheet" type="text/css" href="'+abstring+'"/>';
}
return text;
},
addStyleSheet: function(/*dojo._Url*/ uri){
// summary:
// add an external stylesheet for the editing area
// uri:
// A dojo.uri.Uri pointing to the url of the external css file
var url=uri.toString();
//if uri is relative, then convert it to absolute so that it can be resolved correctly in iframe
if(url.charAt(0) === '.' || (url.charAt(0) !== '/' && !uri.host)){
url = (new _Url(win.global.location, url)).toString();
}
if(array.indexOf(this.editingAreaStyleSheets, url) > -1){
// console.debug("dijit._editor.RichText.addStyleSheet: Style sheet "+url+" is already applied");
return;
}
this.editingAreaStyleSheets.push(url);
this.onLoadDeferred.addCallback(lang.hitch(this, function(){
if(this.document.createStyleSheet){ //IE
this.document.createStyleSheet(url);
}else{ //other browser
var head = this.document.getElementsByTagName("head")[0];
var stylesheet = this.document.createElement("link");
stylesheet.rel="stylesheet";
stylesheet.type="text/css";
stylesheet.href=url;
head.appendChild(stylesheet);
}
}));
},
removeStyleSheet: function(/*dojo._Url*/ uri){
// summary:
// remove an external stylesheet for the editing area
var url=uri.toString();
//if uri is relative, then convert it to absolute so that it can be resolved correctly in iframe
if(url.charAt(0) === '.' || (url.charAt(0) !== '/' && !uri.host)){
url = (new _Url(win.global.location, url)).toString();
}
var index = array.indexOf(this.editingAreaStyleSheets, url);
if(index === -1){
// console.debug("dijit._editor.RichText.removeStyleSheet: Style sheet "+url+" has not been applied");
return;
}
delete this.editingAreaStyleSheets[index];
win.withGlobal(this.window,'query', dojo, ['link:[href="'+url+'"]']).orphan();
},
// disabled: Boolean
// The editor is disabled; the text cannot be changed.
disabled: false,
_mozSettingProps: {'styleWithCSS':false},
_setDisabledAttr: function(/*Boolean*/ value){
value = !!value;
this._set("disabled", value);
if(!this.isLoaded){ return; } // this method requires init to be complete
if(has("ie") || has("webkit") || has("opera")){
var preventIEfocus = has("ie") && (this.isLoaded || !this.focusOnLoad);
if(preventIEfocus){ this.editNode.unselectable = "on"; }
this.editNode.contentEditable = !value;
if(preventIEfocus){
var _this = this;
setTimeout(function(){
if(_this.editNode){ // guard in case widget destroyed before timeout
_this.editNode.unselectable = "off";
}
}, 0);
}
}else{ //moz
try{
this.document.designMode=(value?'off':'on');
}catch(e){ return; } // ! _disabledOK
if(!value && this._mozSettingProps){
var ps = this._mozSettingProps;
var n;
for(n in ps){
if(ps.hasOwnProperty(n)){
try{
this.document.execCommand(n,false,ps[n]);
}catch(e2){}
}
}
}
// this.document.execCommand('contentReadOnly', false, value);
// if(value){
// this.blur(); //to remove the blinking caret
// }
}
this._disabledOK = true;
},
/* Event handlers
*****************/
onLoad: function(/*String*/ html){
// summary:
// Handler after the iframe finishes loading.
// html: String
// Editor contents should be set to this value
// tags:
// protected
// TODO: rename this to _onLoad, make empty public onLoad() method, deprecate/make protected onLoadDeferred handler?
if(!this.window.__registeredWindow){
this.window.__registeredWindow = true;
this._iframeRegHandle = focus.registerIframe(this.iframe);
}
if(!has("ie") && !has("webkit") && (this.height || has("mozilla"))){
this.editNode=this.document.body;
}else{
// there's a wrapper div around the content, see _getIframeDocTxt().
this.editNode=this.document.body.firstChild;
var _this = this;
if(has("ie")){ // #4996 IE wants to focus the BODY tag
this.tabStop = domConstruct.create('div', { tabIndex: -1 }, this.editingArea);
this.iframe.onfocus = function(){ _this.editNode.setActive(); };
}
}
this.focusNode = this.editNode; // for InlineEditBox
var events = this.events.concat(this.captureEvents);
var ap = this.iframe ? this.document : this.editNode;
array.forEach(events, function(item){
this.connect(ap, item.toLowerCase(), item);
}, this);
this.connect(ap, "onmouseup", "onClick"); // mouseup in the margin does not generate an onclick event
if(has("ie")){ // IE contentEditable
this.connect(this.document, "onmousedown", "_onIEMouseDown"); // #4996 fix focus
// give the node Layout on IE
// TODO: this may no longer be needed, since we've reverted IE to using an iframe,
// not contentEditable. Removing it would also probably remove the need for creating
// the extra <div> in _getIframeDocTxt()
this.editNode.style.zoom = 1.0;
}else{
this.connect(this.document, "onmousedown", function(){
// Clear the moveToStart focus, as mouse
// down will set cursor point. Required to properly
// work with selection/position driven plugins and clicks in
// the window. refs: #10678
delete this._cursorToStart;
});
}
if(has("webkit")){
//WebKit sometimes doesn't fire right on selections, so the toolbar
//doesn't update right. Therefore, help it out a bit with an additional
//listener. A mouse up will typically indicate a display change, so fire this
//and get the toolbar to adapt. Reference: #9532
this._webkitListener = this.connect(this.document, "onmouseup", "onDisplayChanged");
this.connect(this.document, "onmousedown", function(e){
var t = e.target;
if(t && (t === this.document.body || t === this.document)){
// Since WebKit uses the inner DIV, we need to check and set position.
// See: #12024 as to why the change was made.
setTimeout(lang.hitch(this, "placeCursorAtEnd"), 0);
}
});
}
if(has("ie")){
// Try to make sure 'hidden' elements aren't visible in edit mode (like browsers other than IE
// do). See #9103
try{
this.document.execCommand('RespectVisibilityInDesign', true, null);
}catch(e){/* squelch */}
}
this.isLoaded = true;
this.set('disabled', this.disabled); // initialize content to editable (or not)
// Note that setValue() call will only work after isLoaded is set to true (above)
// Set up a function to allow delaying the setValue until a callback is fired
// This ensures extensions like dijit.Editor have a way to hold the value set
// until plugins load (and do things like register filters).
var setContent = lang.hitch(this, function(){
this.setValue(html);
if(this.onLoadDeferred){
this.onLoadDeferred.callback(true);
}
this.onDisplayChanged();
if(this.focusOnLoad){
// after the document loads, then set focus after updateInterval expires so that
// onNormalizedDisplayChanged has run to avoid input caret issues
ready(lang.hitch(this, function(){ setTimeout(lang.hitch(this, "focus"), this.updateInterval); }));
}
// Save off the initial content now
this.value = this.getValue(true);
});
if(this.setValueDeferred){
this.setValueDeferred.addCallback(setContent);
}else{
setContent();
}
},
onKeyDown: function(/* Event */ e){
// summary:
// Handler for onkeydown event
// tags:
// protected
// we need this event at the moment to get the events from control keys
// such as the backspace. It might be possible to add this to Dojo, so that
// keyPress events can be emulated by the keyDown and keyUp detection.
if(e.keyCode === keys.TAB && this.isTabIndent ){
event.stop(e); //prevent tab from moving focus out of editor
// FIXME: this is a poor-man's indent/outdent. It would be
// better if it added 4 "&nbsp;" chars in an undoable way.
// Unfortunately pasteHTML does not prove to be undoable
if(this.queryCommandEnabled((e.shiftKey ? "outdent" : "indent"))){
this.execCommand((e.shiftKey ? "outdent" : "indent"));
}
}
if(has("ie")){
if(e.keyCode == keys.TAB && !this.isTabIndent){
if(e.shiftKey && !e.ctrlKey && !e.altKey){
// focus the BODY so the browser will tab away from it instead
this.iframe.focus();
}else if(!e.shiftKey && !e.ctrlKey && !e.altKey){
// focus the BODY so the browser will tab away from it instead
this.tabStop.focus();
}
}else if(e.keyCode === keys.BACKSPACE && this.document.selection.type === "Control"){
// IE has a bug where if a non-text object is selected in the editor,
// hitting backspace would act as if the browser's back button was
// clicked instead of deleting the object. see #1069
event.stop(e);
this.execCommand("delete");
}else if((65 <= e.keyCode && e.keyCode <= 90) ||
(e.keyCode>=37 && e.keyCode<=40) // FIXME: get this from connect() instead!
){ //arrow keys
e.charCode = e.keyCode;
this.onKeyPress(e);
}
}
if(has("ff")){
if(e.keyCode === keys.PAGE_UP || e.keyCode === keys.PAGE_DOWN ){
if(this.editNode.clientHeight >= this.editNode.scrollHeight){
// Stop the event to prevent firefox from trapping the cursor when there is no scroll bar.
e.preventDefault();
}
}
}
return true;
},
onKeyUp: function(/*===== e =====*/){
// summary:
// Handler for onkeyup event
// tags:
// callback
},
setDisabled: function(/*Boolean*/ disabled){
// summary:
// Deprecated, use set('disabled', ...) instead.
// tags:
// deprecated
kernel.deprecated('dijit.Editor::setDisabled is deprecated','use dijit.Editor::attr("disabled",boolean) instead', 2.0);
this.set('disabled',disabled);
},
_setValueAttr: function(/*String*/ value){
// summary:
// Registers that attr("value", foo) should call setValue(foo)
this.setValue(value);
},
_setDisableSpellCheckAttr: function(/*Boolean*/ disabled){
if(this.document){
domAttr.set(this.document.body, "spellcheck", !disabled);
}else{
// try again after the editor is finished loading
this.onLoadDeferred.addCallback(lang.hitch(this, function(){
domAttr.set(this.document.body, "spellcheck", !disabled);
}));
}
this._set("disableSpellCheck", disabled);
},
onKeyPress: function(e){
// summary:
// Handle the various key events
// tags:
// protected
var c = (e.keyChar && e.keyChar.toLowerCase()) || e.keyCode,
handlers = this._keyHandlers[c],
args = arguments;
if(handlers && !e.altKey){
array.some(handlers, function(h){
// treat meta- same as ctrl-, for benefit of mac users
if(!(h.shift ^ e.shiftKey) && !(h.ctrl ^ (e.ctrlKey||e.metaKey))){
if(!h.handler.apply(this, args)){
e.preventDefault();
}
return true;
}
}, this);
}
// function call after the character has been inserted
if(!this._onKeyHitch){
this._onKeyHitch = lang.hitch(this, "onKeyPressed");
}
setTimeout(this._onKeyHitch, 1);
return true;
},
addKeyHandler: function(/*String*/ key, /*Boolean*/ ctrl, /*Boolean*/ shift, /*Function*/ handler){
// summary:
// Add a handler for a keyboard shortcut
// description:
// The key argument should be in lowercase if it is a letter character
// tags:
// protected
if(!lang.isArray(this._keyHandlers[key])){
this._keyHandlers[key] = [];
}
//TODO: would be nice to make this a hash instead of an array for quick lookups
this._keyHandlers[key].push({
shift: shift || false,
ctrl: ctrl || false,
handler: handler
});
},
onKeyPressed: function(){
// summary:
// Handler for after the user has pressed a key, and the display has been updated.
// (Runs on a timer so that it runs after the display is updated)
// tags:
// private
this.onDisplayChanged(/*e*/); // can't pass in e
},
onClick: function(/*Event*/ e){
// summary:
// Handler for when the user clicks.
// tags:
// private
// console.info('onClick',this._tryDesignModeOn);
this.onDisplayChanged(e);
},
_onIEMouseDown: function(){
// summary:
// IE only to prevent 2 clicks to focus
// tags:
// protected
if(!this.focused && !this.disabled){
this.focus();
}
},
_onBlur: function(e){
// summary:
// Called from focus manager when focus has moved away from this editor
// tags:
// protected
// console.info('_onBlur')
this.inherited(arguments);
var newValue = this.getValue(true);
if(newValue !== this.value){
this.onChange(newValue);
}
this._set("value", newValue);
},
_onFocus: function(/*Event*/ e){
// summary:
// Called from focus manager when focus has moved into this editor
// tags:
// protected
// console.info('_onFocus')
if(!this.disabled){
if(!this._disabledOK){
this.set('disabled', false);
}
this.inherited(arguments);
}
},
// TODO: remove in 2.0
blur: function(){
// summary:
// Remove focus from this instance.
// tags:
// deprecated
if(!has("ie") && this.window.document.documentElement && this.window.document.documentElement.focus){
this.window.document.documentElement.focus();
}else if(win.doc.body.focus){
win.doc.body.focus();
}
},
focus: function(){
// summary:
// Move focus to this editor
if(!this.isLoaded){
this.focusOnLoad = true;
return;
}
if(this._cursorToStart){
delete this._cursorToStart;
if(this.editNode.childNodes){
this.placeCursorAtStart(); // this calls focus() so return
return;
}
}
if(!has("ie")){
focus.focus(this.iframe);
}else if(this.editNode && this.editNode.focus){
// editNode may be hidden in display:none div, lets just punt in this case
//this.editNode.focus(); -> causes IE to scroll always (strict and quirks mode) to the top the Iframe
// if we fire the event manually and let the browser handle the focusing, the latest
// cursor position is focused like in FF
this.iframe.fireEvent('onfocus', document.createEventObject()); // createEventObject only in IE
// }else{
// TODO: should we throw here?
// console.debug("Have no idea how to focus into the editor!");
}
},
// _lastUpdate: 0,
updateInterval: 200,
_updateTimer: null,
onDisplayChanged: function(/*Event*/ /*===== e =====*/){
// summary:
// This event will be fired every time the display context
// changes and the result needs to be reflected in the UI.
// description:
// If you don't want to have update too often,
// onNormalizedDisplayChanged should be used instead
// tags:
// private
// var _t=new Date();
if(this._updateTimer){
clearTimeout(this._updateTimer);
}
if(!this._updateHandler){
this._updateHandler = lang.hitch(this,"onNormalizedDisplayChanged");
}
this._updateTimer = setTimeout(this._updateHandler, this.updateInterval);
// Technically this should trigger a call to watch("value", ...) registered handlers,
// but getValue() is too slow to call on every keystroke so we don't.
},
onNormalizedDisplayChanged: function(){
// summary:
// This event is fired every updateInterval ms or more
// description:
// If something needs to happen immediately after a
// user change, please use onDisplayChanged instead.
// tags:
// private
delete this._updateTimer;
},
onChange: function(/*===== newContent =====*/){
// summary:
// This is fired if and only if the editor loses focus and
// the content is changed.
},
_normalizeCommand: function(/*String*/ cmd, /*Anything?*/argument){
// summary:
// Used as the advice function to map our
// normalized set of commands to those supported by the target
// browser.
// tags:
// private
var command = cmd.toLowerCase();
if(command === "formatblock"){
if(has("safari") && argument === undefined){ command = "heading"; }
}else if(command === "hilitecolor" && !has("mozilla")){
command = "backcolor";
}
return command;
},
_qcaCache: {},
queryCommandAvailable: function(/*String*/ command){
// summary:
// Tests whether a command is supported by the host. Clients
// SHOULD check whether a command is supported before attempting
// to use it, behaviour for unsupported commands is undefined.
// command:
// The command to test for
// tags:
// private
// memoizing version. See _queryCommandAvailable for computing version
var ca = this._qcaCache[command];
if(ca !== undefined){ return ca; }
return (this._qcaCache[command] = this._queryCommandAvailable(command));
},
_queryCommandAvailable: function(/*String*/ command){
// summary:
// See queryCommandAvailable().
// tags:
// private
var ie = 1;
var mozilla = 1 << 1;
var webkit = 1 << 2;
var opera = 1 << 3;
function isSupportedBy(browsers){
return {
ie: Boolean(browsers & ie),
mozilla: Boolean(browsers & mozilla),
webkit: Boolean(browsers & webkit),
opera: Boolean(browsers & opera)
};
}
var supportedBy = null;
switch(command.toLowerCase()){
case "bold": case "italic": case "underline":
case "subscript": case "superscript":
case "fontname": case "fontsize":
case "forecolor": case "hilitecolor":
case "justifycenter": case "justifyfull": case "justifyleft":
case "justifyright": case "delete": case "selectall": case "toggledir":
supportedBy = isSupportedBy(mozilla | ie | webkit | opera);
break;
case "createlink": case "unlink": case "removeformat":
case "inserthorizontalrule": case "insertimage":
case "insertorderedlist": case "insertunorderedlist":
case "indent": case "outdent": case "formatblock":
case "inserthtml": case "undo": case "redo": case "strikethrough": case "tabindent":
supportedBy = isSupportedBy(mozilla | ie | opera | webkit);
break;
case "blockdirltr": case "blockdirrtl":
case "dirltr": case "dirrtl":
case "inlinedirltr": case "inlinedirrtl":
supportedBy = isSupportedBy(ie);
break;
case "cut": case "copy": case "paste":
supportedBy = isSupportedBy( ie | mozilla | webkit);
break;
case "inserttable":
supportedBy = isSupportedBy(mozilla | ie);
break;
case "insertcell": case "insertcol": case "insertrow":
case "deletecells": case "deletecols": case "deleterows":
case "mergecells": case "splitcell":
supportedBy = isSupportedBy(ie | mozilla);
break;
default: return false;
}
return (has("ie") && supportedBy.ie) ||
(has("mozilla") && supportedBy.mozilla) ||
(has("webkit") && supportedBy.webkit) ||
(has("opera") && supportedBy.opera); // Boolean return true if the command is supported, false otherwise
},
execCommand: function(/*String*/ command, argument){
// summary:
// Executes a command in the Rich Text area
// command:
// The command to execute
// argument:
// An optional argument to the command
// tags:
// protected
var returnValue;
//focus() is required for IE to work
//In addition, focus() makes sure after the execution of
//the command, the editor receives the focus as expected
this.focus();
command = this._normalizeCommand(command, argument);
if(argument !== undefined){
if(command === "heading"){
throw new Error("unimplemented");
}else if((command === "formatblock") && has("ie")){
argument = '<'+argument+'>';
}
}
//Check to see if we have any over-rides for commands, they will be functions on this
//widget of the form _commandImpl. If we don't, fall through to the basic native
//exec command of the browser.
var implFunc = "_" + command + "Impl";
if(this[implFunc]){
returnValue = this[implFunc](argument);
}else{
argument = arguments.length > 1 ? argument : null;
if(argument || command !== "createlink"){
returnValue = this.document.execCommand(command, false, argument);
}
}
this.onDisplayChanged();
return returnValue;
},
queryCommandEnabled: function(/*String*/ command){
// summary:
// Check whether a command is enabled or not.
// command:
// The command to execute
// tags:
// protected
if(this.disabled || !this._disabledOK){ return false; }
command = this._normalizeCommand(command);
//Check to see if we have any over-rides for commands, they will be functions on this
//widget of the form _commandEnabledImpl. If we don't, fall through to the basic native
//command of the browser.
var implFunc = "_" + command + "EnabledImpl";
if(this[implFunc]){
return this[implFunc](command);
}else{
return this._browserQueryCommandEnabled(command);
}
},
queryCommandState: function(command){
// summary:
// Check the state of a given command and returns true or false.
// tags:
// protected
if(this.disabled || !this._disabledOK){ return false; }
command = this._normalizeCommand(command);
try{
return this.document.queryCommandState(command);
}catch(e){
//Squelch, occurs if editor is hidden on FF 3 (and maybe others.)
return false;
}
},
queryCommandValue: function(command){
// summary:
// Check the value of a given command. This matters most for
// custom selections and complex values like font value setting.
// tags:
// protected
if(this.disabled || !this._disabledOK){ return false; }
var r;
command = this._normalizeCommand(command);
if(has("ie") && command === "formatblock"){
r = this._native2LocalFormatNames[this.document.queryCommandValue(command)];
}else if(has("mozilla") && command === "hilitecolor"){
var oldValue;
try{
oldValue = this.document.queryCommandValue("styleWithCSS");
}catch(e){
oldValue = false;
}
this.document.execCommand("styleWithCSS", false, true);
r = this.document.queryCommandValue(command);
this.document.execCommand("styleWithCSS", false, oldValue);
}else{
r = this.document.queryCommandValue(command);
}
return r;
},
// Misc.
_sCall: function(name, args){
// summary:
// Run the named method of dijit._editor.selection over the
// current editor instance's window, with the passed args.
// tags:
// private
return win.withGlobal(this.window, name, selectionapi, args);
},
// FIXME: this is a TON of code duplication. Why?
placeCursorAtStart: function(){
// summary:
// Place the cursor at the start of the editing area.
// tags:
// private
this.focus();
//see comments in placeCursorAtEnd
var isvalid=false;
if(has("mozilla")){
// TODO: Is this branch even necessary?
var first=this.editNode.firstChild;
while(first){
if(first.nodeType === 3){
if(first.nodeValue.replace(/^\s+|\s+$/g, "").length>0){
isvalid=true;
this._sCall("selectElement", [ first ]);
break;
}
}else if(first.nodeType === 1){
isvalid=true;
var tg = first.tagName ? first.tagName.toLowerCase() : "";
// Collapse before childless tags.
if(/br|input|img|base|meta|area|basefont|hr|link/.test(tg)){
this._sCall("selectElement", [ first ]);
}else{
// Collapse inside tags with children.
this._sCall("selectElementChildren", [ first ]);
}
break;
}
first = first.nextSibling;
}
}else{
isvalid=true;
this._sCall("selectElementChildren", [ this.editNode ]);
}
if(isvalid){
this._sCall("collapse", [ true ]);
}
},
placeCursorAtEnd: function(){
// summary:
// Place the cursor at the end of the editing area.
// tags:
// private
this.focus();
//In mozilla, if last child is not a text node, we have to use
// selectElementChildren on this.editNode.lastChild otherwise the
// cursor would be placed at the end of the closing tag of
//this.editNode.lastChild
var isvalid=false;
if(has("mozilla")){
var last=this.editNode.lastChild;
while(last){
if(last.nodeType === 3){
if(last.nodeValue.replace(/^\s+|\s+$/g, "").length>0){
isvalid=true;
this._sCall("selectElement", [ last ]);
break;
}
}else if(last.nodeType === 1){
isvalid=true;
if(last.lastChild){
this._sCall("selectElement", [ last.lastChild ]);
}else{
this._sCall("selectElement", [ last ]);
}
break;
}
last = last.previousSibling;
}
}else{
isvalid=true;
this._sCall("selectElementChildren", [ this.editNode ]);
}
if(isvalid){
this._sCall("collapse", [ false ]);
}
},
getValue: function(/*Boolean?*/ nonDestructive){
// summary:
// Return the current content of the editing area (post filters
// are applied). Users should call get('value') instead.
// nonDestructive:
// defaults to false. Should the post-filtering be run over a copy
// of the live DOM? Most users should pass "true" here unless they
// *really* know that none of the installed filters are going to
// mess up the editing session.
// tags:
// private
if(this.textarea){
if(this.isClosed || !this.isLoaded){
return this.textarea.value;
}
}
return this._postFilterContent(null, nonDestructive);
},
_getValueAttr: function(){
// summary:
// Hook to make attr("value") work
return this.getValue(true);
},
setValue: function(/*String*/ html){
// summary:
// This function sets the content. No undo history is preserved.
// Users should use set('value', ...) instead.
// tags:
// deprecated
// TODO: remove this and getValue() for 2.0, and move code to _setValueAttr()
if(!this.isLoaded){
// try again after the editor is finished loading
this.onLoadDeferred.addCallback(lang.hitch(this, function(){
this.setValue(html);
}));
return;
}
this._cursorToStart = true;
if(this.textarea && (this.isClosed || !this.isLoaded)){
this.textarea.value=html;
}else{
html = this._preFilterContent(html);
var node = this.isClosed ? this.domNode : this.editNode;
if(html && has("mozilla") && html.toLowerCase() === "<p></p>"){
html = "<p>&#160;</p>"; // &nbsp;
}
// Use &nbsp; to avoid webkit problems where editor is disabled until the user clicks it
if(!html && has("webkit")){
html = "&#160;"; // &nbsp;
}
node.innerHTML = html;
this._preDomFilterContent(node);
}
this.onDisplayChanged();
this._set("value", this.getValue(true));
},
replaceValue: function(/*String*/ html){
// summary:
// This function set the content while trying to maintain the undo stack
// (now only works fine with Moz, this is identical to setValue in all
// other browsers)
// tags:
// protected
if(this.isClosed){
this.setValue(html);
}else if(this.window && this.window.getSelection && !has("mozilla")){ // Safari
// look ma! it's a totally f'd browser!
this.setValue(html);
}else if(this.window && this.window.getSelection){ // Moz
html = this._preFilterContent(html);
this.execCommand("selectall");
if(!html){
this._cursorToStart = true;
html = "&#160;"; // &nbsp;
}
this.execCommand("inserthtml", html);
this._preDomFilterContent(this.editNode);
}else if(this.document && this.document.selection){//IE
//In IE, when the first element is not a text node, say
//an <a> tag, when replacing the content of the editing
//area, the <a> tag will be around all the content
//so for now, use setValue for IE too
this.setValue(html);
}
this._set("value", this.getValue(true));
},
_preFilterContent: function(/*String*/ html){
// summary:
// Filter the input before setting the content of the editing
// area. DOM pre-filtering may happen after this
// string-based filtering takes place but as of 1.2, this is not
// guaranteed for operations such as the inserthtml command.
// tags:
// private
var ec = html;
array.forEach(this.contentPreFilters, function(ef){ if(ef){ ec = ef(ec); } });
return ec;
},
_preDomFilterContent: function(/*DomNode*/ dom){
// summary:
// filter the input's live DOM. All filter operations should be
// considered to be "live" and operating on the DOM that the user
// will be interacting with in their editing session.
// tags:
// private
dom = dom || this.editNode;
array.forEach(this.contentDomPreFilters, function(ef){
if(ef && lang.isFunction(ef)){
ef(dom);
}
}, this);
},
_postFilterContent: function(
/*DomNode|DomNode[]|String?*/ dom,
/*Boolean?*/ nonDestructive){
// summary:
// filter the output after getting the content of the editing area
//
// description:
// post-filtering allows plug-ins and users to specify any number
// of transforms over the editor's content, enabling many common
// use-cases such as transforming absolute to relative URLs (and
// vice-versa), ensuring conformance with a particular DTD, etc.
// The filters are registered in the contentDomPostFilters and
// contentPostFilters arrays. Each item in the
// contentDomPostFilters array is a function which takes a DOM
// Node or array of nodes as its only argument and returns the
// same. It is then passed down the chain for further filtering.
// The contentPostFilters array behaves the same way, except each
// member operates on strings. Together, the DOM and string-based
// filtering allow the full range of post-processing that should
// be necessaray to enable even the most agressive of post-editing
// conversions to take place.
//
// If nonDestructive is set to "true", the nodes are cloned before
// filtering proceeds to avoid potentially destructive transforms
// to the content which may still needed to be edited further.
// Once DOM filtering has taken place, the serialized version of
// the DOM which is passed is run through each of the
// contentPostFilters functions.
//
// dom:
// a node, set of nodes, which to filter using each of the current
// members of the contentDomPostFilters and contentPostFilters arrays.
//
// nonDestructive:
// defaults to "false". If true, ensures that filtering happens on
// a clone of the passed-in content and not the actual node
// itself.
//
// tags:
// private
var ec;
if(!lang.isString(dom)){
dom = dom || this.editNode;
if(this.contentDomPostFilters.length){
if(nonDestructive){
dom = lang.clone(dom);
}
array.forEach(this.contentDomPostFilters, function(ef){
dom = ef(dom);
});
}
ec = htmlapi.getChildrenHtml(dom);
}else{
ec = dom;
}
if(!lang.trim(ec.replace(/^\xA0\xA0*/, '').replace(/\xA0\xA0*$/, '')).length){
ec = "";
}
// if(has("ie")){
// //removing appended <P>&nbsp;</P> for IE
// ec = ec.replace(/(?:<p>&nbsp;</p>[\n\r]*)+$/i,"");
// }
array.forEach(this.contentPostFilters, function(ef){
ec = ef(ec);
});
return ec;
},
_saveContent: function(){
// summary:
// Saves the content in an onunload event if the editor has not been closed
// tags:
// private
var saveTextarea = dom.byId(dijit._scopeName + "._editor.RichText.value");
if(saveTextarea){
if(saveTextarea.value){
saveTextarea.value += this._SEPARATOR;
}
saveTextarea.value += this.name + this._NAME_CONTENT_SEP + this.getValue(true);
}
},
escapeXml: function(/*String*/ str, /*Boolean*/ noSingleQuotes){
// summary:
// Adds escape sequences for special characters in XML.
// Optionally skips escapes for single quotes
// tags:
// private
str = str.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
if(!noSingleQuotes){
str = str.replace(/'/gm, "&#39;");
}
return str; // string
},
getNodeHtml: function(/* DomNode */ node){
// summary:
// Deprecated. Use dijit/_editor/html::_getNodeHtml() instead.
// tags:
// deprecated
kernel.deprecated('dijit.Editor::getNodeHtml is deprecated','use dijit/_editor/html::getNodeHtml instead', 2);
return htmlapi.getNodeHtml(node); // String
},
getNodeChildrenHtml: function(/* DomNode */ dom){
// summary:
// Deprecated. Use dijit/_editor/html::getChildrenHtml() instead.
// tags:
// deprecated
kernel.deprecated('dijit.Editor::getNodeChildrenHtml is deprecated','use dijit/_editor/html::getChildrenHtml instead', 2);
return htmlapi.getChildrenHtml(dom);
},
close: function(/*Boolean?*/ save){
// summary:
// Kills the editor and optionally writes back the modified contents to the
// element from which it originated.
// save:
// Whether or not to save the changes. If false, the changes are discarded.
// tags:
// private
if(this.isClosed){ return; }
if(!arguments.length){ save = true; }
if(save){
this._set("value", this.getValue(true));
}
// line height is squashed for iframes
// FIXME: why was this here? if(this.iframe){ this.domNode.style.lineHeight = null; }
if(this.interval){ clearInterval(this.interval); }
if(this._webkitListener){
//Cleaup of WebKit fix: #9532
this.disconnect(this._webkitListener);
delete this._webkitListener;
}
// Guard against memory leaks on IE (see #9268)
if(has("ie")){
this.iframe.onfocus = null;
}
this.iframe._loadFunc = null;
if(this._iframeRegHandle){
this._iframeRegHandle.remove();
delete this._iframeRegHandle;
}
if(this.textarea){
var s = this.textarea.style;
s.position = "";
s.left = s.top = "";
if(has("ie")){
s.overflow = this.__overflow;
this.__overflow = null;
}
this.textarea.value = this.value;
domConstruct.destroy(this.domNode);
this.domNode = this.textarea;
}else{
// Note that this destroys the iframe
this.domNode.innerHTML = this.value;
}
delete this.iframe;
domClass.remove(this.domNode, this.baseClass);
this.isClosed = true;
this.isLoaded = false;
delete this.editNode;
delete this.focusNode;
if(this.window && this.window._frameElement){
this.window._frameElement = null;
}
this.window = null;
this.document = null;
this.editingArea = null;
this.editorObject = null;
},
destroy: function(){
if(!this.isClosed){ this.close(false); }
if(this._updateTimer){
clearTimeout(this._updateTimer);
}
this.inherited(arguments);
if(RichText._globalSaveHandler){
delete RichText._globalSaveHandler[this.id];
}
},
_removeMozBogus: function(/* String */ html){
// summary:
// Post filter to remove unwanted HTML attributes generated by mozilla
// tags:
// private
return html.replace(/\stype="_moz"/gi, '').replace(/\s_moz_dirty=""/gi, '').replace(/_moz_resizing="(true|false)"/gi,''); // String
},
_removeWebkitBogus: function(/* String */ html){
// summary:
// Post filter to remove unwanted HTML attributes generated by webkit
// tags:
// private
html = html.replace(/\sclass="webkit-block-placeholder"/gi, '');
html = html.replace(/\sclass="apple-style-span"/gi, '');
// For some reason copy/paste sometime adds extra meta tags for charset on
// webkit (chrome) on mac.They need to be removed. See: #12007"
html = html.replace(/<meta charset=\"utf-8\" \/>/gi, '');
return html; // String
},
_normalizeFontStyle: function(/* String */ html){
// summary:
// Convert 'strong' and 'em' to 'b' and 'i'.
// description:
// Moz can not handle strong/em tags correctly, so to help
// mozilla and also to normalize output, convert them to 'b' and 'i'.
//
// Note the IE generates 'strong' and 'em' rather than 'b' and 'i'
// tags:
// private
return html.replace(/<(\/)?strong([ \>])/gi, '<$1b$2')
.replace(/<(\/)?em([ \>])/gi, '<$1i$2' ); // String
},
_preFixUrlAttributes: function(/* String */ html){
// summary:
// Pre-filter to do fixing to href attributes on <a> and <img> tags
// tags:
// private
return html.replace(/(?:(<a(?=\s).*?\shref=)("|')(.*?)\2)|(?:(<a\s.*?href=)([^"'][^ >]+))/gi,
'$1$4$2$3$5$2 _djrealurl=$2$3$5$2')
.replace(/(?:(<img(?=\s).*?\ssrc=)("|')(.*?)\2)|(?:(<img\s.*?src=)([^"'][^ >]+))/gi,
'$1$4$2$3$5$2 _djrealurl=$2$3$5$2'); // String
},
/*****************************************************************************
The following functions implement HTML manipulation commands for various
browser/contentEditable implementations. The goal of them is to enforce
standard behaviors of them.
******************************************************************************/
/*** queryCommandEnabled implementations ***/
_browserQueryCommandEnabled: function(command){
// summary:
// Implementation to call to the native queryCommandEnabled of the browser.
// command:
// The command to check.
// tags:
// protected
if(!command) { return false; }
var elem = has("ie") ? this.document.selection.createRange() : this.document;
try{
return elem.queryCommandEnabled(command);
}catch(e){
return false;
}
},
_createlinkEnabledImpl: function(/*===== argument =====*/){
// summary:
// This function implements the test for if the create link
// command should be enabled or not.
// argument:
// arguments to the exec command, if any.
// tags:
// protected
var enabled = true;
if(has("opera")){
var sel = this.window.getSelection();
if(sel.isCollapsed){
enabled = true;
}else{
enabled = this.document.queryCommandEnabled("createlink");
}
}else{
enabled = this._browserQueryCommandEnabled("createlink");
}
return enabled;
},
_unlinkEnabledImpl: function(/*===== argument =====*/){
// summary:
// This function implements the test for if the unlink
// command should be enabled or not.
// argument:
// arguments to the exec command, if any.
// tags:
// protected
var enabled = true;
if(has("mozilla") || has("webkit")){
enabled = this._sCall("hasAncestorElement", ["a"]);
}else{
enabled = this._browserQueryCommandEnabled("unlink");
}
return enabled;
},
_inserttableEnabledImpl: function(/*===== argument =====*/){
// summary:
// This function implements the test for if the inserttable
// command should be enabled or not.
// argument:
// arguments to the exec command, if any.
// tags:
// protected
var enabled = true;
if(has("mozilla") || has("webkit")){
enabled = true;
}else{
enabled = this._browserQueryCommandEnabled("inserttable");
}
return enabled;
},
_cutEnabledImpl: function(/*===== argument =====*/){
// summary:
// This function implements the test for if the cut
// command should be enabled or not.
// argument:
// arguments to the exec command, if any.
// tags:
// protected
var enabled = true;
if(has("webkit")){
// WebKit deems clipboard activity as a security threat and natively would return false
var sel = this.window.getSelection();
if(sel){ sel = sel.toString(); }
enabled = !!sel;
}else{
enabled = this._browserQueryCommandEnabled("cut");
}
return enabled;
},
_copyEnabledImpl: function(/*===== argument =====*/){
// summary:
// This function implements the test for if the copy
// command should be enabled or not.
// argument:
// arguments to the exec command, if any.
// tags:
// protected
var enabled = true;
if(has("webkit")){
// WebKit deems clipboard activity as a security threat and natively would return false
var sel = this.window.getSelection();
if(sel){ sel = sel.toString(); }
enabled = !!sel;
}else{
enabled = this._browserQueryCommandEnabled("copy");
}
return enabled;
},
_pasteEnabledImpl: function(/*===== argument =====*/){
// summary:c
// This function implements the test for if the paste
// command should be enabled or not.
// argument:
// arguments to the exec command, if any.
// tags:
// protected
var enabled = true;
if(has("webkit")){
return true;
}else{
enabled = this._browserQueryCommandEnabled("paste");
}
return enabled;
},
/*** execCommand implementations ***/
_inserthorizontalruleImpl: function(argument){
// summary:
// This function implements the insertion of HTML 'HR' tags.
// into a point on the page. IE doesn't to it right, so
// we have to use an alternate form
// argument:
// arguments to the exec command, if any.
// tags:
// protected
if(has("ie")){
return this._inserthtmlImpl("<hr>");
}
return this.document.execCommand("inserthorizontalrule", false, argument);
},
_unlinkImpl: function(argument){
// summary:
// This function implements the unlink of an 'a' tag.
// argument:
// arguments to the exec command, if any.
// tags:
// protected
if((this.queryCommandEnabled("unlink")) && (has("mozilla") || has("webkit"))){
var a = this._sCall("getAncestorElement", [ "a" ]);
this._sCall("selectElement", [ a ]);
return this.document.execCommand("unlink", false, null);
}
return this.document.execCommand("unlink", false, argument);
},
_hilitecolorImpl: function(argument){
// summary:
// This function implements the hilitecolor command
// argument:
// arguments to the exec command, if any.
// tags:
// protected
var returnValue;
var isApplied = this._handleTextColorOrProperties("hilitecolor", argument);
if(!isApplied){
if(has("mozilla")){
// mozilla doesn't support hilitecolor properly when useCSS is
// set to false (bugzilla #279330)
this.document.execCommand("styleWithCSS", false, true);
console.log("Executing color command.");
returnValue = this.document.execCommand("hilitecolor", false, argument);
this.document.execCommand("styleWithCSS", false, false);
}else{
returnValue = this.document.execCommand("hilitecolor", false, argument);
}
}
return returnValue;
},
_backcolorImpl: function(argument){
// summary:
// This function implements the backcolor command
// argument:
// arguments to the exec command, if any.
// tags:
// protected
if(has("ie")){
// Tested under IE 6 XP2, no problem here, comment out
// IE weirdly collapses ranges when we exec these commands, so prevent it
// var tr = this.document.selection.createRange();
argument = argument ? argument : null;
}
var isApplied = this._handleTextColorOrProperties("backcolor", argument);
if(!isApplied){
isApplied = this.document.execCommand("backcolor", false, argument);
}
return isApplied;
},
_forecolorImpl: function(argument){
// summary:
// This function implements the forecolor command
// argument:
// arguments to the exec command, if any.
// tags:
// protected
if(has("ie")){
// Tested under IE 6 XP2, no problem here, comment out
// IE weirdly collapses ranges when we exec these commands, so prevent it
// var tr = this.document.selection.createRange();
argument = argument? argument : null;
}
var isApplied = false;
isApplied = this._handleTextColorOrProperties("forecolor", argument);
if(!isApplied){
isApplied = this.document.execCommand("forecolor", false, argument);
}
return isApplied;
},
_inserthtmlImpl: function(argument){
// summary:
// This function implements the insertion of HTML content into
// a point on the page.
// argument:
// The content to insert, if any.
// tags:
// protected
argument = this._preFilterContent(argument);
var rv = true;
if(has("ie")){
var insertRange = this.document.selection.createRange();
if(this.document.selection.type.toUpperCase() === 'CONTROL'){
var n=insertRange.item(0);
while(insertRange.length){
insertRange.remove(insertRange.item(0));
}
n.outerHTML=argument;
}else{
insertRange.pasteHTML(argument);
}
insertRange.select();
//insertRange.collapse(true);
}else if(has("mozilla") && !argument.length){
//mozilla can not inserthtml an empty html to delete current selection
//so we delete the selection instead in this case
this._sCall("remove"); // FIXME
}else{
rv = this.document.execCommand("inserthtml", false, argument);
}
return rv;
},
_boldImpl: function(argument){
// summary:
// This function implements an over-ride of the bold command.
// argument:
// Not used, operates by selection.
// tags:
// protected
var applied = false;
if(has("ie")){
this._adaptIESelection();
applied = this._adaptIEFormatAreaAndExec("bold");
}
if(!applied){
applied = this.document.execCommand("bold", false, argument);
}
return applied;
},
_italicImpl: function(argument){
// summary:
// This function implements an over-ride of the italic command.
// argument:
// Not used, operates by selection.
// tags:
// protected
var applied = false;
if(has("ie")){
this._adaptIESelection();
applied = this._adaptIEFormatAreaAndExec("italic");
}
if(!applied){
applied = this.document.execCommand("italic", false, argument);
}
return applied;
},
_underlineImpl: function(argument){
// summary:
// This function implements an over-ride of the underline command.
// argument:
// Not used, operates by selection.
// tags:
// protected
var applied = false;
if(has("ie")){
this._adaptIESelection();
applied = this._adaptIEFormatAreaAndExec("underline");
}
if(!applied){
applied = this.document.execCommand("underline", false, argument);
}
return applied;
},
_strikethroughImpl: function(argument){
// summary:
// This function implements an over-ride of the strikethrough command.
// argument:
// Not used, operates by selection.
// tags:
// protected
var applied = false;
if(has("ie")){
this._adaptIESelection();
applied = this._adaptIEFormatAreaAndExec("strikethrough");
}
if(!applied){
applied = this.document.execCommand("strikethrough", false, argument);
}
return applied;
},
_superscriptImpl: function(argument){
// summary:
// This function implements an over-ride of the superscript command.
// argument:
// Not used, operates by selection.
// tags:
// protected
var applied = false;
if(has("ie")){
this._adaptIESelection();
applied = this._adaptIEFormatAreaAndExec("superscript");
}
if(!applied){
applied = this.document.execCommand("superscript", false, argument);
}
return applied;
},
_subscriptImpl: function(argument){
// summary:
// This function implements an over-ride of the superscript command.
// argument:
// Not used, operates by selection.
// tags:
// protected
var applied = false;
if(has("ie")){
this._adaptIESelection();
applied = this._adaptIEFormatAreaAndExec("subscript");
}
if(!applied){
applied = this.document.execCommand("subscript", false, argument);
}
return applied;
},
_fontnameImpl: function(argument){
// summary:
// This function implements the fontname command
// argument:
// arguments to the exec command, if any.
// tags:
// protected
var isApplied;
if(has("ie")){
isApplied = this._handleTextColorOrProperties("fontname", argument);
}
if(!isApplied){
isApplied = this.document.execCommand("fontname", false, argument);
}
return isApplied;
},
_fontsizeImpl: function(argument){
// summary:
// This function implements the fontsize command
// argument:
// arguments to the exec command, if any.
// tags:
// protected
var isApplied;
if(has("ie")){
isApplied = this._handleTextColorOrProperties("fontsize", argument);
}
if(!isApplied){
isApplied = this.document.execCommand("fontsize", false, argument);
}
return isApplied;
},
_insertorderedlistImpl: function(argument){
// summary:
// This function implements the insertorderedlist command
// argument:
// arguments to the exec command, if any.
// tags:
// protected
var applied = false;
if(has("ie")){
applied = this._adaptIEList("insertorderedlist", argument);
}
if(!applied){
applied = this.document.execCommand("insertorderedlist", false, argument);
}
return applied;
},
_insertunorderedlistImpl: function(argument){
// summary:
// This function implements the insertunorderedlist command
// argument:
// arguments to the exec command, if any.
// tags:
// protected
var applied = false;
if(has("ie")){
applied = this._adaptIEList("insertunorderedlist", argument);
}
if(!applied){
applied = this.document.execCommand("insertunorderedlist", false, argument);
}
return applied;
},
getHeaderHeight: function(){
// summary:
// A function for obtaining the height of the header node
return this._getNodeChildrenHeight(this.header); // Number
},
getFooterHeight: function(){
// summary:
// A function for obtaining the height of the footer node
return this._getNodeChildrenHeight(this.footer); // Number
},
_getNodeChildrenHeight: function(node){
// summary:
// An internal function for computing the cumulative height of all child nodes of 'node'
// node:
// The node to process the children of;
var h = 0;
if(node && node.childNodes){
// IE didn't compute it right when position was obtained on the node directly is some cases,
// so we have to walk over all the children manually.
var i;
for(i = 0; i < node.childNodes.length; i++){
var size = domGeometry.position(node.childNodes[i]);
h += size.h;
}
}
return h; // Number
},
_isNodeEmpty: function(node, startOffset){
// summary:
// Function to test if a node is devoid of real content.
// node:
// The node to check.
// tags:
// private.
if(node.nodeType === 1/*element*/){
if(node.childNodes.length > 0){
return this._isNodeEmpty(node.childNodes[0], startOffset);
}
return true;
}else if(node.nodeType === 3/*text*/){
return (node.nodeValue.substring(startOffset) === "");
}
return false;
},
_removeStartingRangeFromRange: function(node, range){
// summary:
// Function to adjust selection range by removing the current
// start node.
// node:
// The node to remove from the starting range.
// range:
// The range to adapt.
// tags:
// private
if(node.nextSibling){
range.setStart(node.nextSibling,0);
}else{
var parent = node.parentNode;
while(parent && parent.nextSibling == null){
//move up the tree until we find a parent that has another node, that node will be the next node
parent = parent.parentNode;
}
if(parent){
range.setStart(parent.nextSibling,0);
}
}
return range;
},
_adaptIESelection: function(){
// summary:
// Function to adapt the IE range by removing leading 'newlines'
// Needed to fix issue with bold/italics/underline not working if
// range included leading 'newlines'.
// In IE, if a user starts a selection at the very end of a line,
// then the native browser commands will fail to execute correctly.
// To work around the issue, we can remove all empty nodes from
// the start of the range selection.
var selection = rangeapi.getSelection(this.window);
if(selection && selection.rangeCount && !selection.isCollapsed){
var range = selection.getRangeAt(0);
var firstNode = range.startContainer;
var startOffset = range.startOffset;
while(firstNode.nodeType === 3/*text*/ && startOffset >= firstNode.length && firstNode.nextSibling){
//traverse the text nodes until we get to the one that is actually highlighted
startOffset = startOffset - firstNode.length;
firstNode = firstNode.nextSibling;
}
//Remove the starting ranges until the range does not start with an empty node.
var lastNode=null;
while(this._isNodeEmpty(firstNode, startOffset) && firstNode !== lastNode){
lastNode =firstNode; //this will break the loop in case we can't find the next sibling
range = this._removeStartingRangeFromRange(firstNode, range); //move the start container to the next node in the range
firstNode = range.startContainer;
startOffset = 0; //start at the beginning of the new starting range
}
selection.removeAllRanges();// this will work as long as users cannot select multiple ranges. I have not been able to do that in the editor.
selection.addRange(range);
}
},
_adaptIEFormatAreaAndExec: function(command){
// summary:
// Function to handle IE's quirkiness regarding how it handles
// format commands on a word. This involves a lit of node splitting
// and format cloning.
// command:
// The format command, needed to check if the desired
// command is true or not.
var selection = rangeapi.getSelection(this.window);
var doc = this.document;
var rs, ret, range, txt, startNode, endNode, breaker, sNode;
if(command && selection && selection.isCollapsed){
var isApplied = this.queryCommandValue(command);
if(isApplied){
// We have to split backwards until we hit the format
var nNames = this._tagNamesForCommand(command);
range = selection.getRangeAt(0);
var fs = range.startContainer;
if(fs.nodeType === 3){
var offset = range.endOffset;
if(fs.length < offset){
//We are not looking from the right node, try to locate the correct one
ret = this._adjustNodeAndOffset(rs, offset);
fs = ret.node;
offset = ret.offset;
}
}
var topNode;
while(fs && fs !== this.editNode){
// We have to walk back and see if this is still a format or not.
// Hm, how do I do this?
var tName = fs.tagName? fs.tagName.toLowerCase() : "";
if(array.indexOf(nNames, tName) > -1){
topNode = fs;
break;
}
fs = fs.parentNode;
}
// Okay, we have a stopping place, time to split things apart.
if(topNode){
// Okay, we know how far we have to split backwards, so we have to split now.
rs = range.startContainer;
var newblock = doc.createElement(topNode.tagName);
domConstruct.place(newblock, topNode, "after");
if(rs && rs.nodeType === 3){
// Text node, we have to split it.
var nodeToMove, tNode;
var endOffset = range.endOffset;
if(rs.length < endOffset){
//We are not splitting the right node, try to locate the correct one
ret = this._adjustNodeAndOffset(rs, endOffset);
rs = ret.node;
endOffset = ret.offset;
}
txt = rs.nodeValue;
startNode = doc.createTextNode(txt.substring(0, endOffset));
var endText = txt.substring(endOffset, txt.length);
if(endText){
endNode = doc.createTextNode(endText);
}
// Place the split, then remove original nodes.
domConstruct.place(startNode, rs, "before");
if(endNode){
breaker = doc.createElement("span");
breaker.className = "ieFormatBreakerSpan";
domConstruct.place(breaker, rs, "after");
domConstruct.place(endNode, breaker, "after");
endNode = breaker;
}
domConstruct.destroy(rs);
// Okay, we split the text. Now we need to see if we're
// parented to the block element we're splitting and if
// not, we have to split all the way up. Ugh.
var parentC = startNode.parentNode;
var tagList = [];
var tagData;
while(parentC !== topNode){
var tg = parentC.tagName;
tagData = {tagName: tg};
tagList.push(tagData);
var newTg = doc.createElement(tg);
// Clone over any 'style' data.
if(parentC.style){
if(newTg.style){
if(parentC.style.cssText){
newTg.style.cssText = parentC.style.cssText;
tagData.cssText = parentC.style.cssText;
}
}
}
// If font also need to clone over any font data.
if(parentC.tagName === "FONT"){
if(parentC.color){
newTg.color = parentC.color;
tagData.color = parentC.color;
}
if(parentC.face){
newTg.face = parentC.face;
tagData.face = parentC.face;
}
if(parentC.size){ // this check was necessary on IE
newTg.size = parentC.size;
tagData.size = parentC.size;
}
}
if(parentC.className){
newTg.className = parentC.className;
tagData.className = parentC.className;
}
// Now move end node and every sibling
// after it over into the new tag.
if(endNode){
nodeToMove = endNode;
while(nodeToMove){
tNode = nodeToMove.nextSibling;
newTg.appendChild(nodeToMove);
nodeToMove = tNode;
}
}
if(newTg.tagName == parentC.tagName){
breaker = doc.createElement("span");
breaker.className = "ieFormatBreakerSpan";
domConstruct.place(breaker, parentC, "after");
domConstruct.place(newTg, breaker, "after");
}else{
domConstruct.place(newTg, parentC, "after");
}
startNode = parentC;
endNode = newTg;
parentC = parentC.parentNode;
}
// Lastly, move the split out all the split tags
// to the new block as they should now be split properly.
if(endNode){
nodeToMove = endNode;
if(nodeToMove.nodeType === 1 || (nodeToMove.nodeType === 3 && nodeToMove.nodeValue)){
// Non-blank text and non-text nodes need to clear out that blank space
// before moving the contents.
newblock.innerHTML = "";
}
while(nodeToMove){
tNode = nodeToMove.nextSibling;
newblock.appendChild(nodeToMove);
nodeToMove = tNode;
}
}
// We had intermediate tags, we have to now recreate them inbetween the split
// and restore what styles, classnames, etc, we can.
if(tagList.length){
tagData = tagList.pop();
var newContTag = doc.createElement(tagData.tagName);
if(tagData.cssText && newContTag.style){
newContTag.style.cssText = tagData.cssText;
}
if(tagData.className){
newContTag.className = tagData.className;
}
if(tagData.tagName === "FONT"){
if(tagData.color){
newContTag.color = tagData.color;
}
if(tagData.face){
newContTag.face = tagData.face;
}
if(tagData.size){
newContTag.size = tagData.size;
}
}
domConstruct.place(newContTag, newblock, "before");
while(tagList.length){
tagData = tagList.pop();
var newTgNode = doc.createElement(tagData.tagName);
if(tagData.cssText && newTgNode.style){
newTgNode.style.cssText = tagData.cssText;
}
if(tagData.className){
newTgNode.className = tagData.className;
}
if(tagData.tagName === "FONT"){
if(tagData.color){
newTgNode.color = tagData.color;
}
if(tagData.face){
newTgNode.face = tagData.face;
}
if(tagData.size){
newTgNode.size = tagData.size;
}
}
newContTag.appendChild(newTgNode);
newContTag = newTgNode;
}
// Okay, everything is theoretically split apart and removed from the content
// so insert the dummy text to select, select it, then
// clear to position cursor.
sNode = doc.createTextNode(".");
breaker.appendChild(sNode);
newContTag.appendChild(sNode);
win.withGlobal(this.window, lang.hitch(this, function(){
var newrange = rangeapi.create();
newrange.setStart(sNode, 0);
newrange.setEnd(sNode, sNode.length);
selection.removeAllRanges();
selection.addRange(newrange);
selectionapi.collapse(false);
sNode.parentNode.innerHTML = "";
}));
}else{
// No extra tags, so we have to insert a breaker point and rely
// on filters to remove it later.
breaker = doc.createElement("span");
breaker.className="ieFormatBreakerSpan";
sNode = doc.createTextNode(".");
breaker.appendChild(sNode);
domConstruct.place(breaker, newblock, "before");
win.withGlobal(this.window, lang.hitch(this, function(){
var newrange = rangeapi.create();
newrange.setStart(sNode, 0);
newrange.setEnd(sNode, sNode.length);
selection.removeAllRanges();
selection.addRange(newrange);
selectionapi.collapse(false);
sNode.parentNode.innerHTML = "";
}));
}
if(!newblock.firstChild){
// Empty, we don't need it. Split was at end or similar
// So, remove it.
domConstruct.destroy(newblock);
}
return true;
}
}
return false;
}else{
range = selection.getRangeAt(0);
rs = range.startContainer;
if(rs && rs.nodeType === 3){
// Text node, we have to split it.
win.withGlobal(this.window, lang.hitch(this, function(){
var offset = range.startOffset;
if(rs.length < offset){
//We are not splitting the right node, try to locate the correct one
ret = this._adjustNodeAndOffset(rs, offset);
rs = ret.node;
offset = ret.offset;
}
txt = rs.nodeValue;
startNode = doc.createTextNode(txt.substring(0, offset));
var endText = txt.substring(offset);
if(endText !== ""){
endNode = doc.createTextNode(txt.substring(offset));
}
// Create a space, we'll select and bold it, so
// the whole word doesn't get bolded
breaker = doc.createElement("span");
sNode = doc.createTextNode(".");
breaker.appendChild(sNode);
if(startNode.length){
domConstruct.place(startNode, rs, "after");
}else{
startNode = rs;
}
domConstruct.place(breaker, startNode, "after");
if(endNode){
domConstruct.place(endNode, breaker, "after");
}
domConstruct.destroy(rs);
var newrange = rangeapi.create();
newrange.setStart(sNode, 0);
newrange.setEnd(sNode, sNode.length);
selection.removeAllRanges();
selection.addRange(newrange);
doc.execCommand(command);
domConstruct.place(breaker.firstChild, breaker, "before");
domConstruct.destroy(breaker);
newrange.setStart(sNode, 0);
newrange.setEnd(sNode, sNode.length);
selection.removeAllRanges();
selection.addRange(newrange);
selectionapi.collapse(false);
sNode.parentNode.innerHTML = "";
}));
return true;
}
}
}else{
return false;
}
},
_adaptIEList: function(command /*===== , argument =====*/){
// summary:
// This function handles normalizing the IE list behavior as
// much as possible.
// command:
// The list command to execute.
// argument:
// Any additional argument.
// tags:
// private
var selection = rangeapi.getSelection(this.window);
if(selection.isCollapsed){
// In the case of no selection, lets commonize the behavior and
// make sure that it indents if needed.
if(selection.rangeCount && !this.queryCommandValue(command)){
var range = selection.getRangeAt(0);
var sc = range.startContainer;
if(sc && sc.nodeType == 3){
// text node. Lets see if there is a node before it that isn't
// some sort of breaker.
if(!range.startOffset){
// We're at the beginning of a text area. It may have been br split
// Who knows? In any event, we must create the list manually
// or IE may shove too much into the list element. It seems to
// grab content before the text node too if it's br split.
// Why can't IE work like everyone else?
win.withGlobal(this.window, lang.hitch(this, function(){
// Create a space, we'll select and bold it, so
// the whole word doesn't get bolded
var lType = "ul";
if(command === "insertorderedlist"){
lType = "ol";
}
var list = domConstruct.create(lType);
var li = domConstruct.create("li", null, list);
domConstruct.place(list, sc, "before");
// Move in the text node as part of the li.
li.appendChild(sc);
// We need a br after it or the enter key handler
// sometimes throws errors.
domConstruct.create("br", null, list, "after");
// Okay, now lets move our cursor to the beginning.
var newrange = rangeapi.create();
newrange.setStart(sc, 0);
newrange.setEnd(sc, sc.length);
selection.removeAllRanges();
selection.addRange(newrange);
selectionapi.collapse(true);
}));
return true;
}
}
}
}
return false;
},
_handleTextColorOrProperties: function(command, argument){
// summary:
// This function handles appplying text color as best it is
// able to do so when the selection is collapsed, making the
// behavior cross-browser consistent. It also handles the name
// and size for IE.
// command:
// The command.
// argument:
// Any additional arguments.
// tags:
// private
var selection = rangeapi.getSelection(this.window);
var doc = this.document;
var rs, ret, range, txt, startNode, endNode, breaker, sNode;
argument = argument || null;
if(command && selection && selection.isCollapsed){
if(selection.rangeCount){
range = selection.getRangeAt(0);
rs = range.startContainer;
if(rs && rs.nodeType === 3){
// Text node, we have to split it.
win.withGlobal(this.window, lang.hitch(this, function(){
var offset = range.startOffset;
if(rs.length < offset){
//We are not splitting the right node, try to locate the correct one
ret = this._adjustNodeAndOffset(rs, offset);
rs = ret.node;
offset = ret.offset;
}
txt = rs.nodeValue;
startNode = doc.createTextNode(txt.substring(0, offset));
var endText = txt.substring(offset);
if(endText !== ""){
endNode = doc.createTextNode(txt.substring(offset));
}
// Create a space, we'll select and bold it, so
// the whole word doesn't get bolded
breaker = domConstruct.create("span");
sNode = doc.createTextNode(".");
breaker.appendChild(sNode);
// Create a junk node to avoid it trying to stlye the breaker.
// This will get destroyed later.
var extraSpan = domConstruct.create("span");
breaker.appendChild(extraSpan);
if(startNode.length){
domConstruct.place(startNode, rs, "after");
}else{
startNode = rs;
}
domConstruct.place(breaker, startNode, "after");
if(endNode){
domConstruct.place(endNode, breaker, "after");
}
domConstruct.destroy(rs);
var newrange = rangeapi.create();
newrange.setStart(sNode, 0);
newrange.setEnd(sNode, sNode.length);
selection.removeAllRanges();
selection.addRange(newrange);
if(has("webkit")){
// WebKit is frustrating with positioning the cursor.
// It stinks to have a selected space, but there really
// isn't much choice here.
var style = "color";
if(command === "hilitecolor" || command === "backcolor"){
style = "backgroundColor";
}
domStyle.set(breaker, style, argument);
selectionapi.remove();
domConstruct.destroy(extraSpan);
breaker.innerHTML = "&#160;"; // &nbsp;
selectionapi.selectElement(breaker);
this.focus();
}else{
this.execCommand(command, argument);
domConstruct.place(breaker.firstChild, breaker, "before");
domConstruct.destroy(breaker);
newrange.setStart(sNode, 0);
newrange.setEnd(sNode, sNode.length);
selection.removeAllRanges();
selection.addRange(newrange);
selectionapi.collapse(false);
sNode.parentNode.removeChild(sNode);
}
}));
return true;
}
}
}
return false;
},
_adjustNodeAndOffset: function(/*DomNode*/node, /*Int*/offset){
// summary:
// In the case there are multiple text nodes in a row the offset may not be within the node.
// If the offset is larger than the node length, it will attempt to find
// the next text sibling until it locates the text node in which the offset refers to
// node:
// The node to check.
// offset:
// The position to find within the text node
// tags:
// private.
while(node.length < offset && node.nextSibling && node.nextSibling.nodeType === 3){
//Adjust the offset and node in the case of multiple text nodes in a row
offset = offset - node.length;
node = node.nextSibling;
}
return {"node": node, "offset": offset};
},
_tagNamesForCommand: function(command){
// summary:
// Function to return the tab names that are associated
// with a particular style.
// command: String
// The command to return tags for.
// tags:
// private
if(command === "bold"){
return ["b", "strong"];
}else if(command === "italic"){
return ["i","em"];
}else if(command === "strikethrough"){
return ["s", "strike"];
}else if(command === "superscript"){
return ["sup"];
}else if(command === "subscript"){
return ["sub"];
}else if(command === "underline"){
return ["u"];
}
return [];
},
_stripBreakerNodes: function(node){
// summary:
// Function for stripping out the breaker spans inserted by the formatting command.
// Registered as a filter for IE, handles the breaker spans needed to fix up
// How bold/italic/etc, work when selection is collapsed (single cursor).
win.withGlobal(this.window, lang.hitch(this, function(){
var breakers = query(".ieFormatBreakerSpan", node);
var i;
for(i = 0; i < breakers.length; i++){
var b = breakers[i];
while(b.firstChild){
domConstruct.place(b.firstChild, b, "before");
}
domConstruct.destroy(b);
}
}));
return node;
}
});
return RichText;
});
},
'dijit/nls/loading':function(){
define({ root:
//begin v1.x content
({
loadingState: "Loading...",
errorState: "Sorry, an error occurred"
})
//end v1.x content
,
"zh": true,
"zh-tw": true,
"tr": true,
"th": true,
"sv": true,
"sl": true,
"sk": true,
"ru": true,
"ro": true,
"pt": true,
"pt-pt": true,
"pl": true,
"nl": true,
"nb": true,
"ko": true,
"kk": true,
"ja": true,
"it": true,
"hu": true,
"hr": true,
"he": true,
"fr": true,
"fi": true,
"es": true,
"el": true,
"de": true,
"da": true,
"cs": true,
"ca": true,
"az": true,
"ar": true
});
},
'dojo/dnd/Moveable':function(){
define(["../main", "../Evented", "../touch", "./Mover"], function(dojo, Evented, touch) {
// module:
// dojo/dnd/Moveable
// summary:
// TODOC
/*=====
dojo.declare("dojo.dnd.__MoveableArgs", [], {
// handle: Node||String
// A node (or node's id), which is used as a mouse handle.
// If omitted, the node itself is used as a handle.
handle: null,
// delay: Number
// delay move by this number of pixels
delay: 0,
// skip: Boolean
// skip move of form elements
skip: false,
// mover: Object
// a constructor of custom Mover
mover: dojo.dnd.Mover
});
=====*/
dojo.declare("dojo.dnd.Moveable", [Evented], {
// object attributes (for markup)
handle: "",
delay: 0,
skip: false,
constructor: function(node, params){
// summary:
// an object, which makes a node moveable
// node: Node
// a node (or node's id) to be moved
// params: dojo.dnd.__MoveableArgs?
// optional parameters
this.node = dojo.byId(node);
if(!params){ params = {}; }
this.handle = params.handle ? dojo.byId(params.handle) : null;
if(!this.handle){ this.handle = this.node; }
this.delay = params.delay > 0 ? params.delay : 0;
this.skip = params.skip;
this.mover = params.mover ? params.mover : dojo.dnd.Mover;
this.events = [
dojo.connect(this.handle, touch.press, this, "onMouseDown"),
// cancel text selection and text dragging
dojo.connect(this.handle, "ondragstart", this, "onSelectStart"),
dojo.connect(this.handle, "onselectstart", this, "onSelectStart")
];
},
// markup methods
markupFactory: function(params, node, ctor){
return new ctor(node, params);
},
// methods
destroy: function(){
// summary:
// stops watching for possible move, deletes all references, so the object can be garbage-collected
dojo.forEach(this.events, dojo.disconnect);
this.events = this.node = this.handle = null;
},
// mouse event processors
onMouseDown: function(e){
// summary:
// event processor for onmousedown/ontouchstart, creates a Mover for the node
// e: Event
// mouse/touch event
if(this.skip && dojo.dnd.isFormElement(e)){ return; }
if(this.delay){
this.events.push(
dojo.connect(this.handle, touch.move, this, "onMouseMove"),
dojo.connect(this.handle, touch.release, this, "onMouseUp")
);
this._lastX = e.pageX;
this._lastY = e.pageY;
}else{
this.onDragDetected(e);
}
dojo.stopEvent(e);
},
onMouseMove: function(e){
// summary:
// event processor for onmousemove/ontouchmove, used only for delayed drags
// e: Event
// mouse/touch event
if(Math.abs(e.pageX - this._lastX) > this.delay || Math.abs(e.pageY - this._lastY) > this.delay){
this.onMouseUp(e);
this.onDragDetected(e);
}
dojo.stopEvent(e);
},
onMouseUp: function(e){
// summary:
// event processor for onmouseup, used only for delayed drags
// e: Event
// mouse event
for(var i = 0; i < 2; ++i){
dojo.disconnect(this.events.pop());
}
dojo.stopEvent(e);
},
onSelectStart: function(e){
// summary:
// event processor for onselectevent and ondragevent
// e: Event
// mouse event
if(!this.skip || !dojo.dnd.isFormElement(e)){
dojo.stopEvent(e);
}
},
// local events
onDragDetected: function(/* Event */ e){
// summary:
// called when the drag is detected;
// responsible for creation of the mover
new this.mover(this.node, e, this);
},
onMoveStart: function(/* dojo.dnd.Mover */ mover){
// summary:
// called before every move operation
dojo.publish("/dnd/move/start", [mover]);
dojo.addClass(dojo.body(), "dojoMove");
dojo.addClass(this.node, "dojoMoveItem");
},
onMoveStop: function(/* dojo.dnd.Mover */ mover){
// summary:
// called after every move operation
dojo.publish("/dnd/move/stop", [mover]);
dojo.removeClass(dojo.body(), "dojoMove");
dojo.removeClass(this.node, "dojoMoveItem");
},
onFirstMove: function(/* dojo.dnd.Mover */ mover, /* Event */ e){
// summary:
// called during the very first move notification;
// can be used to initialize coordinates, can be overwritten.
// default implementation does nothing
},
onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop, /* Event */ e){
// summary:
// called during every move notification;
// should actually move the node; can be overwritten.
this.onMoving(mover, leftTop);
var s = mover.node.style;
s.left = leftTop.l + "px";
s.top = leftTop.t + "px";
this.onMoved(mover, leftTop);
},
onMoving: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
// summary:
// called before every incremental move; can be overwritten.
// default implementation does nothing
},
onMoved: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
// summary:
// called after every incremental move; can be overwritten.
// default implementation does nothing
}
});
return dojo.dnd.Moveable;
});
},
'dijit/TooltipDialog':function(){
define([
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.replace
"dojo/_base/event", // event.stop
"dojo/keys", // keys
"dojo/_base/lang", // lang.hitch
"./focus",
"./layout/ContentPane",
"./_DialogMixin",
"./form/_FormMixin",
"./_TemplatedMixin",
"dojo/text!./templates/TooltipDialog.html",
"." // exports methods to dijit global
], function(declare, domClass, event, keys, lang,
focus, ContentPane, _DialogMixin, _FormMixin, _TemplatedMixin, template, dijit){
/*=====
var ContentPane = dijit.layout.ContentPane;
var _DialogMixin = dijit._DialogMixin;
var _FormMixin = dijit.form._FormMixin;
var _TemplatedMixin = dijit._TemplatedMixin;
=====*/
// module:
// dijit/TooltipDialog
// summary:
// Pops up a dialog that appears like a Tooltip
return declare("dijit.TooltipDialog",
[ContentPane, _TemplatedMixin, _FormMixin, _DialogMixin], {
// summary:
// Pops up a dialog that appears like a Tooltip
// title: String
// Description of tooltip dialog (required for a11y)
title: "",
// doLayout: [protected] Boolean
// Don't change this parameter from the default value.
// This ContentPane parameter doesn't make sense for TooltipDialog, since TooltipDialog
// is never a child of a layout container, nor can you specify the size of
// TooltipDialog in order to control the size of an inner widget.
doLayout: false,
// autofocus: Boolean
// A Toggle to modify the default focus behavior of a Dialog, which
// is to focus on the first dialog element after opening the dialog.
// False will disable autofocusing. Default: true
autofocus: true,
// baseClass: [protected] String
// The root className to use for the various states of this widget
baseClass: "dijitTooltipDialog",
// _firstFocusItem: [private] [readonly] DomNode
// The pointer to the first focusable node in the dialog.
// Set by `dijit._DialogMixin._getFocusItems`.
_firstFocusItem: null,
// _lastFocusItem: [private] [readonly] DomNode
// The pointer to which node has focus prior to our dialog.
// Set by `dijit._DialogMixin._getFocusItems`.
_lastFocusItem: null,
templateString: template,
_setTitleAttr: function(/*String*/ title){
this.containerNode.title = title;
this._set("title", title)
},
postCreate: function(){
this.inherited(arguments);
this.connect(this.containerNode, "onkeypress", "_onKey");
},
orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ corner){
// summary:
// Configure widget to be displayed in given position relative to the button.
// This is called from the dijit.popup code, and should not be called
// directly.
// tags:
// protected
var newC = "dijitTooltipAB" + (corner.charAt(1) == 'L' ? "Left" : "Right")
+ " dijitTooltip"
+ (corner.charAt(0) == 'T' ? "Below" : "Above");
domClass.replace(this.domNode, newC, this._currentOrientClass || "");
this._currentOrientClass = newC;
},
focus: function(){
// summary:
// Focus on first field
this._getFocusItems(this.containerNode);
focus.focus(this._firstFocusItem);
},
onOpen: function(/*Object*/ pos){
// summary:
// Called when dialog is displayed.
// This is called from the dijit.popup code, and should not be called directly.
// tags:
// protected
this.orient(this.domNode,pos.aroundCorner, pos.corner);
this._onShow(); // lazy load trigger
},
onClose: function(){
// summary:
// Called when dialog is hidden.
// This is called from the dijit.popup code, and should not be called directly.
// tags:
// protected
this.onHide();
},
_onKey: function(/*Event*/ evt){
// summary:
// Handler for keyboard events
// description:
// Keep keyboard focus in dialog; close dialog on escape key
// tags:
// private
var node = evt.target;
if(evt.charOrCode === keys.TAB){
this._getFocusItems(this.containerNode);
}
var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
if(evt.charOrCode == keys.ESCAPE){
// Use setTimeout to avoid crash on IE, see #10396.
setTimeout(lang.hitch(this, "onCancel"), 0);
event.stop(evt);
}else if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === keys.TAB){
if(!singleFocusItem){
focus.focus(this._lastFocusItem); // send focus to last item in dialog
}
event.stop(evt);
}else if(node == this._lastFocusItem && evt.charOrCode === keys.TAB && !evt.shiftKey){
if(!singleFocusItem){
focus.focus(this._firstFocusItem); // send focus to first item in dialog
}
event.stop(evt);
}else if(evt.charOrCode === keys.TAB){
// we want the browser's default tab handling to move focus
// but we don't want the tab to propagate upwards
evt.stopPropagation();
}
}
});
});
},
'dojo/nls/colors':function(){
define({ root:
//begin v1.x content
({
// local representation of all CSS3 named colors, companion to dojo.colors. To be used where descriptive information
// is required for each color, such as a palette widget, and not for specifying color programatically.
//Note: due to the SVG 1.0 spec additions, some of these are alternate spellings for the same color e.g. gray vs. gray.
//TODO: should we be using unique rgb values as keys instead and avoid these duplicates, or rely on the caller to do the reverse mapping?
aliceblue: "alice blue",
antiquewhite: "antique white",
aqua: "aqua",
aquamarine: "aquamarine",
azure: "azure",
beige: "beige",
bisque: "bisque",
black: "black",
blanchedalmond: "blanched almond",
blue: "blue",
blueviolet: "blue-violet",
brown: "brown",
burlywood: "burlywood",
cadetblue: "cadet blue",
chartreuse: "chartreuse",
chocolate: "chocolate",
coral: "coral",
cornflowerblue: "cornflower blue",
cornsilk: "cornsilk",
crimson: "crimson",
cyan: "cyan",
darkblue: "dark blue",
darkcyan: "dark cyan",
darkgoldenrod: "dark goldenrod",
darkgray: "dark gray",
darkgreen: "dark green",
darkgrey: "dark gray", // same as darkgray
darkkhaki: "dark khaki",
darkmagenta: "dark magenta",
darkolivegreen: "dark olive green",
darkorange: "dark orange",
darkorchid: "dark orchid",
darkred: "dark red",
darksalmon: "dark salmon",
darkseagreen: "dark sea green",
darkslateblue: "dark slate blue",
darkslategray: "dark slate gray",
darkslategrey: "dark slate gray", // same as darkslategray
darkturquoise: "dark turquoise",
darkviolet: "dark violet",
deeppink: "deep pink",
deepskyblue: "deep sky blue",
dimgray: "dim gray",
dimgrey: "dim gray", // same as dimgray
dodgerblue: "dodger blue",
firebrick: "fire brick",
floralwhite: "floral white",
forestgreen: "forest green",
fuchsia: "fuchsia",
gainsboro: "gainsboro",
ghostwhite: "ghost white",
gold: "gold",
goldenrod: "goldenrod",
gray: "gray",
green: "green",
greenyellow: "green-yellow",
grey: "gray", // same as gray
honeydew: "honeydew",
hotpink: "hot pink",
indianred: "indian red",
indigo: "indigo",
ivory: "ivory",
khaki: "khaki",
lavender: "lavender",
lavenderblush: "lavender blush",
lawngreen: "lawn green",
lemonchiffon: "lemon chiffon",
lightblue: "light blue",
lightcoral: "light coral",
lightcyan: "light cyan",
lightgoldenrodyellow: "light goldenrod yellow",
lightgray: "light gray",
lightgreen: "light green",
lightgrey: "light gray", // same as lightgray
lightpink: "light pink",
lightsalmon: "light salmon",
lightseagreen: "light sea green",
lightskyblue: "light sky blue",
lightslategray: "light slate gray",
lightslategrey: "light slate gray", // same as lightslategray
lightsteelblue: "light steel blue",
lightyellow: "light yellow",
lime: "lime",
limegreen: "lime green",
linen: "linen",
magenta: "magenta",
maroon: "maroon",
mediumaquamarine: "medium aquamarine",
mediumblue: "medium blue",
mediumorchid: "medium orchid",
mediumpurple: "medium purple",
mediumseagreen: "medium sea green",
mediumslateblue: "medium slate blue",
mediumspringgreen: "medium spring green",
mediumturquoise: "medium turquoise",
mediumvioletred: "medium violet-red",
midnightblue: "midnight blue",
mintcream: "mint cream",
mistyrose: "misty rose",
moccasin: "moccasin",
navajowhite: "navajo white",
navy: "navy",
oldlace: "old lace",
olive: "olive",
olivedrab: "olive drab",
orange: "orange",
orangered: "orange red",
orchid: "orchid",
palegoldenrod: "pale goldenrod",
palegreen: "pale green",
paleturquoise: "pale turquoise",
palevioletred: "pale violet-red",
papayawhip: "papaya whip",
peachpuff: "peach puff",
peru: "peru",
pink: "pink",
plum: "plum",
powderblue: "powder blue",
purple: "purple",
red: "red",
rosybrown: "rosy brown",
royalblue: "royal blue",
saddlebrown: "saddle brown",
salmon: "salmon",
sandybrown: "sandy brown",
seagreen: "sea green",
seashell: "seashell",
sienna: "sienna",
silver: "silver",
skyblue: "sky blue",
slateblue: "slate blue",
slategray: "slate gray",
slategrey: "slate gray", // same as slategray
snow: "snow",
springgreen: "spring green",
steelblue: "steel blue",
tan: "tan",
teal: "teal",
thistle: "thistle",
tomato: "tomato",
transparent: "transparent",
turquoise: "turquoise",
violet: "violet",
wheat: "wheat",
white: "white",
whitesmoke: "white smoke",
yellow: "yellow",
yellowgreen: "yellow green"
})
//end v1.x content
,
"zh": true,
"zh-tw": true,
"tr": true,
"th": true,
"sv": true,
"sl": true,
"sk": true,
"ru": true,
"ro": true,
"pt": true,
"pt-pt": true,
"pl": true,
"nl": true,
"nb": true,
"ko": true,
"kk": true,
"ja": true,
"it": true,
"hu": true,
"hr": true,
"he": true,
"fr": true,
"fi": true,
"es": true,
"el": true,
"de": true,
"da": true,
"cs": true,
"ca": true,
"az": true,
"ar": true
});
},
'dojo/store/util/SimpleQueryEngine':function(){
define(["../../_base/array"], function(arrayUtil) {
// module:
// dojo/store/util/SimpleQueryEngine
// summary:
// The module defines a simple filtering query engine for object stores.
return function(query, options){
// summary:
// Simple query engine that matches using filter functions, named filter
// functions or objects by name-value on a query object hash
//
// description:
// The SimpleQueryEngine provides a way of getting a QueryResults through
// the use of a simple object hash as a filter. The hash will be used to
// match properties on data objects with the corresponding value given. In
// other words, only exact matches will be returned.
//
// This function can be used as a template for more complex query engines;
// for example, an engine can be created that accepts an object hash that
// contains filtering functions, or a string that gets evaluated, etc.
//
// When creating a new dojo.store, simply set the store's queryEngine
// field as a reference to this function.
//
// query: Object
// An object hash with fields that may match fields of items in the store.
// Values in the hash will be compared by normal == operator, but regular expressions
// or any object that provides a test() method are also supported and can be
// used to match strings by more complex expressions
// (and then the regex's or object's test() method will be used to match values).
//
// options: dojo.store.util.SimpleQueryEngine.__queryOptions?
// An object that contains optional information such as sort, start, and count.
//
// returns: Function
// A function that caches the passed query under the field "matches". See any
// of the "query" methods on dojo.stores.
//
// example:
// Define a store with a reference to this engine, and set up a query method.
//
// | var myStore = function(options){
// | // ...more properties here
// | this.queryEngine = dojo.store.util.SimpleQueryEngine;
// | // define our query method
// | this.query = function(query, options){
// | return dojo.store.util.QueryResults(this.queryEngine(query, options)(this.data));
// | };
// | };
// create our matching query function
switch(typeof query){
default:
throw new Error("Can not query with a " + typeof query);
case "object": case "undefined":
var queryObject = query;
query = function(object){
for(var key in queryObject){
var required = queryObject[key];
if(required && required.test){
if(!required.test(object[key])){
return false;
}
}else if(required != object[key]){
return false;
}
}
return true;
};
break;
case "string":
// named query
if(!this[query]){
throw new Error("No filter function " + query + " was found in store");
}
query = this[query];
// fall through
case "function":
// fall through
}
function execute(array){
// execute the whole query, first we filter
var results = arrayUtil.filter(array, query);
// next we sort
if(options && options.sort){
results.sort(function(a, b){
for(var sort, i=0; sort = options.sort[i]; i++){
var aValue = a[sort.attribute];
var bValue = b[sort.attribute];
if (aValue != bValue) {
return !!sort.descending == aValue > bValue ? -1 : 1;
}
}
return 0;
});
}
// now we paginate
if(options && (options.start || options.count)){
var total = results.length;
results = results.slice(options.start || 0, (options.start || 0) + (options.count || Infinity));
results.total = total;
}
return results;
}
execute.matches = query;
return execute;
};
});
},
'dojo/cldr/nls/number':function(){
define({ root:
//begin v1.x content
{
"scientificFormat": "#E0",
"currencySpacing-afterCurrency-currencyMatch": "[:letter:]",
"infinity": "∞",
"list": ";",
"percentSign": "%",
"minusSign": "-",
"currencySpacing-beforeCurrency-surroundingMatch": "[:digit:]",
"decimalFormat-short": "000T",
"currencySpacing-afterCurrency-insertBetween": " ",
"nan": "NaN",
"nativeZeroDigit": "0",
"plusSign": "+",
"currencySpacing-afterCurrency-surroundingMatch": "[:digit:]",
"currencySpacing-beforeCurrency-currencyMatch": "[:letter:]",
"currencyFormat": "¤ #,##0.00",
"perMille": "‰",
"group": ",",
"percentFormat": "#,##0%",
"decimalFormat": "#,##0.###",
"decimal": ".",
"patternDigit": "#",
"currencySpacing-beforeCurrency-insertBetween": " ",
"exponential": "E"
}
//end v1.x content
,
"ar": true,
"ca": true,
"cs": true,
"da": true,
"de": true,
"el": true,
"en": true,
"en-au": true,
"en-gb": true,
"es": true,
"fi": true,
"fr": true,
"fr-ch": true,
"he": true,
"hu": true,
"it": true,
"ja": true,
"ko": true,
"nb": true,
"nl": true,
"pl": true,
"pt": true,
"pt-pt": true,
"ro": true,
"ru": true,
"sk": true,
"sl": true,
"sv": true,
"th": true,
"tr": true,
"zh": true,
"zh-hant": true,
"zh-hk": true
});
},
'dijit/form/_ExpandingTextAreaMixin':function(){
define([
"dojo/_base/declare", // declare
"dojo/dom-construct", // domConstruct.create
"dojo/_base/lang", // lang.hitch
"dojo/_base/window" // win.body
], function(declare, domConstruct, lang, win){
// module:
// dijit/form/_ExpandingTextAreaMixin
// summary:
// Mixin for textarea widgets to add auto-expanding capability
// feature detection
var needsHelpShrinking;
return declare("dijit.form._ExpandingTextAreaMixin", null, {
// summary:
// Mixin for textarea widgets to add auto-expanding capability
_setValueAttr: function(){
this.inherited(arguments);
this.resize();
},
postCreate: function(){
this.inherited(arguments);
var textarea = this.textbox;
if(needsHelpShrinking == undefined){
var te = domConstruct.create('textarea', {rows:"5", cols:"20", value: ' ', style: {zoom:1, overflow:'hidden', visibility:'hidden', position:'absolute', border:"0px solid black", padding:"0px"}}, win.body(), "last");
needsHelpShrinking = te.scrollHeight >= te.clientHeight;
win.body().removeChild(te);
}
this.connect(textarea, "onscroll", "_resizeLater");
this.connect(textarea, "onresize", "_resizeLater");
this.connect(textarea, "onfocus", "_resizeLater");
textarea.style.overflowY = "hidden";
this._estimateHeight();
this._resizeLater();
},
_onInput: function(e){
this.inherited(arguments);
this.resize();
},
_estimateHeight: function(){
// summary:
// Approximate the height when the textarea is invisible with the number of lines in the text.
// Fails when someone calls setValue with a long wrapping line, but the layout fixes itself when the user clicks inside so . . .
// In IE, the resize event is supposed to fire when the textarea becomes visible again and that will correct the size automatically.
//
var textarea = this.textbox;
textarea.style.height = "auto";
// #rows = #newlines+1
// Note: on Moz, the following #rows appears to be 1 too many.
// Actually, Moz is reserving room for the scrollbar.
// If you increase the font size, this behavior becomes readily apparent as the last line gets cut off without the +1.
textarea.rows = (textarea.value.match(/\n/g) || []).length + 2;
},
_resizeLater: function(){
setTimeout(lang.hitch(this, "resize"), 0);
},
resize: function(){
// summary:
// Resizes the textarea vertically (should be called after a style/value change)
function textareaScrollHeight(){
var empty = false;
if(textarea.value === ''){
textarea.value = ' ';
empty = true;
}
var sh = textarea.scrollHeight;
if(empty){ textarea.value = ''; }
return sh;
}
var textarea = this.textbox;
if(textarea.style.overflowY == "hidden"){ textarea.scrollTop = 0; }
if(this.resizeTimer){ clearTimeout(this.resizeTimer); }
this.resizeTimer = null;
if(this.busyResizing){ return; }
this.busyResizing = true;
if(textareaScrollHeight() || textarea.offsetHeight){
var currentHeight = textarea.style.height;
if(!(/px/.test(currentHeight))){
currentHeight = textareaScrollHeight();
textarea.rows = 1;
textarea.style.height = currentHeight + "px";
}
var newH = Math.max(parseInt(currentHeight) - textarea.clientHeight, 0) + textareaScrollHeight();
var newHpx = newH + "px";
if(newHpx != textarea.style.height){
textarea.rows = 1;
textarea.style.height = newHpx;
}
if(needsHelpShrinking){
var scrollHeight = textareaScrollHeight();
textarea.style.height = "auto";
if(textareaScrollHeight() < scrollHeight){ // scrollHeight can shrink so now try a larger value
newHpx = newH - scrollHeight + textareaScrollHeight() + "px";
}
textarea.style.height = newHpx;
}
textarea.style.overflowY = textareaScrollHeight() > textarea.clientHeight ? "auto" : "hidden";
}else{
// hidden content of unknown size
this._estimateHeight();
}
this.busyResizing = false;
},
destroy: function(){
if(this.resizeTimer){ clearTimeout(this.resizeTimer); }
if(this.shrinkTimer){ clearTimeout(this.shrinkTimer); }
this.inherited(arguments);
}
});
});
},
'dijit/MenuItem':function(){
define([
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"dojo/dom-attr", // domAttr.set
"dojo/dom-class", // domClass.toggle
"dojo/_base/event", // event.stop
"dojo/_base/kernel", // kernel.deprecated
"dojo/_base/sniff", // has("ie")
"./_Widget",
"./_TemplatedMixin",
"./_Contained",
"./_CssStateMixin",
"dojo/text!./templates/MenuItem.html"
], function(declare, dom, domAttr, domClass, event, kernel, has,
_Widget, _TemplatedMixin, _Contained, _CssStateMixin, template){
/*=====
var _Widget = dijit._Widget;
var _TemplatedMixin = dijit._TemplatedMixin;
var _Contained = dijit._Contained;
var _CssStateMixin = dijit._CssStateMixin;
=====*/
// module:
// dijit/MenuItem
// summary:
// A line item in a Menu Widget
return declare("dijit.MenuItem",
[_Widget, _TemplatedMixin, _Contained, _CssStateMixin],
{
// summary:
// A line item in a Menu Widget
// Make 3 columns
// icon, label, and expand arrow (BiDi-dependent) indicating sub-menu
templateString: template,
baseClass: "dijitMenuItem",
// label: String
// Menu text
label: '',
_setLabelAttr: { node: "containerNode", type: "innerHTML" },
// iconClass: String
// Class to apply to DOMNode to make it display an icon.
iconClass: "dijitNoIcon",
_setIconClassAttr: { node: "iconNode", type: "class" },
// accelKey: String
// Text for the accelerator (shortcut) key combination.
// Note that although Menu can display accelerator keys there
// is no infrastructure to actually catch and execute these
// accelerators.
accelKey: "",
// disabled: Boolean
// If true, the menu item is disabled.
// If false, the menu item is enabled.
disabled: false,
_fillContent: function(/*DomNode*/ source){
// If button label is specified as srcNodeRef.innerHTML rather than
// this.params.label, handle it here.
if(source && !("label" in this.params)){
this.set('label', source.innerHTML);
}
},
buildRendering: function(){
this.inherited(arguments);
var label = this.id+"_text";
domAttr.set(this.containerNode, "id", label);
if(this.accelKeyNode){
domAttr.set(this.accelKeyNode, "id", this.id + "_accel");
label += " " + this.id + "_accel";
}
this.domNode.setAttribute("aria-labelledby", label);
dom.setSelectable(this.domNode, false);
},
_onHover: function(){
// summary:
// Handler when mouse is moved onto menu item
// tags:
// protected
this.getParent().onItemHover(this);
},
_onUnhover: function(){
// summary:
// Handler when mouse is moved off of menu item,
// possibly to a child menu, or maybe to a sibling
// menuitem or somewhere else entirely.
// tags:
// protected
// if we are unhovering the currently selected item
// then unselect it
this.getParent().onItemUnhover(this);
// When menu is hidden (collapsed) due to clicking a MenuItem and having it execute,
// FF and IE don't generate an onmouseout event for the MenuItem.
// So, help out _CssStateMixin in this case.
this._set("hovering", false);
},
_onClick: function(evt){
// summary:
// Internal handler for click events on MenuItem.
// tags:
// private
this.getParent().onItemClick(this, evt);
event.stop(evt);
},
onClick: function(/*Event*/){
// summary:
// User defined function to handle clicks
// tags:
// callback
},
focus: function(){
// summary:
// Focus on this MenuItem
try{
if(has("ie") == 8){
// needed for IE8 which won't scroll TR tags into view on focus yet calling scrollIntoView creates flicker (#10275)
this.containerNode.focus();
}
this.focusNode.focus();
}catch(e){
// this throws on IE (at least) in some scenarios
}
},
_onFocus: function(){
// summary:
// This is called by the focus manager when focus
// goes to this MenuItem or a child menu.
// tags:
// protected
this._setSelected(true);
this.getParent()._onItemFocus(this);
this.inherited(arguments);
},
_setSelected: function(selected){
// summary:
// Indicate that this node is the currently selected one
// tags:
// private
/***
* TODO: remove this method and calls to it, when _onBlur() is working for MenuItem.
* Currently _onBlur() gets called when focus is moved from the MenuItem to a child menu.
* That's not supposed to happen, but the problem is:
* In order to allow dijit.popup's getTopPopup() to work,a sub menu's popupParent
* points to the parent Menu, bypassing the parent MenuItem... thus the
* MenuItem is not in the chain of active widgets and gets a premature call to
* _onBlur()
*/
domClass.toggle(this.domNode, "dijitMenuItemSelected", selected);
},
setLabel: function(/*String*/ content){
// summary:
// Deprecated. Use set('label', ...) instead.
// tags:
// deprecated
kernel.deprecated("dijit.MenuItem.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
this.set("label", content);
},
setDisabled: function(/*Boolean*/ disabled){
// summary:
// Deprecated. Use set('disabled', bool) instead.
// tags:
// deprecated
kernel.deprecated("dijit.Menu.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
this.set('disabled', disabled);
},
_setDisabledAttr: function(/*Boolean*/ value){
// summary:
// Hook for attr('disabled', ...) to work.
// Enable or disable this menu item.
this.focusNode.setAttribute('aria-disabled', value ? 'true' : 'false');
this._set("disabled", value);
},
_setAccelKeyAttr: function(/*String*/ value){
// summary:
// Hook for attr('accelKey', ...) to work.
// Set accelKey on this menu item.
this.accelKeyNode.style.display=value?"":"none";
this.accelKeyNode.innerHTML=value;
//have to use colSpan to make it work in IE
domAttr.set(this.containerNode,'colSpan',value?"1":"2");
this._set("accelKey", value);
}
});
});
},
'dijit/MenuBarItem':function(){
define([
"dojo/_base/declare", // declare
"./MenuItem",
"dojo/text!./templates/MenuBarItem.html"
], function(declare, MenuItem, template){
/*=====
var MenuItem = dijit.MenuItem;
=====*/
// module:
// dijit/MenuBarItem
// summary:
// Item in a MenuBar that's clickable, and doesn't spawn a submenu when pressed (or hovered)
var _MenuBarItemMixin = declare("dijit._MenuBarItemMixin", null, {
templateString: template,
// Map widget attributes to DOMNode attributes.
_setIconClassAttr: null // cancel MenuItem setter because we don't have a place for an icon
});
var MenuBarItem = declare("dijit.MenuBarItem", [MenuItem, _MenuBarItemMixin], {
// summary:
// Item in a MenuBar that's clickable, and doesn't spawn a submenu when pressed (or hovered)
});
MenuBarItem._MenuBarItemMixin = _MenuBarItemMixin; // dojox.mobile is accessing this
return MenuBarItem;
});
},
'dijit/layout/TabController':function(){
define([
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"dojo/dom-attr", // domAttr.attr
"dojo/dom-class", // domClass.toggle
"dojo/i18n", // i18n.getLocalization
"dojo/_base/lang", // lang.hitch lang.trim
"./StackController",
"../Menu",
"../MenuItem",
"dojo/text!./templates/_TabButton.html",
"dojo/i18n!../nls/common"
], function(declare, dom, domAttr, domClass, i18n, lang, StackController, Menu, MenuItem, template){
/*=====
var StackController = dijit.layout.StackController;
var Menu = dijit.Menu;
var MenuItem = dijit.MenuItem;
=====*/
// module:
// dijit/layout/TabController
// summary:
// Set of tabs (the things with titles and a close button, that you click to show a tab panel).
// Used internally by `dijit.layout.TabContainer`.
var TabButton = declare("dijit.layout._TabButton", StackController.StackButton, {
// summary:
// A tab (the thing you click to select a pane).
// description:
// Contains the title of the pane, and optionally a close-button to destroy the pane.
// This is an internal widget and should not be instantiated directly.
// tags:
// private
// baseClass: String
// The CSS class applied to the domNode.
baseClass: "dijitTab",
// Apply dijitTabCloseButtonHover when close button is hovered
cssStateNodes: {
closeNode: "dijitTabCloseButton"
},
templateString: template,
// Override _FormWidget.scrollOnFocus.
// Don't scroll the whole tab container into view when the button is focused.
scrollOnFocus: false,
buildRendering: function(){
this.inherited(arguments);
dom.setSelectable(this.containerNode, false);
},
startup: function(){
this.inherited(arguments);
var n = this.domNode;
// Required to give IE6 a kick, as it initially hides the
// tabs until they are focused on.
setTimeout(function(){
n.className = n.className;
}, 1);
},
_setCloseButtonAttr: function(/*Boolean*/ disp){
// summary:
// Hide/show close button
this._set("closeButton", disp);
domClass.toggle(this.innerDiv, "dijitClosable", disp);
this.closeNode.style.display = disp ? "" : "none";
if(disp){
var _nlsResources = i18n.getLocalization("dijit", "common");
if(this.closeNode){
domAttr.set(this.closeNode,"title", _nlsResources.itemClose);
}
// add context menu onto title button
this._closeMenu = new Menu({
id: this.id+"_Menu",
dir: this.dir,
lang: this.lang,
textDir: this.textDir,
targetNodeIds: [this.domNode]
});
this._closeMenu.addChild(new MenuItem({
label: _nlsResources.itemClose,
dir: this.dir,
lang: this.lang,
textDir: this.textDir,
onClick: lang.hitch(this, "onClickCloseButton")
}));
}else{
if(this._closeMenu){
this._closeMenu.destroyRecursive();
delete this._closeMenu;
}
}
},
_setLabelAttr: function(/*String*/ content){
// summary:
// Hook for set('label', ...) to work.
// description:
// takes an HTML string.
// Inherited ToggleButton implementation will Set the label (text) of the button;
// Need to set the alt attribute of icon on tab buttons if no label displayed
this.inherited(arguments);
if(!this.showLabel && !this.params.title){
this.iconNode.alt = lang.trim(this.containerNode.innerText || this.containerNode.textContent || '');
}
},
destroy: function(){
if(this._closeMenu){
this._closeMenu.destroyRecursive();
delete this._closeMenu;
}
this.inherited(arguments);
}
});
var TabController = declare("dijit.layout.TabController", StackController, {
// summary:
// Set of tabs (the things with titles and a close button, that you click to show a tab panel).
// Used internally by `dijit.layout.TabContainer`.
// description:
// Lets the user select the currently shown pane in a TabContainer or StackContainer.
// TabController also monitors the TabContainer, and whenever a pane is
// added or deleted updates itself accordingly.
// tags:
// private
baseClass: "dijitTabController",
templateString: "<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'></div>",
// tabPosition: String
// Defines where tabs go relative to the content.
// "top", "bottom", "left-h", "right-h"
tabPosition: "top",
// buttonWidget: Constructor
// The tab widget to create to correspond to each page
buttonWidget: TabButton,
_rectifyRtlTabList: function(){
// summary:
// For left/right TabContainer when page is RTL mode, rectify the width of all tabs to be equal, otherwise the tab widths are different in IE
if(0 >= this.tabPosition.indexOf('-h')){ return; }
if(!this.pane2button){ return; }
var maxWidth = 0;
for(var pane in this.pane2button){
var ow = this.pane2button[pane].innerDiv.scrollWidth;
maxWidth = Math.max(maxWidth, ow);
}
//unify the length of all the tabs
for(pane in this.pane2button){
this.pane2button[pane].innerDiv.style.width = maxWidth + 'px';
}
}
});
TabController.TabButton = TabButton; // for monkey patching
return TabController;
});
},
'dojo/cldr/supplemental':function(){
define(["../_base/kernel", "../_base/lang", "../i18n"], function(dojo, lang) {
// module:
// dojo/cldr/supplemental
// summary:
// TODOC
lang.getObject("cldr.supplemental", true, dojo);
dojo.cldr.supplemental.getFirstDayOfWeek = function(/*String?*/locale){
// summary: Returns a zero-based index for first day of the week
// description:
// Returns a zero-based index for first day of the week, as used by the local (Gregorian) calendar.
// e.g. Sunday (returns 0), or Monday (returns 1)
// from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/weekData/firstDay
var firstDay = {/*default is 1=Monday*/
mv:5,
ae:6,af:6,bh:6,dj:6,dz:6,eg:6,er:6,et:6,iq:6,ir:6,jo:6,ke:6,kw:6,
ly:6,ma:6,om:6,qa:6,sa:6,sd:6,so:6,sy:6,tn:6,ye:6,
ar:0,as:0,az:0,bw:0,ca:0,cn:0,fo:0,ge:0,gl:0,gu:0,hk:0,
il:0,'in':0,jm:0,jp:0,kg:0,kr:0,la:0,mh:0,mn:0,mo:0,mp:0,
mt:0,nz:0,ph:0,pk:0,sg:0,th:0,tt:0,tw:0,um:0,us:0,uz:0,
vi:0,zw:0
// variant. do not use? gb:0,
};
var country = dojo.cldr.supplemental._region(locale);
var dow = firstDay[country];
return (dow === undefined) ? 1 : dow; /*Number*/
};
dojo.cldr.supplemental._region = function(/*String?*/locale){
locale = dojo.i18n.normalizeLocale(locale);
var tags = locale.split('-');
var region = tags[1];
if(!region){
// IE often gives language only (#2269)
// Arbitrary mappings of language-only locales to a country:
region = {de:"de", en:"us", es:"es", fi:"fi", fr:"fr", he:"il", hu:"hu", it:"it",
ja:"jp", ko:"kr", nl:"nl", pt:"br", sv:"se", zh:"cn"}[tags[0]];
}else if(region.length == 4){
// The ISO 3166 country code is usually in the second position, unless a
// 4-letter script is given. See http://www.ietf.org/rfc/rfc4646.txt
region = tags[2];
}
return region;
};
dojo.cldr.supplemental.getWeekend = function(/*String?*/locale){
// summary: Returns a hash containing the start and end days of the weekend
// description:
// Returns a hash containing the start and end days of the weekend according to local custom using locale,
// or by default in the user's locale.
// e.g. {start:6, end:0}
// from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/weekData/weekend{Start,End}
var weekendStart = {/*default is 6=Saturday*/
'in':0,
af:4,dz:4,ir:4,om:4,sa:4,ye:4,
ae:5,bh:5,eg:5,il:5,iq:5,jo:5,kw:5,ly:5,ma:5,qa:5,sd:5,sy:5,tn:5
};
var weekendEnd = {/*default is 0=Sunday*/
af:5,dz:5,ir:5,om:5,sa:5,ye:5,
ae:6,bh:5,eg:6,il:6,iq:6,jo:6,kw:6,ly:6,ma:6,qa:6,sd:6,sy:6,tn:6
};
var country = dojo.cldr.supplemental._region(locale);
var start = weekendStart[country];
var end = weekendEnd[country];
if(start === undefined){start=6;}
if(end === undefined){end=0;}
return {start:start, end:end}; /*Object {start,end}*/
};
return dojo.cldr.supplemental;
});
},
'dijit/MenuBar':function(){
require({cache:{
'url:dijit/templates/MenuBar.html':"<div class=\"dijitMenuBar dijitMenuPassive\" data-dojo-attach-point=\"containerNode\" role=\"menubar\" tabIndex=\"${tabIndex}\" data-dojo-attach-event=\"onkeypress: _onKeyPress\"></div>\n"}});
define("dijit/MenuBar", [
"dojo/_base/declare", // declare
"dojo/_base/event", // event.stop
"dojo/keys", // keys.DOWN_ARROW
"./_MenuBase",
"dojo/text!./templates/MenuBar.html"
], function(declare, event, keys, _MenuBase, template){
/*=====
var _MenuBase = dijit._MenuBase;
=====*/
// module:
// dijit/MenuBar
// summary:
// A menu bar, listing menu choices horizontally, like the "File" menu in most desktop applications
return declare("dijit.MenuBar", _MenuBase, {
// summary:
// A menu bar, listing menu choices horizontally, like the "File" menu in most desktop applications
templateString: template,
baseClass: "dijitMenuBar",
// _isMenuBar: [protected] Boolean
// This is a MenuBar widget, not a (vertical) Menu widget.
_isMenuBar: true,
postCreate: function(){
var l = this.isLeftToRight();
this.connectKeyNavHandlers(
l ? [keys.LEFT_ARROW] : [keys.RIGHT_ARROW],
l ? [keys.RIGHT_ARROW] : [keys.LEFT_ARROW]
);
// parameter to dijit.popup.open() about where to put popup (relative to this.domNode)
this._orient = ["below"];
},
focusChild: function(item){
// overload focusChild so that whenever the focus is moved to a new item,
// check the previous focused whether it has its popup open, if so, after
// focusing the new item, open its submenu immediately
var prev_item = this.focusedChild,
showpopup = prev_item && prev_item.popup && prev_item.popup.isShowingNow;
this.inherited(arguments);
if(showpopup && item.popup && !item.disabled){
this._openPopup(); // TODO: on down arrow, _openPopup() is called here and in onItemClick()
}
},
_onKeyPress: function(/*Event*/ evt){
// summary:
// Handle keyboard based menu navigation.
// tags:
// protected
if(evt.ctrlKey || evt.altKey){ return; }
switch(evt.charOrCode){
case keys.DOWN_ARROW:
this._moveToPopup(evt);
event.stop(evt);
}
},
onItemClick: function(/*dijit._Widget*/ item, /*Event*/ evt){
// summary:
// Handle clicks on an item. Cancels a dropdown if already open.
// tags:
// private
if(item.popup && item.popup.isShowingNow){
item.popup.onCancel();
}else{
this.inherited(arguments);
}
}
});
});
},
'dijit/ToolbarSeparator':function(){
define("dijit/ToolbarSeparator", [
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"./_Widget",
"./_TemplatedMixin"
], function(declare, dom, _Widget, _TemplatedMixin){
/*=====
var _Widget = dijit._Widget;
var _TemplatedMixin = dijit._TemplatedMixin;
=====*/
// module:
// dijit/ToolbarSeparator
// summary:
// A spacer between two `dijit.Toolbar` items
return declare("dijit.ToolbarSeparator", [_Widget, _TemplatedMixin], {
// summary:
// A spacer between two `dijit.Toolbar` items
templateString: '<div class="dijitToolbarSeparator dijitInline" role="presentation"></div>',
buildRendering: function(){
this.inherited(arguments);
dom.setSelectable(this.domNode, false);
},
isFocusable: function(){
// summary:
// This widget isn't focusable, so pass along that fact.
// tags:
// protected
return false;
}
});
});
},
'dijit/layout/StackController':function(){
define([
"dojo/_base/array", // array.forEach array.indexOf array.map
"dojo/_base/declare", // declare
"dojo/_base/event", // event.stop
"dojo/keys", // keys
"dojo/_base/lang", // lang.getObject
"dojo/_base/sniff", // has("ie")
"../focus", // focus.focus()
"../registry", // registry.byId
"../_Widget",
"../_TemplatedMixin",
"../_Container",
"../form/ToggleButton",
"dojo/i18n!../nls/common"
], function(array, declare, event, keys, lang, has,
focus, registry, _Widget, _TemplatedMixin, _Container, ToggleButton){
/*=====
var _Widget = dijit._Widget;
var _TemplatedMixin = dijit._TemplatedMixin;
var _Container = dijit._Container;
var ToggleButton = dijit.form.ToggleButton;
=====*/
// module:
// dijit/layout/StackController
// summary:
// Set of buttons to select a page in a `dijit.layout.StackContainer`
var StackButton = declare("dijit.layout._StackButton", ToggleButton, {
// summary:
// Internal widget used by StackContainer.
// description:
// The button-like or tab-like object you click to select or delete a page
// tags:
// private
// Override _FormWidget.tabIndex.
// StackContainer buttons are not in the tab order by default.
// Probably we should be calling this.startupKeyNavChildren() instead.
tabIndex: "-1",
// closeButton: Boolean
// When true, display close button for this tab
closeButton: false,
_setCheckedAttr: function(/*Boolean*/ value, /*Boolean?*/ priorityChange){
this.inherited(arguments);
this.focusNode.removeAttribute("aria-pressed");
},
buildRendering: function(/*Event*/ evt){
this.inherited(arguments);
(this.focusNode || this.domNode).setAttribute("role", "tab");
},
onClick: function(/*Event*/ /*===== evt =====*/){
// summary:
// This is for TabContainer where the tabs are <span> rather than button,
// so need to set focus explicitly (on some browsers)
// Note that you shouldn't override this method, but you can connect to it.
focus.focus(this.focusNode);
// ... now let StackController catch the event and tell me what to do
},
onClickCloseButton: function(/*Event*/ evt){
// summary:
// StackContainer connects to this function; if your widget contains a close button
// then clicking it should call this function.
// Note that you shouldn't override this method, but you can connect to it.
evt.stopPropagation();
}
});
var StackController = declare("dijit.layout.StackController", [_Widget, _TemplatedMixin, _Container], {
// summary:
// Set of buttons to select a page in a `dijit.layout.StackContainer`
// description:
// Monitors the specified StackContainer, and whenever a page is
// added, deleted, or selected, updates itself accordingly.
baseClass: "dijitStackController",
templateString: "<span role='tablist' data-dojo-attach-event='onkeypress'></span>",
// containerId: [const] String
// The id of the page container that I point to
containerId: "",
// buttonWidget: [const] Constructor
// The button widget to create to correspond to each page
buttonWidget: StackButton,
constructor: function(){
this.pane2button = {}; // mapping from pane id to buttons
this.pane2connects = {}; // mapping from pane id to this.connect() handles
this.pane2watches = {}; // mapping from pane id to watch() handles
},
postCreate: function(){
this.inherited(arguments);
// Listen to notifications from StackContainer
this.subscribe(this.containerId+"-startup", "onStartup");
this.subscribe(this.containerId+"-addChild", "onAddChild");
this.subscribe(this.containerId+"-removeChild", "onRemoveChild");
this.subscribe(this.containerId+"-selectChild", "onSelectChild");
this.subscribe(this.containerId+"-containerKeyPress", "onContainerKeyPress");
},
onStartup: function(/*Object*/ info){
// summary:
// Called after StackContainer has finished initializing
// tags:
// private
array.forEach(info.children, this.onAddChild, this);
if(info.selected){
// Show button corresponding to selected pane (unless selected
// is null because there are no panes)
this.onSelectChild(info.selected);
}
},
destroy: function(){
for(var pane in this.pane2button){
this.onRemoveChild(registry.byId(pane));
}
this.inherited(arguments);
},
onAddChild: function(/*dijit._Widget*/ page, /*Integer?*/ insertIndex){
// summary:
// Called whenever a page is added to the container.
// Create button corresponding to the page.
// tags:
// private
// create an instance of the button widget
// (remove typeof buttonWidget == string support in 2.0)
var cls = lang.isString(this.buttonWidget) ? lang.getObject(this.buttonWidget) : this.buttonWidget;
var button = new cls({
id: this.id + "_" + page.id,
label: page.title,
dir: page.dir,
lang: page.lang,
textDir: page.textDir,
showLabel: page.showTitle,
iconClass: page.iconClass,
closeButton: page.closable,
title: page.tooltip
});
button.focusNode.setAttribute("aria-selected", "false");
// map from page attribute to corresponding tab button attribute
var pageAttrList = ["title", "showTitle", "iconClass", "closable", "tooltip"],
buttonAttrList = ["label", "showLabel", "iconClass", "closeButton", "title"];
// watch() so events like page title changes are reflected in tab button
this.pane2watches[page.id] = array.map(pageAttrList, function(pageAttr, idx){
return page.watch(pageAttr, function(name, oldVal, newVal){
button.set(buttonAttrList[idx], newVal);
});
});
// connections so that clicking a tab button selects the corresponding page
this.pane2connects[page.id] = [
this.connect(button, 'onClick', lang.hitch(this,"onButtonClick", page)),
this.connect(button, 'onClickCloseButton', lang.hitch(this,"onCloseButtonClick", page))
];
this.addChild(button, insertIndex);
this.pane2button[page.id] = button;
page.controlButton = button; // this value might be overwritten if two tabs point to same container
if(!this._currentChild){ // put the first child into the tab order
button.focusNode.setAttribute("tabIndex", "0");
button.focusNode.setAttribute("aria-selected", "true");
this._currentChild = page;
}
// make sure all tabs have the same length
if(!this.isLeftToRight() && has("ie") && this._rectifyRtlTabList){
this._rectifyRtlTabList();
}
},
onRemoveChild: function(/*dijit._Widget*/ page){
// summary:
// Called whenever a page is removed from the container.
// Remove the button corresponding to the page.
// tags:
// private
if(this._currentChild === page){ this._currentChild = null; }
// disconnect/unwatch connections/watches related to page being removed
array.forEach(this.pane2connects[page.id], lang.hitch(this, "disconnect"));
delete this.pane2connects[page.id];
array.forEach(this.pane2watches[page.id], function(w){ w.unwatch(); });
delete this.pane2watches[page.id];
var button = this.pane2button[page.id];
if(button){
this.removeChild(button);
delete this.pane2button[page.id];
button.destroy();
}
delete page.controlButton;
},
onSelectChild: function(/*dijit._Widget*/ page){
// summary:
// Called when a page has been selected in the StackContainer, either by me or by another StackController
// tags:
// private
if(!page){ return; }
if(this._currentChild){
var oldButton=this.pane2button[this._currentChild.id];
oldButton.set('checked', false);
oldButton.focusNode.setAttribute("aria-selected", "false");
oldButton.focusNode.setAttribute("tabIndex", "-1");
}
var newButton=this.pane2button[page.id];
newButton.set('checked', true);
newButton.focusNode.setAttribute("aria-selected", "true");
this._currentChild = page;
newButton.focusNode.setAttribute("tabIndex", "0");
var container = registry.byId(this.containerId);
container.containerNode.setAttribute("aria-labelledby", newButton.id);
},
onButtonClick: function(/*dijit._Widget*/ page){
// summary:
// Called whenever one of my child buttons is pressed in an attempt to select a page
// tags:
// private
if(this._currentChild.id === page.id) {
//In case the user clicked the checked button, keep it in the checked state because it remains to be the selected stack page.
var button=this.pane2button[page.id];
button.set('checked', true);
}
var container = registry.byId(this.containerId);
container.selectChild(page);
},
onCloseButtonClick: function(/*dijit._Widget*/ page){
// summary:
// Called whenever one of my child buttons [X] is pressed in an attempt to close a page
// tags:
// private
var container = registry.byId(this.containerId);
container.closeChild(page);
if(this._currentChild){
var b = this.pane2button[this._currentChild.id];
if(b){
focus.focus(b.focusNode || b.domNode);
}
}
},
// TODO: this is a bit redundant with forward, back api in StackContainer
adjacent: function(/*Boolean*/ forward){
// summary:
// Helper for onkeypress to find next/previous button
// tags:
// private
if(!this.isLeftToRight() && (!this.tabPosition || /top|bottom/.test(this.tabPosition))){ forward = !forward; }
// find currently focused button in children array
var children = this.getChildren();
var current = array.indexOf(children, this.pane2button[this._currentChild.id]);
// pick next button to focus on
var offset = forward ? 1 : children.length - 1;
return children[ (current + offset) % children.length ]; // dijit._Widget
},
onkeypress: function(/*Event*/ e){
// summary:
// Handle keystrokes on the page list, for advancing to next/previous button
// and closing the current page if the page is closable.
// tags:
// private
if(this.disabled || e.altKey ){ return; }
var forward = null;
if(e.ctrlKey || !e._djpage){
switch(e.charOrCode){
case keys.LEFT_ARROW:
case keys.UP_ARROW:
if(!e._djpage){ forward = false; }
break;
case keys.PAGE_UP:
if(e.ctrlKey){ forward = false; }
break;
case keys.RIGHT_ARROW:
case keys.DOWN_ARROW:
if(!e._djpage){ forward = true; }
break;
case keys.PAGE_DOWN:
if(e.ctrlKey){ forward = true; }
break;
case keys.HOME:
case keys.END:
var children = this.getChildren();
if(children && children.length){
children[e.charOrCode == keys.HOME ? 0 : children.length-1].onClick();
}
event.stop(e);
break;
case keys.DELETE:
if(this._currentChild.closable){
this.onCloseButtonClick(this._currentChild);
}
event.stop(e);
break;
default:
if(e.ctrlKey){
if(e.charOrCode === keys.TAB){
this.adjacent(!e.shiftKey).onClick();
event.stop(e);
}else if(e.charOrCode == "w"){
if(this._currentChild.closable){
this.onCloseButtonClick(this._currentChild);
}
event.stop(e); // avoid browser tab closing.
}
}
}
// handle next/previous page navigation (left/right arrow, etc.)
if(forward !== null){
this.adjacent(forward).onClick();
event.stop(e);
}
}
},
onContainerKeyPress: function(/*Object*/ info){
// summary:
// Called when there was a keypress on the container
// tags:
// private
info.e._djpage = info.page;
this.onkeypress(info.e);
}
});
StackController.StackButton = StackButton; // for monkey patching
return StackController;
});
},
'url:dijit/templates/TooltipDialog.html':"<div role=\"presentation\" tabIndex=\"-1\">\n\t<div class=\"dijitTooltipContainer\" role=\"presentation\">\n\t\t<div class =\"dijitTooltipContents dijitTooltipFocusNode\" data-dojo-attach-point=\"containerNode\" role=\"dialog\"></div>\n\t</div>\n\t<div class=\"dijitTooltipConnector\" role=\"presentation\"></div>\n</div>\n",
'dojo/dnd/Mover':function(){
define(["../main", "../Evented", "../touch", "./common", "./autoscroll"], function(dojo, Evented, touch) {
// module:
// dojo/dnd/Mover
// summary:
// TODOC
dojo.declare("dojo.dnd.Mover", [Evented], {
constructor: function(node, e, host){
// summary:
// an object which makes a node follow the mouse, or touch-drag on touch devices.
// Used as a default mover, and as a base class for custom movers.
// node: Node
// a node (or node's id) to be moved
// e: Event
// a mouse event, which started the move;
// only pageX and pageY properties are used
// host: Object?
// object which implements the functionality of the move,
// and defines proper events (onMoveStart and onMoveStop)
this.node = dojo.byId(node);
this.marginBox = {l: e.pageX, t: e.pageY};
this.mouseButton = e.button;
var h = (this.host = host), d = node.ownerDocument;
this.events = [
// At the start of a drag, onFirstMove is called, and then the following two
// connects are disconnected
dojo.connect(d, touch.move, this, "onFirstMove"),
// These are called continually during the drag
dojo.connect(d, touch.move, this, "onMouseMove"),
// And these are called at the end of the drag
dojo.connect(d, touch.release, this, "onMouseUp"),
// cancel text selection and text dragging
dojo.connect(d, "ondragstart", dojo.stopEvent),
dojo.connect(d.body, "onselectstart", dojo.stopEvent)
];
// notify that the move has started
if(h && h.onMoveStart){
h.onMoveStart(this);
}
},
// mouse event processors
onMouseMove: function(e){
// summary:
// event processor for onmousemove/ontouchmove
// e: Event
// mouse/touch event
dojo.dnd.autoScroll(e);
var m = this.marginBox;
this.host.onMove(this, {l: m.l + e.pageX, t: m.t + e.pageY}, e);
dojo.stopEvent(e);
},
onMouseUp: function(e){
if(dojo.isWebKit && dojo.isMac && this.mouseButton == 2 ?
e.button == 0 : this.mouseButton == e.button){ // TODO Should condition be met for touch devices, too?
this.destroy();
}
dojo.stopEvent(e);
},
// utilities
onFirstMove: function(e){
// summary:
// makes the node absolute; it is meant to be called only once.
// relative and absolutely positioned nodes are assumed to use pixel units
var s = this.node.style, l, t, h = this.host;
switch(s.position){
case "relative":
case "absolute":
// assume that left and top values are in pixels already
l = Math.round(parseFloat(s.left)) || 0;
t = Math.round(parseFloat(s.top)) || 0;
break;
default:
s.position = "absolute"; // enforcing the absolute mode
var m = dojo.marginBox(this.node);
// event.pageX/pageY (which we used to generate the initial
// margin box) includes padding and margin set on the body.
// However, setting the node's position to absolute and then
// doing dojo.marginBox on it *doesn't* take that additional
// space into account - so we need to subtract the combined
// padding and margin. We use getComputedStyle and
// _getMarginBox/_getContentBox to avoid the extra lookup of
// the computed style.
var b = dojo.doc.body;
var bs = dojo.getComputedStyle(b);
var bm = dojo._getMarginBox(b, bs);
var bc = dojo._getContentBox(b, bs);
l = m.l - (bc.l - bm.l);
t = m.t - (bc.t - bm.t);
break;
}
this.marginBox.l = l - this.marginBox.l;
this.marginBox.t = t - this.marginBox.t;
if(h && h.onFirstMove){
h.onFirstMove(this, e);
}
// Disconnect onmousemove and ontouchmove events that call this function
dojo.disconnect(this.events.shift());
},
destroy: function(){
// summary:
// stops the move, deletes all references, so the object can be garbage-collected
dojo.forEach(this.events, dojo.disconnect);
// undo global settings
var h = this.host;
if(h && h.onMoveStop){
h.onMoveStop(this);
}
// destroy objects
this.events = this.node = this.host = null;
}
});
return dojo.dnd.Mover;
});
},
'dijit/form/HorizontalRule':function(){
define([
"dojo/_base/declare", // declare
"../_Widget",
"../_TemplatedMixin"
], function(declare, _Widget, _TemplatedMixin){
/*=====
var _Widget = dijit._Widget;
var _TemplatedMixin = dijit._TemplatedMixin;
=====*/
// module:
// dijit/form/HorizontalRule
// summary:
// Hash marks for `dijit.form.HorizontalSlider`
return declare("dijit.form.HorizontalRule", [_Widget, _TemplatedMixin], {
// summary:
// Hash marks for `dijit.form.HorizontalSlider`
templateString: '<div class="dijitRuleContainer dijitRuleContainerH"></div>',
// count: Integer
// Number of hash marks to generate
count: 3,
// container: String
// For HorizontalSlider, this is either "topDecoration" or "bottomDecoration",
// and indicates whether this rule goes above or below the slider.
container: "containerNode",
// ruleStyle: String
// CSS style to apply to individual hash marks
ruleStyle: "",
_positionPrefix: '<div class="dijitRuleMark dijitRuleMarkH" style="left:',
_positionSuffix: '%;',
_suffix: '"></div>',
_genHTML: function(pos){
return this._positionPrefix + pos + this._positionSuffix + this.ruleStyle + this._suffix;
},
// _isHorizontal: [protected extension] Boolean
// VerticalRule will override this...
_isHorizontal: true,
buildRendering: function(){
this.inherited(arguments);
var innerHTML;
if(this.count == 1){
innerHTML = this._genHTML(50, 0);
}else{
var i;
var interval = 100 / (this.count-1);
if(!this._isHorizontal || this.isLeftToRight()){
innerHTML = this._genHTML(0, 0);
for(i=1; i < this.count-1; i++){
innerHTML += this._genHTML(interval*i, i);
}
innerHTML += this._genHTML(100, this.count-1);
}else{
innerHTML = this._genHTML(100, 0);
for(i=1; i < this.count-1; i++){
innerHTML += this._genHTML(100-interval*i, i);
}
innerHTML += this._genHTML(0, this.count-1);
}
}
this.domNode.innerHTML = innerHTML;
}
});
});
},
'dijit/layout/TabContainer':function(){
define([
"dojo/_base/lang", // lang.getObject
"dojo/_base/declare", // declare
"./_TabContainerBase",
"./TabController",
"./ScrollingTabController"
], function(lang, declare, _TabContainerBase, TabController, ScrollingTabController){
/*=====
var _TabContainerBase = dijit.layout._TabContainerBase;
var TabController = dijit.layout.TabController;
var ScrollingTabController = dijit.layout.ScrollingTabController;
=====*/
// module:
// dijit/layout/TabContainer
// summary:
// A Container with tabs to select each child (only one of which is displayed at a time).
return declare("dijit.layout.TabContainer", _TabContainerBase, {
// summary:
// A Container with tabs to select each child (only one of which is displayed at a time).
// description:
// A TabContainer is a container that has multiple panes, but shows only
// one pane at a time. There are a set of tabs corresponding to each pane,
// where each tab has the name (aka title) of the pane, and optionally a close button.
// useMenu: [const] Boolean
// True if a menu should be used to select tabs when they are too
// wide to fit the TabContainer, false otherwise.
useMenu: true,
// useSlider: [const] Boolean
// True if a slider should be used to select tabs when they are too
// wide to fit the TabContainer, false otherwise.
useSlider: true,
// controllerWidget: String
// An optional parameter to override the widget used to display the tab labels
controllerWidget: "",
_makeController: function(/*DomNode*/ srcNode){
// summary:
// Instantiate tablist controller widget and return reference to it.
// Callback from _TabContainerBase.postCreate().
// tags:
// protected extension
var cls = this.baseClass + "-tabs" + (this.doLayout ? "" : " dijitTabNoLayout"),
TabController = lang.getObject(this.controllerWidget);
return new TabController({
id: this.id + "_tablist",
dir: this.dir,
lang: this.lang,
textDir: this.textDir,
tabPosition: this.tabPosition,
doLayout: this.doLayout,
containerId: this.id,
"class": cls,
nested: this.nested,
useMenu: this.useMenu,
useSlider: this.useSlider,
tabStripClass: this.tabStrip ? this.baseClass + (this.tabStrip ? "":"No") + "Strip": null
}, srcNode);
},
postMixInProperties: function(){
this.inherited(arguments);
// Scrolling controller only works for horizontal non-nested tabs
if(!this.controllerWidget){
this.controllerWidget = (this.tabPosition == "top" || this.tabPosition == "bottom") && !this.nested ?
"dijit.layout.ScrollingTabController" : "dijit.layout.TabController";
}
}
});
});
},
'url:dijit/templates/Menu.html':"<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" role=\"menu\" tabIndex=\"${tabIndex}\" data-dojo-attach-event=\"onkeypress:_onKeyPress\" cellspacing=\"0\">\n\t<tbody class=\"dijitReset\" data-dojo-attach-point=\"containerNode\"></tbody>\n</table>\n",
'dijit/_editor/nls/FontChoice':function(){
define("dijit/_editor/nls/FontChoice", { root:
//begin v1.x content
({
fontSize: "Size",
fontName: "Font",
formatBlock: "Format",
serif: "serif",
"sans-serif": "sans-serif",
monospace: "monospace",
cursive: "cursive",
fantasy: "fantasy",
noFormat: "None",
p: "Paragraph",
h1: "Heading",
h2: "Subheading",
h3: "Sub-subheading",
pre: "Pre-formatted",
1: "xx-small",
2: "x-small",
3: "small",
4: "medium",
5: "large",
6: "x-large",
7: "xx-large"
})
//end v1.x content
,
"zh": true,
"zh-tw": true,
"tr": true,
"th": true,
"sv": true,
"sl": true,
"sk": true,
"ru": true,
"ro": true,
"pt": true,
"pt-pt": true,
"pl": true,
"nl": true,
"nb": true,
"ko": true,
"kk": true,
"ja": true,
"it": true,
"hu": true,
"hr": true,
"he": true,
"fr": true,
"fi": true,
"es": true,
"el": true,
"de": true,
"da": true,
"cs": true,
"ca": true,
"az": true,
"ar": true
});
},
'dijit/form/_Spinner':function(){
define([
"dojo/_base/declare", // declare
"dojo/_base/event", // event.stop
"dojo/keys", // keys keys.DOWN_ARROW keys.PAGE_DOWN keys.PAGE_UP keys.UP_ARROW
"dojo/_base/lang", // lang.hitch
"dojo/_base/sniff", // has("mozilla")
"dijit/typematic",
"./RangeBoundTextBox",
"dojo/text!./templates/Spinner.html",
"./_TextBoxMixin" // selectInputText
], function(declare, event, keys, lang, has, typematic, RangeBoundTextBox, template, _TextBoxMixin){
/*=====
var RangeBoundTextBox = dijit.form.RangeBoundTextBox;
=====*/
// module:
// dijit/form/_Spinner
// summary:
// Mixin for validation widgets with a spinner.
return declare("dijit.form._Spinner", RangeBoundTextBox, {
// summary:
// Mixin for validation widgets with a spinner.
// description:
// This class basically (conceptually) extends `dijit.form.ValidationTextBox`.
// It modifies the template to have up/down arrows, and provides related handling code.
// defaultTimeout: Number
// Number of milliseconds before a held arrow key or up/down button becomes typematic
defaultTimeout: 500,
// minimumTimeout: Number
// minimum number of milliseconds that typematic event fires when held key or button is held
minimumTimeout: 10,
// timeoutChangeRate: Number
// Fraction of time used to change the typematic timer between events.
// 1.0 means that each typematic event fires at defaultTimeout intervals.
// < 1.0 means that each typematic event fires at an increasing faster rate.
timeoutChangeRate: 0.90,
// smallDelta: Number
// Adjust the value by this much when spinning using the arrow keys/buttons
smallDelta: 1,
// largeDelta: Number
// Adjust the value by this much when spinning using the PgUp/Dn keys
largeDelta: 10,
templateString: template,
baseClass: "dijitTextBox dijitSpinner",
// Set classes like dijitUpArrowButtonHover or dijitDownArrowButtonActive depending on
// mouse action over specified node
cssStateNodes: {
"upArrowNode": "dijitUpArrowButton",
"downArrowNode": "dijitDownArrowButton"
},
adjust: function(val /*=====, delta =====*/){
// summary:
// Overridable function used to adjust a primitive value(Number/Date/...) by the delta amount specified.
// The val is adjusted in a way that makes sense to the object type.
// val: Object
// delta: Number
// tags:
// protected extension
return val;
},
_arrowPressed: function(/*Node*/ nodePressed, /*Number*/ direction, /*Number*/ increment){
// summary:
// Handler for arrow button or arrow key being pressed
if(this.disabled || this.readOnly){ return; }
this._setValueAttr(this.adjust(this.get('value'), direction*increment), false);
_TextBoxMixin.selectInputText(this.textbox, this.textbox.value.length);
},
_arrowReleased: function(/*Node*/ /*===== node =====*/){
// summary:
// Handler for arrow button or arrow key being released
this._wheelTimer = null;
},
_typematicCallback: function(/*Number*/ count, /*DOMNode*/ node, /*Event*/ evt){
var inc=this.smallDelta;
if(node == this.textbox){
var key = evt.charOrCode;
inc = (key == keys.PAGE_UP || key == keys.PAGE_DOWN) ? this.largeDelta : this.smallDelta;
node = (key == keys.UP_ARROW || key == keys.PAGE_UP) ? this.upArrowNode : this.downArrowNode;
}
if(count == -1){ this._arrowReleased(node); }
else{ this._arrowPressed(node, (node == this.upArrowNode) ? 1 : -1, inc); }
},
_wheelTimer: null,
_mouseWheeled: function(/*Event*/ evt){
// summary:
// Mouse wheel listener where supported
event.stop(evt);
// FIXME: Safari bubbles
// be nice to DOH and scroll as much as the event says to
var wheelDelta = evt.wheelDelta / 120;
if(Math.floor(wheelDelta) != wheelDelta){
// If not an int multiple of 120, then its touchpad scrolling.
// This can change very fast so just assume 1 wheel click to make it more manageable.
wheelDelta = evt.wheelDelta > 0 ? 1 : -1;
}
var scrollAmount = evt.detail ? (evt.detail * -1) : wheelDelta;
if(scrollAmount !== 0){
var node = this[(scrollAmount > 0 ? "upArrowNode" : "downArrowNode" )];
this._arrowPressed(node, scrollAmount, this.smallDelta);
if(!this._wheelTimer){
clearTimeout(this._wheelTimer);
}
this._wheelTimer = setTimeout(lang.hitch(this,"_arrowReleased",node), 50);
}
},
postCreate: function(){
this.inherited(arguments);
// extra listeners
this.connect(this.domNode, !has("mozilla") ? "onmousewheel" : 'DOMMouseScroll', "_mouseWheeled");
this._connects.push(typematic.addListener(this.upArrowNode, this.textbox, {charOrCode:keys.UP_ARROW,ctrlKey:false,altKey:false,shiftKey:false,metaKey:false}, this, "_typematicCallback", this.timeoutChangeRate, this.defaultTimeout, this.minimumTimeout));
this._connects.push(typematic.addListener(this.downArrowNode, this.textbox, {charOrCode:keys.DOWN_ARROW,ctrlKey:false,altKey:false,shiftKey:false,metaKey:false}, this, "_typematicCallback", this.timeoutChangeRate, this.defaultTimeout, this.minimumTimeout));
this._connects.push(typematic.addListener(this.upArrowNode, this.textbox, {charOrCode:keys.PAGE_UP,ctrlKey:false,altKey:false,shiftKey:false,metaKey:false}, this, "_typematicCallback", this.timeoutChangeRate, this.defaultTimeout, this.minimumTimeout));
this._connects.push(typematic.addListener(this.downArrowNode, this.textbox, {charOrCode:keys.PAGE_DOWN,ctrlKey:false,altKey:false,shiftKey:false,metaKey:false}, this, "_typematicCallback", this.timeoutChangeRate, this.defaultTimeout, this.minimumTimeout));
}
});
});
},
'dijit/form/Button':function(){
define([
"require",
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.toggle
"dojo/_base/kernel", // kernel.deprecated
"dojo/_base/lang", // lang.trim
"dojo/ready",
"./_FormWidget",
"./_ButtonMixin",
"dojo/text!./templates/Button.html"
], function(require, declare, domClass, kernel, lang, ready, _FormWidget, _ButtonMixin, template){
/*=====
var _FormWidget = dijit.form._FormWidget;
var _ButtonMixin = dijit.form._ButtonMixin;
=====*/
// module:
// dijit/form/Button
// summary:
// Button widget
// Back compat w/1.6, remove for 2.0
if(!kernel.isAsync){
ready(0, function(){
var requires = ["dijit/form/DropDownButton", "dijit/form/ComboButton", "dijit/form/ToggleButton"];
require(requires); // use indirection so modules not rolled into a build
});
}
return declare("dijit.form.Button", [_FormWidget, _ButtonMixin], {
// summary:
// Basically the same thing as a normal HTML button, but with special styling.
// description:
// Buttons can display a label, an icon, or both.
// A label should always be specified (through innerHTML) or the label
// attribute. It can be hidden via showLabel=false.
// example:
// | <button data-dojo-type="dijit.form.Button" onClick="...">Hello world</button>
//
// example:
// | var button1 = new dijit.form.Button({label: "hello world", onClick: foo});
// | dojo.body().appendChild(button1.domNode);
// showLabel: Boolean
// Set this to true to hide the label text and display only the icon.
// (If showLabel=false then iconClass must be specified.)
// Especially useful for toolbars.
// If showLabel=true, the label will become the title (a.k.a. tooltip/hint) of the icon.
//
// The exception case is for computers in high-contrast mode, where the label
// will still be displayed, since the icon doesn't appear.
showLabel: true,
// iconClass: String
// Class to apply to DOMNode in button to make it display an icon
iconClass: "dijitNoIcon",
_setIconClassAttr: { node: "iconNode", type: "class" },
baseClass: "dijitButton",
templateString: template,
// Map widget attributes to DOMNode attributes.
_setValueAttr: "valueNode",
_onClick: function(/*Event*/ e){
// summary:
// Internal function to handle click actions
var ok = this.inherited(arguments);
if(ok){
if(this.valueNode){
this.valueNode.click();
e.preventDefault(); // cancel BUTTON click and continue with hidden INPUT click
// leave ok = true so that subclasses can do what they need to do
}
}
return ok;
},
_fillContent: function(/*DomNode*/ source){
// Overrides _Templated._fillContent().
// If button label is specified as srcNodeRef.innerHTML rather than
// this.params.label, handle it here.
// TODO: remove the method in 2.0, parser will do it all for me
if(source && (!this.params || !("label" in this.params))){
var sourceLabel = lang.trim(source.innerHTML);
if(sourceLabel){
this.label = sourceLabel; // _applyAttributes will be called after buildRendering completes to update the DOM
}
}
},
_setShowLabelAttr: function(val){
if(this.containerNode){
domClass.toggle(this.containerNode, "dijitDisplayNone", !val);
}
this._set("showLabel", val);
},
setLabel: function(/*String*/ content){
// summary:
// Deprecated. Use set('label', ...) instead.
kernel.deprecated("dijit.form.Button.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
this.set("label", content);
},
_setLabelAttr: function(/*String*/ content){
// summary:
// Hook for set('label', ...) to work.
// description:
// Set the label (text) of the button; takes an HTML string.
// If the label is hidden (showLabel=false) then and no title has
// been specified, then label is also set as title attribute of icon.
this.inherited(arguments);
if(!this.showLabel && !("title" in this.params)){
this.titleNode.title = lang.trim(this.containerNode.innerText || this.containerNode.textContent || '');
}
}
});
});
},
'url:dijit/layout/templates/TabContainer.html':"<div class=\"dijitTabContainer\">\n\t<div class=\"dijitTabListWrapper\" data-dojo-attach-point=\"tablistNode\"></div>\n\t<div data-dojo-attach-point=\"tablistSpacer\" class=\"dijitTabSpacer ${baseClass}-spacer\"></div>\n\t<div class=\"dijitTabPaneWrapper ${baseClass}-container\" data-dojo-attach-point=\"containerNode\"></div>\n</div>\n",
'dojo/dnd/move':function(){
define(["../main", "./Mover", "./Moveable"], function(dojo) {
// module:
// dojo/dnd/move
// summary:
// TODOC
/*=====
dojo.declare("dojo.dnd.move.__constrainedMoveableArgs", [dojo.dnd.__MoveableArgs], {
// constraints: Function
// Calculates a constraint box.
// It is called in a context of the moveable object.
constraints: function(){},
// within: Boolean
// restrict move within boundaries.
within: false
});
=====*/
dojo.declare("dojo.dnd.move.constrainedMoveable", dojo.dnd.Moveable, {
// object attributes (for markup)
constraints: function(){},
within: false,
constructor: function(node, params){
// summary:
// an object that makes a node moveable
// node: Node
// a node (or node's id) to be moved
// params: dojo.dnd.move.__constrainedMoveableArgs?
// an optional object with additional parameters;
// the rest is passed to the base class
if(!params){ params = {}; }
this.constraints = params.constraints;
this.within = params.within;
},
onFirstMove: function(/* dojo.dnd.Mover */ mover){
// summary:
// called during the very first move notification;
// can be used to initialize coordinates, can be overwritten.
var c = this.constraintBox = this.constraints.call(this, mover);
c.r = c.l + c.w;
c.b = c.t + c.h;
if(this.within){
var mb = dojo._getMarginSize(mover.node);
c.r -= mb.w;
c.b -= mb.h;
}
},
onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
// summary:
// called during every move notification;
// should actually move the node; can be overwritten.
var c = this.constraintBox, s = mover.node.style;
this.onMoving(mover, leftTop);
leftTop.l = leftTop.l < c.l ? c.l : c.r < leftTop.l ? c.r : leftTop.l;
leftTop.t = leftTop.t < c.t ? c.t : c.b < leftTop.t ? c.b : leftTop.t;
s.left = leftTop.l + "px";
s.top = leftTop.t + "px";
this.onMoved(mover, leftTop);
}
});
/*=====
dojo.declare("dojo.dnd.move.__boxConstrainedMoveableArgs", [dojo.dnd.move.__constrainedMoveableArgs], {
// box: Object
// a constraint box
box: {}
});
=====*/
dojo.declare("dojo.dnd.move.boxConstrainedMoveable", dojo.dnd.move.constrainedMoveable, {
// box:
// object attributes (for markup)
box: {},
constructor: function(node, params){
// summary:
// an object, which makes a node moveable
// node: Node
// a node (or node's id) to be moved
// params: dojo.dnd.move.__boxConstrainedMoveableArgs?
// an optional object with parameters
var box = params && params.box;
this.constraints = function(){ return box; };
}
});
/*=====
dojo.declare("dojo.dnd.move.__parentConstrainedMoveableArgs", [dojo.dnd.move.__constrainedMoveableArgs], {
// area: String
// A parent's area to restrict the move.
// Can be "margin", "border", "padding", or "content".
area: ""
});
=====*/
dojo.declare("dojo.dnd.move.parentConstrainedMoveable", dojo.dnd.move.constrainedMoveable, {
// area:
// object attributes (for markup)
area: "content",
constructor: function(node, params){
// summary:
// an object, which makes a node moveable
// node: Node
// a node (or node's id) to be moved
// params: dojo.dnd.move.__parentConstrainedMoveableArgs?
// an optional object with parameters
var area = params && params.area;
this.constraints = function(){
var n = this.node.parentNode,
s = dojo.getComputedStyle(n),
mb = dojo._getMarginBox(n, s);
if(area == "margin"){
return mb; // Object
}
var t = dojo._getMarginExtents(n, s);
mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
if(area == "border"){
return mb; // Object
}
t = dojo._getBorderExtents(n, s);
mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
if(area == "padding"){
return mb; // Object
}
t = dojo._getPadExtents(n, s);
mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
return mb; // Object
};
}
});
// patching functions one level up for compatibility
dojo.dnd.constrainedMover = dojo.dnd.move.constrainedMover;
dojo.dnd.boxConstrainedMover = dojo.dnd.move.boxConstrainedMover;
dojo.dnd.parentConstrainedMover = dojo.dnd.move.parentConstrainedMover;
return dojo.dnd.move;
});
},
'dijit/form/Form':function(){
define([
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/_base/event", // event.stop
"dojo/_base/kernel", // kernel.deprecated
"dojo/_base/sniff", // has("ie")
"../_Widget",
"../_TemplatedMixin",
"./_FormMixin",
"../layout/_ContentPaneResizeMixin"
], function(declare, domAttr, event, kernel, has, _Widget, _TemplatedMixin, _FormMixin, _ContentPaneResizeMixin){
/*=====
var _Widget = dijit._Widget;
var _TemplatedMixin = dijit._TemplatedMixin;
var _FormMixin = dijit.form._FormMixin;
var _ContentPaneResizeMixin = dijit.layout._ContentPaneResizeMixin;
=====*/
// module:
// dijit/form/Form
// summary:
// Widget corresponding to HTML form tag, for validation and serialization
return declare("dijit.form.Form", [_Widget, _TemplatedMixin, _FormMixin, _ContentPaneResizeMixin], {
// summary:
// Widget corresponding to HTML form tag, for validation and serialization
//
// example:
// | <form data-dojo-type="dijit.form.Form" id="myForm">
// | Name: <input type="text" name="name" />
// | </form>
// | myObj = {name: "John Doe"};
// | dijit.byId('myForm').set('value', myObj);
// |
// | myObj=dijit.byId('myForm').get('value');
// HTML <FORM> attributes
// name: String?
// Name of form for scripting.
name: "",
// action: String?
// Server-side form handler.
action: "",
// method: String?
// HTTP method used to submit the form, either "GET" or "POST".
method: "",
// encType: String?
// Encoding type for the form, ex: application/x-www-form-urlencoded.
encType: "",
// accept-charset: String?
// List of supported charsets.
"accept-charset": "",
// accept: String?
// List of MIME types for file upload.
accept: "",
// target: String?
// Target frame for the document to be opened in.
target: "",
templateString: "<form data-dojo-attach-point='containerNode' data-dojo-attach-event='onreset:_onReset,onsubmit:_onSubmit' ${!nameAttrSetting}></form>",
postMixInProperties: function(){
// Setup name=foo string to be referenced from the template (but only if a name has been specified)
// Unfortunately we can't use _setNameAttr to set the name due to IE limitations, see #8660
this.nameAttrSetting = this.name ? ("name='" + this.name + "'") : "";
this.inherited(arguments);
},
execute: function(/*Object*/ /*===== formContents =====*/){
// summary:
// Deprecated: use submit()
// tags:
// deprecated
},
onExecute: function(){
// summary:
// Deprecated: use onSubmit()
// tags:
// deprecated
},
_setEncTypeAttr: function(/*String*/ value){
this.encType = value;
domAttr.set(this.domNode, "encType", value);
if(has("ie")){ this.domNode.encoding = value; }
},
reset: function(/*Event?*/ e){
// summary:
// restores all widget values back to their init values,
// calls onReset() which can cancel the reset by returning false
// create fake event so we can know if preventDefault() is called
var faux = {
returnValue: true, // the IE way
preventDefault: function(){ // not IE
this.returnValue = false;
},
stopPropagation: function(){},
currentTarget: e ? e.target : this.domNode,
target: e ? e.target : this.domNode
};
// if return value is not exactly false, and haven't called preventDefault(), then reset
if(!(this.onReset(faux) === false) && faux.returnValue){
this.inherited(arguments, []);
}
},
onReset: function(/*Event?*/ /*===== e =====*/){
// summary:
// Callback when user resets the form. This method is intended
// to be over-ridden. When the `reset` method is called
// programmatically, the return value from `onReset` is used
// to compute whether or not resetting should proceed
// tags:
// callback
return true; // Boolean
},
_onReset: function(e){
this.reset(e);
event.stop(e);
return false;
},
_onSubmit: function(e){
var fp = this.constructor.prototype;
// TODO: remove this if statement beginning with 2.0
if(this.execute != fp.execute || this.onExecute != fp.onExecute){
kernel.deprecated("dijit.form.Form:execute()/onExecute() are deprecated. Use onSubmit() instead.", "", "2.0");
this.onExecute();
this.execute(this.getValues());
}
if(this.onSubmit(e) === false){ // only exactly false stops submit
event.stop(e);
}
},
onSubmit: function(/*Event?*/ /*===== e =====*/){
// summary:
// Callback when user submits the form.
// description:
// This method is intended to be over-ridden, but by default it checks and
// returns the validity of form elements. When the `submit`
// method is called programmatically, the return value from
// `onSubmit` is used to compute whether or not submission
// should proceed
// tags:
// extension
return this.isValid(); // Boolean
},
submit: function(){
// summary:
// programmatically submit form if and only if the `onSubmit` returns true
if(!(this.onSubmit() === false)){
this.containerNode.submit();
}
}
});
});
},
'dijit/layout/_TabContainerBase':function(){
define([
"dojo/text!./templates/TabContainer.html",
"./StackContainer",
"./utils", // marginBox2contextBox, layoutChildren
"../_TemplatedMixin",
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.add
"dojo/dom-geometry", // domGeometry.contentBox
"dojo/dom-style" // domStyle.style
], function(template, StackContainer, layoutUtils, _TemplatedMixin, declare, domClass, domGeometry, domStyle){
/*=====
var StackContainer = dijit.layout.StackContainer;
var _TemplatedMixin = dijit._TemplatedMixin;
=====*/
// module:
// dijit/layout/_TabContainerBase
// summary:
// Abstract base class for TabContainer. Must define _makeController() to instantiate
// and return the widget that displays the tab labels
return declare("dijit.layout._TabContainerBase", [StackContainer, _TemplatedMixin], {
// summary:
// Abstract base class for TabContainer. Must define _makeController() to instantiate
// and return the widget that displays the tab labels
// description:
// A TabContainer is a container that has multiple panes, but shows only
// one pane at a time. There are a set of tabs corresponding to each pane,
// where each tab has the name (aka title) of the pane, and optionally a close button.
// tabPosition: String
// Defines where tabs go relative to tab content.
// "top", "bottom", "left-h", "right-h"
tabPosition: "top",
baseClass: "dijitTabContainer",
// tabStrip: [const] Boolean
// Defines whether the tablist gets an extra class for layouting, putting a border/shading
// around the set of tabs. Not supported by claro theme.
tabStrip: false,
// nested: [const] Boolean
// If true, use styling for a TabContainer nested inside another TabContainer.
// For tundra etc., makes tabs look like links, and hides the outer
// border since the outer TabContainer already has a border.
nested: false,
templateString: template,
postMixInProperties: function(){
// set class name according to tab position, ex: dijitTabContainerTop
this.baseClass += this.tabPosition.charAt(0).toUpperCase() + this.tabPosition.substr(1).replace(/-.*/, "");
this.srcNodeRef && domStyle.set(this.srcNodeRef, "visibility", "hidden");
this.inherited(arguments);
},
buildRendering: function(){
this.inherited(arguments);
// Create the tab list that will have a tab (a.k.a. tab button) for each tab panel
this.tablist = this._makeController(this.tablistNode);
if(!this.doLayout){ domClass.add(this.domNode, "dijitTabContainerNoLayout"); }
if(this.nested){
/* workaround IE's lack of support for "a > b" selectors by
* tagging each node in the template.
*/
domClass.add(this.domNode, "dijitTabContainerNested");
domClass.add(this.tablist.containerNode, "dijitTabContainerTabListNested");
domClass.add(this.tablistSpacer, "dijitTabContainerSpacerNested");
domClass.add(this.containerNode, "dijitTabPaneWrapperNested");
}else{
domClass.add(this.domNode, "tabStrip-" + (this.tabStrip ? "enabled" : "disabled"));
}
},
_setupChild: function(/*dijit._Widget*/ tab){
// Overrides StackContainer._setupChild().
domClass.add(tab.domNode, "dijitTabPane");
this.inherited(arguments);
},
startup: function(){
if(this._started){ return; }
// wire up the tablist and its tabs
this.tablist.startup();
this.inherited(arguments);
},
layout: function(){
// Overrides StackContainer.layout().
// Configure the content pane to take up all the space except for where the tabs are
if(!this._contentBox || typeof(this._contentBox.l) == "undefined"){return;}
var sc = this.selectedChildWidget;
if(this.doLayout){
// position and size the titles and the container node
var titleAlign = this.tabPosition.replace(/-h/, "");
this.tablist.layoutAlign = titleAlign;
var children = [this.tablist, {
domNode: this.tablistSpacer,
layoutAlign: titleAlign
}, {
domNode: this.containerNode,
layoutAlign: "client"
}];
layoutUtils.layoutChildren(this.domNode, this._contentBox, children);
// Compute size to make each of my children.
// children[2] is the margin-box size of this.containerNode, set by layoutChildren() call above
this._containerContentBox = layoutUtils.marginBox2contentBox(this.containerNode, children[2]);
if(sc && sc.resize){
sc.resize(this._containerContentBox);
}
}else{
// just layout the tab controller, so it can position left/right buttons etc.
if(this.tablist.resize){
//make the tabs zero width so that they don't interfere with width calc, then reset
var s = this.tablist.domNode.style;
s.width="0";
var width = domGeometry.getContentBox(this.domNode).w;
s.width="";
this.tablist.resize({w: width});
}
// and call resize() on the selected pane just to tell it that it's been made visible
if(sc && sc.resize){
sc.resize();
}
}
},
destroy: function(){
if(this.tablist){
this.tablist.destroy();
}
this.inherited(arguments);
}
});
});
},
'dojo/store/Memory':function(){
define(["../_base/declare", "./util/QueryResults", "./util/SimpleQueryEngine"], function(declare, QueryResults, SimpleQueryEngine) {
// module:
// dojo/store/Memory
// summary:
// The module defines an in-memory object store.
return declare("dojo.store.Memory", null, {
// summary:
// This is a basic in-memory object store. It implements dojo.store.api.Store.
constructor: function(/*dojo.store.Memory*/ options){
// summary:
// Creates a memory object store.
// options:
// This provides any configuration information that will be mixed into the store.
// This should generally include the data property to provide the starting set of data.
for(var i in options){
this[i] = options[i];
}
this.setData(this.data || []);
},
// data: Array
// The array of all the objects in the memory store
data:null,
// idProperty: String
// Indicates the property to use as the identity property. The values of this
// property should be unique.
idProperty: "id",
// index: Object
// An index of data indices into the data array by id
index:null,
// queryEngine: Function
// Defines the query engine to use for querying the data store
queryEngine: SimpleQueryEngine,
get: function(id){
// summary:
// Retrieves an object by its identity
// id: Number
// The identity to use to lookup the object
// returns: Object
// The object in the store that matches the given id.
return this.data[this.index[id]];
},
getIdentity: function(object){
// summary:
// Returns an object's identity
// object: Object
// The object to get the identity from
// returns: Number
return object[this.idProperty];
},
put: function(object, options){
// summary:
// Stores an object
// object: Object
// The object to store.
// options: dojo.store.api.Store.PutDirectives??
// Additional metadata for storing the data. Includes an "id"
// property if a specific id is to be used.
// returns: Number
var data = this.data,
index = this.index,
idProperty = this.idProperty;
var id = (options && "id" in options) ? options.id : idProperty in object ? object[idProperty] : Math.random();
if(id in index){
// object exists
if(options && options.overwrite === false){
throw new Error("Object already exists");
}
// replace the entry in data
data[index[id]] = object;
}else{
// add the new object
index[id] = data.push(object) - 1;
}
return id;
},
add: function(object, options){
// summary:
// Creates an object, throws an error if the object already exists
// object: Object
// The object to store.
// options: dojo.store.api.Store.PutDirectives??
// Additional metadata for storing the data. Includes an "id"
// property if a specific id is to be used.
// returns: Number
(options = options || {}).overwrite = false;
// call put with overwrite being false
return this.put(object, options);
},
remove: function(id){
// summary:
// Deletes an object by its identity
// id: Number
// The identity to use to delete the object
// returns: Boolean
// Returns true if an object was removed, falsy (undefined) if no object matched the id
var index = this.index;
var data = this.data;
if(id in index){
data.splice(index[id], 1);
// now we have to reindex
this.setData(data);
return true;
}
},
query: function(query, options){
// summary:
// Queries the store for objects.
// query: Object
// The query to use for retrieving objects from the store.
// options: dojo.store.api.Store.QueryOptions?
// The optional arguments to apply to the resultset.
// returns: dojo.store.api.Store.QueryResults
// The results of the query, extended with iterative methods.
//
// example:
// Given the following store:
//
// | var store = new dojo.store.Memory({
// | data: [
// | {id: 1, name: "one", prime: false },
// | {id: 2, name: "two", even: true, prime: true},
// | {id: 3, name: "three", prime: true},
// | {id: 4, name: "four", even: true, prime: false},
// | {id: 5, name: "five", prime: true}
// | ]
// | });
//
// ...find all items where "prime" is true:
//
// | var results = store.query({ prime: true });
//
// ...or find all items where "even" is true:
//
// | var results = store.query({ even: true });
return QueryResults(this.queryEngine(query, options)(this.data));
},
setData: function(data){
// summary:
// Sets the given data as the source for this store, and indexes it
// data: Object[]
// An array of objects to use as the source of data.
if(data.items){
// just for convenience with the data format IFRS expects
this.idProperty = data.identifier;
data = this.data = data.items;
}else{
this.data = data;
}
this.index = {};
for(var i = 0, l = data.length; i < l; i++){
this.index[data[i][this.idProperty]] = i;
}
}
});
});
},
'url:dijit/templates/Tooltip.html':"<div class=\"dijitTooltip dijitTooltipLeft\" id=\"dojoTooltip\"\n\t><div class=\"dijitTooltipContainer dijitTooltipContents\" data-dojo-attach-point=\"containerNode\" role='alert'></div\n\t><div class=\"dijitTooltipConnector\" data-dojo-attach-point=\"connectorNode\"></div\n></div>\n",
'dijit/Editor':function(){
define([
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/_base/Deferred", // Deferred
"dojo/i18n", // i18n.getLocalization
"dojo/dom-attr", // domAttr.set
"dojo/dom-class", // domClass.add
"dojo/dom-geometry",
"dojo/dom-style", // domStyle.set, get
"dojo/_base/event", // event.stop
"dojo/keys", // keys.F1 keys.F15 keys.TAB
"dojo/_base/lang", // lang.getObject lang.hitch
"dojo/_base/sniff", // has("ie") has("mac") has("webkit")
"dojo/string", // string.substitute
"dojo/topic", // topic.publish()
"dojo/_base/window", // win.withGlobal
"./_base/focus", // dijit.getBookmark()
"./_Container",
"./Toolbar",
"./ToolbarSeparator",
"./layout/_LayoutWidget",
"./form/ToggleButton",
"./_editor/_Plugin",
"./_editor/plugins/EnterKeyHandling",
"./_editor/html",
"./_editor/range",
"./_editor/RichText",
".", // dijit._scopeName
"dojo/i18n!./_editor/nls/commands"
], function(array, declare, Deferred, i18n, domAttr, domClass, domGeometry, domStyle,
event, keys, lang, has, string, topic, win,
focusBase, _Container, Toolbar, ToolbarSeparator, _LayoutWidget, ToggleButton,
_Plugin, EnterKeyHandling, html, rangeapi, RichText, dijit){
// module:
// dijit/Editor
// summary:
// A rich text Editing widget
var Editor = declare("dijit.Editor", RichText, {
// summary:
// A rich text Editing widget
//
// description:
// This widget provides basic WYSIWYG editing features, based on the browser's
// underlying rich text editing capability, accompanied by a toolbar (`dijit.Toolbar`).
// A plugin model is available to extend the editor's capabilities as well as the
// the options available in the toolbar. Content generation may vary across
// browsers, and clipboard operations may have different results, to name
// a few limitations. Note: this widget should not be used with the HTML
// &lt;TEXTAREA&gt; tag -- see dijit._editor.RichText for details.
// plugins: [const] Object[]
// A list of plugin names (as strings) or instances (as objects)
// for this widget.
//
// When declared in markup, it might look like:
// | plugins="['bold',{name:'dijit._editor.plugins.FontChoice', command:'fontName', generic:true}]"
plugins: null,
// extraPlugins: [const] Object[]
// A list of extra plugin names which will be appended to plugins array
extraPlugins: null,
constructor: function(){
// summary:
// Runs on widget initialization to setup arrays etc.
// tags:
// private
if(!lang.isArray(this.plugins)){
this.plugins=["undo","redo","|","cut","copy","paste","|","bold","italic","underline","strikethrough","|",
"insertOrderedList","insertUnorderedList","indent","outdent","|","justifyLeft","justifyRight","justifyCenter","justifyFull",
EnterKeyHandling /*, "createLink"*/];
}
this._plugins=[];
this._editInterval = this.editActionInterval * 1000;
//IE will always lose focus when other element gets focus, while for FF and safari,
//when no iframe is used, focus will be lost whenever another element gets focus.
//For IE, we can connect to onBeforeDeactivate, which will be called right before
//the focus is lost, so we can obtain the selected range. For other browsers,
//no equivalent of onBeforeDeactivate, so we need to do two things to make sure
//selection is properly saved before focus is lost: 1) when user clicks another
//element in the page, in which case we listen to mousedown on the entire page and
//see whether user clicks out of a focus editor, if so, save selection (focus will
//only lost after onmousedown event is fired, so we can obtain correct caret pos.)
//2) when user tabs away from the editor, which is handled in onKeyDown below.
if(has("ie")){
this.events.push("onBeforeDeactivate");
this.events.push("onBeforeActivate");
}
},
postMixInProperties: function(){
// summary:
// Extension to make sure a deferred is in place before certain functions
// execute, like making sure all the plugins are properly inserted.
// Set up a deferred so that the value isn't applied to the editor
// until all the plugins load, needed to avoid timing condition
// reported in #10537.
this.setValueDeferred = new Deferred();
this.inherited(arguments);
},
postCreate: function(){
//for custom undo/redo, if enabled.
this._steps=this._steps.slice(0);
this._undoedSteps=this._undoedSteps.slice(0);
if(lang.isArray(this.extraPlugins)){
this.plugins=this.plugins.concat(this.extraPlugins);
}
this.inherited(arguments);
this.commands = i18n.getLocalization("dijit._editor", "commands", this.lang);
if(!this.toolbar){
// if we haven't been assigned a toolbar, create one
this.toolbar = new Toolbar({
dir: this.dir,
lang: this.lang
});
this.header.appendChild(this.toolbar.domNode);
}
array.forEach(this.plugins, this.addPlugin, this);
// Okay, denote the value can now be set.
this.setValueDeferred.callback(true);
domClass.add(this.iframe.parentNode, "dijitEditorIFrameContainer");
domClass.add(this.iframe, "dijitEditorIFrame");
domAttr.set(this.iframe, "allowTransparency", true);
if(has("webkit")){
// Disable selecting the entire editor by inadvertent double-clicks.
// on buttons, title bar, etc. Otherwise clicking too fast on
// a button such as undo/redo selects the entire editor.
domStyle.set(this.domNode, "KhtmlUserSelect", "none");
}
this.toolbar.startup();
this.onNormalizedDisplayChanged(); //update toolbar button status
},
destroy: function(){
array.forEach(this._plugins, function(p){
if(p && p.destroy){
p.destroy();
}
});
this._plugins=[];
this.toolbar.destroyRecursive();
delete this.toolbar;
this.inherited(arguments);
},
addPlugin: function(/*String||Object||Function*/plugin, /*Integer?*/index){
// summary:
// takes a plugin name as a string or a plugin instance and
// adds it to the toolbar and associates it with this editor
// instance. The resulting plugin is added to the Editor's
// plugins array. If index is passed, it's placed in the plugins
// array at that index. No big magic, but a nice helper for
// passing in plugin names via markup.
//
// plugin: String, args object, plugin instance, or plugin constructor
//
// args:
// This object will be passed to the plugin constructor
//
// index: Integer
// Used when creating an instance from
// something already in this.plugins. Ensures that the new
// instance is assigned to this.plugins at that index.
var args=lang.isString(plugin)?{name:plugin}:lang.isFunction(plugin)?{ctor:plugin}:plugin;
if(!args.setEditor){
var o={"args":args,"plugin":null,"editor":this};
if(args.name){
// search registry for a plugin factory matching args.name, if it's not there then
// fallback to 1.0 API:
// ask all loaded plugin modules to fill in o.plugin if they can (ie, if they implement args.name)
// remove fallback for 2.0.
if(_Plugin.registry[args.name]){
o.plugin = _Plugin.registry[args.name](args);
}else{
topic.publish(dijit._scopeName + ".Editor.getPlugin", o); // publish
}
}
if(!o.plugin){
var pc = args.ctor || lang.getObject(args.name);
if(pc){
o.plugin=new pc(args);
}
}
if(!o.plugin){
console.warn('Cannot find plugin',plugin);
return;
}
plugin=o.plugin;
}
if(arguments.length > 1){
this._plugins[index] = plugin;
}else{
this._plugins.push(plugin);
}
plugin.setEditor(this);
if(lang.isFunction(plugin.setToolbar)){
plugin.setToolbar(this.toolbar);
}
},
//the following 2 functions are required to make the editor play nice under a layout widget, see #4070
resize: function(size){
// summary:
// Resize the editor to the specified size, see `dijit.layout._LayoutWidget.resize`
if(size){
// we've been given a height/width for the entire editor (toolbar + contents), calls layout()
// to split the allocated size between the toolbar and the contents
_LayoutWidget.prototype.resize.apply(this, arguments);
}
/*
else{
// do nothing, the editor is already laid out correctly. The user has probably specified
// the height parameter, which was used to set a size on the iframe
}
*/
},
layout: function(){
// summary:
// Called from `dijit.layout._LayoutWidget.resize`. This shouldn't be called directly
// tags:
// protected
// Converts the iframe (or rather the <div> surrounding it) to take all the available space
// except what's needed for the header (toolbars) and footer (breadcrumbs, etc).
// A class was added to the iframe container and some themes style it, so we have to
// calc off the added margins and padding too. See tracker: #10662
var areaHeight = (this._contentBox.h -
(this.getHeaderHeight() + this.getFooterHeight() +
domGeometry.getPadBorderExtents(this.iframe.parentNode).h +
domGeometry.getMarginExtents(this.iframe.parentNode).h));
this.editingArea.style.height = areaHeight + "px";
if(this.iframe){
this.iframe.style.height="100%";
}
this._layoutMode = true;
},
_onIEMouseDown: function(/*Event*/ e){
// summary:
// IE only to prevent 2 clicks to focus
// tags:
// private
var outsideClientArea;
// IE 8's componentFromPoint is broken, which is a shame since it
// was smaller code, but oh well. We have to do this brute force
// to detect if the click was scroller or not.
var b = this.document.body;
var clientWidth = b.clientWidth;
var clientHeight = b.clientHeight;
var clientLeft = b.clientLeft;
var offsetWidth = b.offsetWidth;
var offsetHeight = b.offsetHeight;
var offsetLeft = b.offsetLeft;
//Check for vertical scroller click.
if(/^rtl$/i.test(b.dir || "")){
if(clientWidth < offsetWidth && e.x > clientWidth && e.x < offsetWidth){
// Check the click was between width and offset width, if so, scroller
outsideClientArea = true;
}
}else{
// RTL mode, we have to go by the left offsets.
if(e.x < clientLeft && e.x > offsetLeft){
// Check the click was between width and offset width, if so, scroller
outsideClientArea = true;
}
}
if(!outsideClientArea){
// Okay, might be horiz scroller, check that.
if(clientHeight < offsetHeight && e.y > clientHeight && e.y < offsetHeight){
// Horizontal scroller.
outsideClientArea = true;
}
}
if(!outsideClientArea){
delete this._cursorToStart; // Remove the force to cursor to start position.
delete this._savedSelection; // new mouse position overrides old selection
if(e.target.tagName == "BODY"){
setTimeout(lang.hitch(this, "placeCursorAtEnd"), 0);
}
this.inherited(arguments);
}
},
onBeforeActivate: function(){
this._restoreSelection();
},
onBeforeDeactivate: function(e){
// summary:
// Called on IE right before focus is lost. Saves the selected range.
// tags:
// private
if(this.customUndo){
this.endEditing(true);
}
//in IE, the selection will be lost when other elements get focus,
//let's save focus before the editor is deactivated
if(e.target.tagName != "BODY"){
this._saveSelection();
}
//console.log('onBeforeDeactivate',this);
},
/* beginning of custom undo/redo support */
// customUndo: Boolean
// Whether we shall use custom undo/redo support instead of the native
// browser support. By default, we now use custom undo. It works better
// than native browser support and provides a consistent behavior across
// browsers with a minimal performance hit. We already had the hit on
// the slowest browser, IE, anyway.
customUndo: true,
// editActionInterval: Integer
// When using customUndo, not every keystroke will be saved as a step.
// Instead typing (including delete) will be grouped together: after
// a user stops typing for editActionInterval seconds, a step will be
// saved; if a user resume typing within editActionInterval seconds,
// the timeout will be restarted. By default, editActionInterval is 3
// seconds.
editActionInterval: 3,
beginEditing: function(cmd){
// summary:
// Called to note that the user has started typing alphanumeric characters, if it's not already noted.
// Deals with saving undo; see editActionInterval parameter.
// tags:
// private
if(!this._inEditing){
this._inEditing=true;
this._beginEditing(cmd);
}
if(this.editActionInterval>0){
if(this._editTimer){
clearTimeout(this._editTimer);
}
this._editTimer = setTimeout(lang.hitch(this, this.endEditing), this._editInterval);
}
},
// TODO: declaring these in the prototype is meaningless, just create in the constructor/postCreate
_steps:[],
_undoedSteps:[],
execCommand: function(cmd){
// summary:
// Main handler for executing any commands to the editor, like paste, bold, etc.
// Called by plugins, but not meant to be called by end users.
// tags:
// protected
if(this.customUndo && (cmd == 'undo' || cmd == 'redo')){
return this[cmd]();
}else{
if(this.customUndo){
this.endEditing();
this._beginEditing();
}
var r = this.inherited(arguments);
if(this.customUndo){
this._endEditing();
}
return r;
}
},
_pasteImpl: function(){
// summary:
// Over-ride of paste command control to make execCommand cleaner
// tags:
// Protected
return this._clipboardCommand("paste");
},
_cutImpl: function(){
// summary:
// Over-ride of cut command control to make execCommand cleaner
// tags:
// Protected
return this._clipboardCommand("cut");
},
_copyImpl: function(){
// summary:
// Over-ride of copy command control to make execCommand cleaner
// tags:
// Protected
return this._clipboardCommand("copy");
},
_clipboardCommand: function(cmd){
// summary:
// Function to handle processing clipboard commands (or at least try to).
// tags:
// Private
var r;
try{
// Try to exec the superclass exec-command and see if it works.
r = this.document.execCommand(cmd, false, null);
if(has("webkit") && !r){ //see #4598: webkit does not guarantee clipboard support from js
throw { code: 1011 }; // throw an object like Mozilla's error
}
}catch(e){
//TODO: when else might we get an exception? Do we need the Mozilla test below?
if(e.code == 1011 /* Mozilla: service denied */){
// Warn user of platform limitation. Cannot programmatically access clipboard. See ticket #4136
var sub = string.substitute,
accel = {cut:'X', copy:'C', paste:'V'};
alert(sub(this.commands.systemShortcut,
[this.commands[cmd], sub(this.commands[has("mac") ? 'appleKey' : 'ctrlKey'], [accel[cmd]])]));
}
r = false;
}
return r;
},
queryCommandEnabled: function(cmd){
// summary:
// Returns true if specified editor command is enabled.
// Used by the plugins to know when to highlight/not highlight buttons.
// tags:
// protected
if(this.customUndo && (cmd == 'undo' || cmd == 'redo')){
return cmd == 'undo' ? (this._steps.length > 1) : (this._undoedSteps.length > 0);
}else{
return this.inherited(arguments);
}
},
_moveToBookmark: function(b){
// summary:
// Selects the text specified in bookmark b
// tags:
// private
var bookmark = b.mark;
var mark = b.mark;
var col = b.isCollapsed;
var r, sNode, eNode, sel;
if(mark){
if(has("ie") < 9){
if(lang.isArray(mark)){
//IE CONTROL, have to use the native bookmark.
bookmark = [];
array.forEach(mark,function(n){
bookmark.push(rangeapi.getNode(n,this.editNode));
},this);
win.withGlobal(this.window,'moveToBookmark',dijit,[{mark: bookmark, isCollapsed: col}]);
}else{
if(mark.startContainer && mark.endContainer){
// Use the pseudo WC3 range API. This works better for positions
// than the IE native bookmark code.
sel = rangeapi.getSelection(this.window);
if(sel && sel.removeAllRanges){
sel.removeAllRanges();
r = rangeapi.create(this.window);
sNode = rangeapi.getNode(mark.startContainer,this.editNode);
eNode = rangeapi.getNode(mark.endContainer,this.editNode);
if(sNode && eNode){
// Okay, we believe we found the position, so add it into the selection
// There are cases where it may not be found, particularly in undo/redo, when
// IE changes the underlying DOM on us (wraps text in a <p> tag or similar.
// So, in those cases, don't bother restoring selection.
r.setStart(sNode,mark.startOffset);
r.setEnd(eNode,mark.endOffset);
sel.addRange(r);
}
}
}
}
}else{//w3c range
sel = rangeapi.getSelection(this.window);
if(sel && sel.removeAllRanges){
sel.removeAllRanges();
r = rangeapi.create(this.window);
sNode = rangeapi.getNode(mark.startContainer,this.editNode);
eNode = rangeapi.getNode(mark.endContainer,this.editNode);
if(sNode && eNode){
// Okay, we believe we found the position, so add it into the selection
// There are cases where it may not be found, particularly in undo/redo, when
// formatting as been done and so on, so don't restore selection then.
r.setStart(sNode,mark.startOffset);
r.setEnd(eNode,mark.endOffset);
sel.addRange(r);
}
}
}
}
},
_changeToStep: function(from, to){
// summary:
// Reverts editor to "to" setting, from the undo stack.
// tags:
// private
this.setValue(to.text);
var b=to.bookmark;
if(!b){ return; }
this._moveToBookmark(b);
},
undo: function(){
// summary:
// Handler for editor undo (ex: ctrl-z) operation
// tags:
// private
//console.log('undo');
var ret = false;
if(!this._undoRedoActive){
this._undoRedoActive = true;
this.endEditing(true);
var s=this._steps.pop();
if(s && this._steps.length>0){
this.focus();
this._changeToStep(s,this._steps[this._steps.length-1]);
this._undoedSteps.push(s);
this.onDisplayChanged();
delete this._undoRedoActive;
ret = true;
}
delete this._undoRedoActive;
}
return ret;
},
redo: function(){
// summary:
// Handler for editor redo (ex: ctrl-y) operation
// tags:
// private
//console.log('redo');
var ret = false;
if(!this._undoRedoActive){
this._undoRedoActive = true;
this.endEditing(true);
var s=this._undoedSteps.pop();
if(s && this._steps.length>0){
this.focus();
this._changeToStep(this._steps[this._steps.length-1],s);
this._steps.push(s);
this.onDisplayChanged();
ret = true;
}
delete this._undoRedoActive;
}
return ret;
},
endEditing: function(ignore_caret){
// summary:
// Called to note that the user has stopped typing alphanumeric characters, if it's not already noted.
// Deals with saving undo; see editActionInterval parameter.
// tags:
// private
if(this._editTimer){
clearTimeout(this._editTimer);
}
if(this._inEditing){
this._endEditing(ignore_caret);
this._inEditing=false;
}
},
_getBookmark: function(){
// summary:
// Get the currently selected text
// tags:
// protected
var b=win.withGlobal(this.window,focusBase.getBookmark);
var tmp=[];
if(b && b.mark){
var mark = b.mark;
if(has("ie") < 9){
// Try to use the pseudo range API on IE for better accuracy.
var sel = rangeapi.getSelection(this.window);
if(!lang.isArray(mark)){
if(sel){
var range;
if(sel.rangeCount){
range = sel.getRangeAt(0);
}
if(range){
b.mark = range.cloneRange();
}else{
b.mark = win.withGlobal(this.window,focusBase.getBookmark);
}
}
}else{
// Control ranges (img, table, etc), handle differently.
array.forEach(b.mark,function(n){
tmp.push(rangeapi.getIndex(n,this.editNode).o);
},this);
b.mark = tmp;
}
}
try{
if(b.mark && b.mark.startContainer){
tmp=rangeapi.getIndex(b.mark.startContainer,this.editNode).o;
b.mark={startContainer:tmp,
startOffset:b.mark.startOffset,
endContainer:b.mark.endContainer===b.mark.startContainer?tmp:rangeapi.getIndex(b.mark.endContainer,this.editNode).o,
endOffset:b.mark.endOffset};
}
}catch(e){
b.mark = null;
}
}
return b;
},
_beginEditing: function(){
// summary:
// Called when the user starts typing alphanumeric characters.
// Deals with saving undo; see editActionInterval parameter.
// tags:
// private
if(this._steps.length === 0){
// You want to use the editor content without post filtering
// to make sure selection restores right for the 'initial' state.
// and undo is called. So not using this.value, as it was 'processed'
// and the line-up for selections may have been altered.
this._steps.push({'text':html.getChildrenHtml(this.editNode),'bookmark':this._getBookmark()});
}
},
_endEditing: function(){
// summary:
// Called when the user stops typing alphanumeric characters.
// Deals with saving undo; see editActionInterval parameter.
// tags:
// private
// Avoid filtering to make sure selections restore.
var v = html.getChildrenHtml(this.editNode);
this._undoedSteps=[];//clear undoed steps
this._steps.push({text: v, bookmark: this._getBookmark()});
},
onKeyDown: function(e){
// summary:
// Handler for onkeydown event.
// tags:
// private
//We need to save selection if the user TAB away from this editor
//no need to call _saveSelection for IE, as that will be taken care of in onBeforeDeactivate
if(!has("ie") && !this.iframe && e.keyCode == keys.TAB && !this.tabIndent){
this._saveSelection();
}
if(!this.customUndo){
this.inherited(arguments);
return;
}
var k = e.keyCode;
if(e.ctrlKey && !e.altKey){//undo and redo only if the special right Alt + z/y are not pressed #5892
if(k == 90 || k == 122){ //z
event.stop(e);
this.undo();
return;
}else if(k == 89 || k == 121){ //y
event.stop(e);
this.redo();
return;
}
}
this.inherited(arguments);
switch(k){
case keys.ENTER:
case keys.BACKSPACE:
case keys.DELETE:
this.beginEditing();
break;
case 88: //x
case 86: //v
if(e.ctrlKey && !e.altKey && !e.metaKey){
this.endEditing();//end current typing step if any
if(e.keyCode == 88){
this.beginEditing('cut');
//use timeout to trigger after the cut is complete
setTimeout(lang.hitch(this, this.endEditing), 1);
}else{
this.beginEditing('paste');
//use timeout to trigger after the paste is complete
setTimeout(lang.hitch(this, this.endEditing), 1);
}
break;
}
//pass through
default:
if(!e.ctrlKey && !e.altKey && !e.metaKey && (e.keyCode<keys.F1 || e.keyCode>keys.F15)){
this.beginEditing();
break;
}
//pass through
case keys.ALT:
this.endEditing();
break;
case keys.UP_ARROW:
case keys.DOWN_ARROW:
case keys.LEFT_ARROW:
case keys.RIGHT_ARROW:
case keys.HOME:
case keys.END:
case keys.PAGE_UP:
case keys.PAGE_DOWN:
this.endEditing(true);
break;
//maybe ctrl+backspace/delete, so don't endEditing when ctrl is pressed
case keys.CTRL:
case keys.SHIFT:
case keys.TAB:
break;
}
},
_onBlur: function(){
// summary:
// Called from focus manager when focus has moved away from this editor
// tags:
// protected
//this._saveSelection();
this.inherited(arguments);
this.endEditing(true);
},
_saveSelection: function(){
// summary:
// Save the currently selected text in _savedSelection attribute
// tags:
// private
try{
this._savedSelection=this._getBookmark();
}catch(e){ /* Squelch any errors that occur if selection save occurs due to being hidden simultaneously. */}
},
_restoreSelection: function(){
// summary:
// Re-select the text specified in _savedSelection attribute;
// see _saveSelection().
// tags:
// private
if(this._savedSelection){
// Clear off cursor to start, we're deliberately going to a selection.
delete this._cursorToStart;
// only restore the selection if the current range is collapsed
// if not collapsed, then it means the editor does not lose
// selection and there is no need to restore it
if(win.withGlobal(this.window,'isCollapsed',dijit)){
this._moveToBookmark(this._savedSelection);
}
delete this._savedSelection;
}
},
onClick: function(){
// summary:
// Handler for when editor is clicked
// tags:
// protected
this.endEditing(true);
this.inherited(arguments);
},
replaceValue: function(/*String*/ html){
// summary:
// over-ride of replaceValue to support custom undo and stack maintenance.
// tags:
// protected
if(!this.customUndo){
this.inherited(arguments);
}else{
if(this.isClosed){
this.setValue(html);
}else{
this.beginEditing();
if(!html){
html = "&#160;"; // &nbsp;
}
this.setValue(html);
this.endEditing();
}
}
},
_setDisabledAttr: function(/*Boolean*/ value){
var disableFunc = lang.hitch(this, function(){
if((!this.disabled && value) || (!this._buttonEnabledPlugins && value)){
// Disable editor: disable all enabled buttons and remember that list
array.forEach(this._plugins, function(p){
p.set("disabled", true);
});
}else if(this.disabled && !value){
// Restore plugins to being active.
array.forEach(this._plugins, function(p){
p.set("disabled", false);
});
}
});
this.setValueDeferred.addCallback(disableFunc);
this.inherited(arguments);
},
_setStateClass: function(){
try{
this.inherited(arguments);
// Let theme set the editor's text color based on editor enabled/disabled state.
// We need to jump through hoops because the main document (where the theme CSS is)
// is separate from the iframe's document.
if(this.document && this.document.body){
domStyle.set(this.document.body, "color", domStyle.get(this.iframe, "color"));
}
}catch(e){ /* Squelch any errors caused by focus change if hidden during a state change */}
}
});
// Register the "default plugins", ie, the built-in editor commands
function simplePluginFactory(args){
return new _Plugin({ command: args.name });
}
function togglePluginFactory(args){
return new _Plugin({ buttonClass: ToggleButton, command: args.name });
}
lang.mixin(_Plugin.registry, {
"undo": simplePluginFactory,
"redo": simplePluginFactory,
"cut": simplePluginFactory,
"copy": simplePluginFactory,
"paste": simplePluginFactory,
"insertOrderedList": simplePluginFactory,
"insertUnorderedList": simplePluginFactory,
"indent": simplePluginFactory,
"outdent": simplePluginFactory,
"justifyCenter": simplePluginFactory,
"justifyFull": simplePluginFactory,
"justifyLeft": simplePluginFactory,
"justifyRight": simplePluginFactory,
"delete": simplePluginFactory,
"selectAll": simplePluginFactory,
"removeFormat": simplePluginFactory,
"unlink": simplePluginFactory,
"insertHorizontalRule": simplePluginFactory,
"bold": togglePluginFactory,
"italic": togglePluginFactory,
"underline": togglePluginFactory,
"strikethrough": togglePluginFactory,
"subscript": togglePluginFactory,
"superscript": togglePluginFactory,
"|": function(){
return new _Plugin({ button: new ToolbarSeparator(), setEditor: function(editor){this.editor = editor;}});
}
});
return Editor;
});
},
'dojo/cldr/nls/currency':function(){
define({ root:
//begin v1.x content
{
"USD_symbol": "US$",
"CAD_symbol": "CA$",
"GBP_symbol": "£",
"HKD_symbol": "HK$",
"JPY_symbol": "JP¥",
"AUD_symbol": "AU$",
"CNY_symbol": "CN¥",
"EUR_symbol": "€"
}
//end v1.x content
,
"ar": true,
"ca": true,
"cs": true,
"da": true,
"de": true,
"el": true,
"en": true,
"en-au": true,
"en-ca": true,
"es": true,
"fi": true,
"fr": true,
"he": true,
"hu": true,
"it": true,
"ja": true,
"ko": true,
"nb": true,
"nl": true,
"pl": true,
"pt": true,
"ro": true,
"ru": true,
"sk": true,
"sl": true,
"sv": true,
"th": true,
"tr": true,
"zh": true,
"zh-hant": true,
"zh-hk": true,
"zh-tw": true
});
},
'dijit/Toolbar':function(){
define([
"require",
"dojo/_base/declare", // declare
"dojo/_base/kernel",
"dojo/keys", // keys.LEFT_ARROW keys.RIGHT_ARROW
"dojo/ready",
"./_Widget",
"./_KeyNavContainer",
"./_TemplatedMixin"
], function(require, declare, kernel, keys, ready, _Widget, _KeyNavContainer, _TemplatedMixin){
/*=====
var _Widget = dijit._Widget;
var _KeyNavContainer = dijit._KeyNavContainer;
var _TemplatedMixin = dijit._TemplatedMixin;
=====*/
// module:
// dijit/Toolbar
// summary:
// A Toolbar widget, used to hold things like `dijit.Editor` buttons
// Back compat w/1.6, remove for 2.0
if(!kernel.isAsync){
ready(0, function(){
var requires = ["dijit/ToolbarSeparator"];
require(requires); // use indirection so modules not rolled into a build
});
}
return declare("dijit.Toolbar", [_Widget, _TemplatedMixin, _KeyNavContainer], {
// summary:
// A Toolbar widget, used to hold things like `dijit.Editor` buttons
templateString:
'<div class="dijit" role="toolbar" tabIndex="${tabIndex}" data-dojo-attach-point="containerNode">' +
'</div>',
baseClass: "dijitToolbar",
postCreate: function(){
this.inherited(arguments);
this.connectKeyNavHandlers(
this.isLeftToRight() ? [keys.LEFT_ARROW] : [keys.RIGHT_ARROW],
this.isLeftToRight() ? [keys.RIGHT_ARROW] : [keys.LEFT_ARROW]
);
}
});
});
},
'dijit/layout/StackContainer':function(){
define([
"dojo/_base/array", // array.forEach array.indexOf array.some
"dojo/cookie", // cookie
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.add domClass.replace
"dojo/_base/kernel", // kernel.isAsync
"dojo/_base/lang", // lang.extend
"dojo/ready",
"dojo/topic", // publish
"../registry", // registry.byId
"../_WidgetBase",
"./_LayoutWidget",
"dojo/i18n!../nls/common"
], function(array, cookie, declare, domClass, kernel, lang, ready, topic,
registry, _WidgetBase, _LayoutWidget){
/*=====
var _WidgetBase = dijit._WidgetBase;
var _LayoutWidget = dijit.layout._LayoutWidget;
var StackController = dijit.layout.StackController;
=====*/
// module:
// dijit/layout/StackContainer
// summary:
// A container that has multiple children, but shows only one child at a time.
// Back compat w/1.6, remove for 2.0
if(!kernel.isAsync){
ready(0, function(){
var requires = ["dijit/layout/StackController"];
require(requires); // use indirection so modules not rolled into a build
});
}
// These arguments can be specified for the children of a StackContainer.
// Since any widget can be specified as a StackContainer child, mix them
// into the base widget class. (This is a hack, but it's effective.)
lang.extend(_WidgetBase, {
// selected: Boolean
// Parameter for children of `dijit.layout.StackContainer` or subclasses.
// Specifies that this widget should be the initially displayed pane.
// Note: to change the selected child use `dijit.layout.StackContainer.selectChild`
selected: false,
// closable: Boolean
// Parameter for children of `dijit.layout.StackContainer` or subclasses.
// True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
closable: false,
// iconClass: String
// Parameter for children of `dijit.layout.StackContainer` or subclasses.
// CSS Class specifying icon to use in label associated with this pane.
iconClass: "dijitNoIcon",
// showTitle: Boolean
// Parameter for children of `dijit.layout.StackContainer` or subclasses.
// When true, display title of this widget as tab label etc., rather than just using
// icon specified in iconClass
showTitle: true
});
return declare("dijit.layout.StackContainer", _LayoutWidget, {
// summary:
// A container that has multiple children, but shows only
// one child at a time
//
// description:
// A container for widgets (ContentPanes, for example) That displays
// only one Widget at a time.
//
// Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild
//
// Can be base class for container, Wizard, Show, etc.
// doLayout: Boolean
// If true, change the size of my currently displayed child to match my size
doLayout: true,
// persist: Boolean
// Remembers the selected child across sessions
persist: false,
baseClass: "dijitStackContainer",
/*=====
// selectedChildWidget: [readonly] dijit._Widget
// References the currently selected child widget, if any.
// Adjust selected child with selectChild() method.
selectedChildWidget: null,
=====*/
buildRendering: function(){
this.inherited(arguments);
domClass.add(this.domNode, "dijitLayoutContainer");
this.containerNode.setAttribute("role", "tabpanel");
},
postCreate: function(){
this.inherited(arguments);
this.connect(this.domNode, "onkeypress", this._onKeyPress);
},
startup: function(){
if(this._started){ return; }
var children = this.getChildren();
// Setup each page panel to be initially hidden
array.forEach(children, this._setupChild, this);
// Figure out which child to initially display, defaulting to first one
if(this.persist){
this.selectedChildWidget = registry.byId(cookie(this.id + "_selectedChild"));
}else{
array.some(children, function(child){
if(child.selected){
this.selectedChildWidget = child;
}
return child.selected;
}, this);
}
var selected = this.selectedChildWidget;
if(!selected && children[0]){
selected = this.selectedChildWidget = children[0];
selected.selected = true;
}
// Publish information about myself so any StackControllers can initialize.
// This needs to happen before this.inherited(arguments) so that for
// TabContainer, this._contentBox doesn't include the space for the tab labels.
topic.publish(this.id+"-startup", {children: children, selected: selected});
// Startup each child widget, and do initial layout like setting this._contentBox,
// then calls this.resize() which does the initial sizing on the selected child.
this.inherited(arguments);
},
resize: function(){
// Resize is called when we are first made visible (it's called from startup()
// if we are initially visible). If this is the first time we've been made
// visible then show our first child.
if(!this._hasBeenShown){
this._hasBeenShown = true;
var selected = this.selectedChildWidget;
if(selected){
this._showChild(selected);
}
}
this.inherited(arguments);
},
_setupChild: function(/*dijit._Widget*/ child){
// Overrides _LayoutWidget._setupChild()
this.inherited(arguments);
domClass.replace(child.domNode, "dijitHidden", "dijitVisible");
// remove the title attribute so it doesn't show up when i hover
// over a node
child.domNode.title = "";
},
addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
// Overrides _Container.addChild() to do layout and publish events
this.inherited(arguments);
if(this._started){
topic.publish(this.id+"-addChild", child, insertIndex); // publish
// in case the tab titles have overflowed from one line to two lines
// (or, if this if first child, from zero lines to one line)
// TODO: w/ScrollingTabController this is no longer necessary, although
// ScrollTabController.resize() does need to get called to show/hide
// the navigation buttons as appropriate, but that's handled in ScrollingTabController.onAddChild().
// If this is updated to not layout [except for initial child added / last child removed], update
// "childless startup" test in StackContainer.html to check for no resize event after second addChild()
this.layout();
// if this is the first child, then select it
if(!this.selectedChildWidget){
this.selectChild(child);
}
}
},
removeChild: function(/*dijit._Widget*/ page){
// Overrides _Container.removeChild() to do layout and publish events
this.inherited(arguments);
if(this._started){
// this will notify any tablists to remove a button; do this first because it may affect sizing
topic.publish(this.id + "-removeChild", page); // publish
}
// If all our children are being destroyed than don't run the code below (to select another page),
// because we are deleting every page one by one
if(this._descendantsBeingDestroyed){ return; }
// Select new page to display, also updating TabController to show the respective tab.
// Do this before layout call because it can affect the height of the TabController.
if(this.selectedChildWidget === page){
this.selectedChildWidget = undefined;
if(this._started){
var children = this.getChildren();
if(children.length){
this.selectChild(children[0]);
}
}
}
if(this._started){
// In case the tab titles now take up one line instead of two lines
// (note though that ScrollingTabController never overflows to multiple lines),
// or the height has changed slightly because of addition/removal of tab which close icon
this.layout();
}
},
selectChild: function(/*dijit._Widget|String*/ page, /*Boolean*/ animate){
// summary:
// Show the given widget (which must be one of my children)
// page:
// Reference to child widget or id of child widget
page = registry.byId(page);
if(this.selectedChildWidget != page){
// Deselect old page and select new one
var d = this._transition(page, this.selectedChildWidget, animate);
this._set("selectedChildWidget", page);
topic.publish(this.id+"-selectChild", page); // publish
if(this.persist){
cookie(this.id + "_selectedChild", this.selectedChildWidget.id);
}
}
return d; // If child has an href, promise that fires when the child's href finishes loading
},
_transition: function(newWidget, oldWidget /*===== , animate =====*/){
// summary:
// Hide the old widget and display the new widget.
// Subclasses should override this.
// newWidget: dijit._Widget
// The newly selected widget.
// oldWidget: dijit._Widget
// The previously selected widget.
// animate: Boolean
// Used by AccordionContainer to turn on/off slide effect.
// tags:
// protected extension
if(oldWidget){
this._hideChild(oldWidget);
}
var d = this._showChild(newWidget);
// Size the new widget, in case this is the first time it's being shown,
// or I have been resized since the last time it was shown.
// Note that page must be visible for resizing to work.
if(newWidget.resize){
if(this.doLayout){
newWidget.resize(this._containerContentBox || this._contentBox);
}else{
// the child should pick it's own size but we still need to call resize()
// (with no arguments) to let the widget lay itself out
newWidget.resize();
}
}
return d; // If child has an href, promise that fires when the child's href finishes loading
},
_adjacent: function(/*Boolean*/ forward){
// summary:
// Gets the next/previous child widget in this container from the current selection.
var children = this.getChildren();
var index = array.indexOf(children, this.selectedChildWidget);
index += forward ? 1 : children.length - 1;
return children[ index % children.length ]; // dijit._Widget
},
forward: function(){
// summary:
// Advance to next page.
return this.selectChild(this._adjacent(true), true);
},
back: function(){
// summary:
// Go back to previous page.
return this.selectChild(this._adjacent(false), true);
},
_onKeyPress: function(e){
topic.publish(this.id+"-containerKeyPress", { e: e, page: this}); // publish
},
layout: function(){
// Implement _LayoutWidget.layout() virtual method.
var child = this.selectedChildWidget;
if(child && child.resize){
if(this.doLayout){
child.resize(this._containerContentBox || this._contentBox);
}else{
child.resize();
}
}
},
_showChild: function(/*dijit._Widget*/ page){
// summary:
// Show the specified child by changing it's CSS, and call _onShow()/onShow() so
// it can do any updates it needs regarding loading href's etc.
// returns:
// Promise that fires when page has finished showing, or true if there's no href
var children = this.getChildren();
page.isFirstChild = (page == children[0]);
page.isLastChild = (page == children[children.length-1]);
page._set("selected", true);
domClass.replace(page.domNode, "dijitVisible", "dijitHidden");
return (page._onShow && page._onShow()) || true;
},
_hideChild: function(/*dijit._Widget*/ page){
// summary:
// Hide the specified child by changing it's CSS, and call _onHide() so
// it's notified.
page._set("selected", false);
domClass.replace(page.domNode, "dijitHidden", "dijitVisible");
page.onHide && page.onHide();
},
closeChild: function(/*dijit._Widget*/ page){
// summary:
// Callback when user clicks the [X] to remove a page.
// If onClose() returns true then remove and destroy the child.
// tags:
// private
var remove = page.onClose(this, page);
if(remove){
this.removeChild(page);
// makes sure we can clean up executeScripts in ContentPane onUnLoad
page.destroyRecursive();
}
},
destroyDescendants: function(/*Boolean*/ preserveDom){
this._descendantsBeingDestroyed = true;
this.selectedChildWidget = undefined;
array.forEach(this.getChildren(), function(child){
if(!preserveDom){
this.removeChild(child);
}
child.destroyRecursive(preserveDom);
}, this);
this._descendantsBeingDestroyed = false;
}
});
});
},
'dojo/regexp':function(){
define(["./_base/kernel", "./_base/lang"], function(dojo, lang) {
// module:
// dojo/regexp
// summary:
// TODOC
lang.getObject("regexp", true, dojo);
/*=====
dojo.regexp = {
// summary: Regular expressions and Builder resources
};
=====*/
dojo.regexp.escapeString = function(/*String*/str, /*String?*/except){
// summary:
// Adds escape sequences for special characters in regular expressions
// except:
// a String with special characters to be left unescaped
return str.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(ch){
if(except && except.indexOf(ch) != -1){
return ch;
}
return "\\" + ch;
}); // String
};
dojo.regexp.buildGroupRE = function(/*Object|Array*/arr, /*Function*/re, /*Boolean?*/nonCapture){
// summary:
// Builds a regular expression that groups subexpressions
// description:
// A utility function used by some of the RE generators. The
// subexpressions are constructed by the function, re, in the second
// parameter. re builds one subexpression for each elem in the array
// a, in the first parameter. Returns a string for a regular
// expression that groups all the subexpressions.
// arr:
// A single value or an array of values.
// re:
// A function. Takes one parameter and converts it to a regular
// expression.
// nonCapture:
// If true, uses non-capturing match, otherwise matches are retained
// by regular expression. Defaults to false
// case 1: a is a single value.
if(!(arr instanceof Array)){
return re(arr); // String
}
// case 2: a is an array
var b = [];
for(var i = 0; i < arr.length; i++){
// convert each elem to a RE
b.push(re(arr[i]));
}
// join the REs as alternatives in a RE group.
return dojo.regexp.group(b.join("|"), nonCapture); // String
};
dojo.regexp.group = function(/*String*/expression, /*Boolean?*/nonCapture){
// summary:
// adds group match to expression
// nonCapture:
// If true, uses non-capturing match, otherwise matches are retained
// by regular expression.
return "(" + (nonCapture ? "?:":"") + expression + ")"; // String
};
return dojo.regexp;
});
},
'dijit/form/ComboBox':function(){
define([
"dojo/_base/declare", // declare
"./ValidationTextBox",
"./ComboBoxMixin"
], function(declare, ValidationTextBox, ComboBoxMixin){
/*=====
var ValidationTextBox = dijit.form.ValidationTextBox;
var ComboBoxMixin = dijit.form.ComboBoxMixin;
=====*/
// module:
// dijit/form/ComboBox
// summary:
// Auto-completing text box
return declare("dijit.form.ComboBox", [ValidationTextBox, ComboBoxMixin], {
// summary:
// Auto-completing text box
//
// description:
// The drop down box's values are populated from an class called
// a data provider, which returns a list of values based on the characters
// that the user has typed into the input box.
// If OPTION tags are used as the data provider via markup,
// then the OPTION tag's child text node is used as the widget value
// when selected. The OPTION tag's value attribute is ignored.
// To set the default value when using OPTION tags, specify the selected
// attribute on 1 of the child OPTION tags.
//
// Some of the options to the ComboBox are actually arguments to the data
// provider.
});
});
},
'dijit/form/_FormMixin':function(){
define([
"dojo/_base/array", // array.every array.filter array.forEach array.indexOf array.map
"dojo/_base/declare", // declare
"dojo/_base/kernel", // kernel.deprecated
"dojo/_base/lang", // lang.hitch lang.isArray
"dojo/window" // winUtils.scrollIntoView
], function(array, declare, kernel, lang, winUtils){
// module:
// dijit/form/_FormMixin
// summary:
// Mixin for containers of form widgets (i.e. widgets that represent a single value
// and can be children of a <form> node or dijit.form.Form widget)
return declare("dijit.form._FormMixin", null, {
// summary:
// Mixin for containers of form widgets (i.e. widgets that represent a single value
// and can be children of a <form> node or dijit.form.Form widget)
// description:
// Can extract all the form widgets
// values and combine them into a single javascript object, or alternately
// take such an object and set the values for all the contained
// form widgets
/*=====
// value: Object
// Name/value hash for each child widget with a name and value.
// Child widgets without names are not part of the hash.
//
// If there are multiple child widgets w/the same name, value is an array,
// unless they are radio buttons in which case value is a scalar (since only
// one radio button can be checked at a time).
//
// If a child widget's name is a dot separated list (like a.b.c.d), it's a nested structure.
//
// Example:
// | { name: "John Smith", interests: ["sports", "movies"] }
=====*/
// state: [readonly] String
// Will be "Error" if one or more of the child widgets has an invalid value,
// "Incomplete" if not all of the required child widgets are filled in. Otherwise, "",
// which indicates that the form is ready to be submitted.
state: "",
// TODO:
// * Repeater
// * better handling for arrays. Often form elements have names with [] like
// * people[3].sex (for a list of people [{name: Bill, sex: M}, ...])
//
//
_getDescendantFormWidgets: function(/*dijit._WidgetBase[]?*/ children){
// summary:
// Returns all form widget descendants, searching through non-form child widgets like BorderContainer
var res = [];
array.forEach(children || this.getChildren(), function(child){
if("value" in child){
res.push(child);
}else{
res = res.concat(this._getDescendantFormWidgets(child.getChildren()));
}
}, this);
return res;
},
reset: function(){
array.forEach(this._getDescendantFormWidgets(), function(widget){
if(widget.reset){
widget.reset();
}
});
},
validate: function(){
// summary:
// returns if the form is valid - same as isValid - but
// provides a few additional (ui-specific) features.
// 1 - it will highlight any sub-widgets that are not
// valid
// 2 - it will call focus() on the first invalid
// sub-widget
var didFocus = false;
return array.every(array.map(this._getDescendantFormWidgets(), function(widget){
// Need to set this so that "required" widgets get their
// state set.
widget._hasBeenBlurred = true;
var valid = widget.disabled || !widget.validate || widget.validate();
if(!valid && !didFocus){
// Set focus of the first non-valid widget
winUtils.scrollIntoView(widget.containerNode || widget.domNode);
widget.focus();
didFocus = true;
}
return valid;
}), function(item){ return item; });
},
setValues: function(val){
kernel.deprecated(this.declaredClass+"::setValues() is deprecated. Use set('value', val) instead.", "", "2.0");
return this.set('value', val);
},
_setValueAttr: function(/*Object*/ obj){
// summary:
// Fill in form values from according to an Object (in the format returned by get('value'))
// generate map from name --> [list of widgets with that name]
var map = { };
array.forEach(this._getDescendantFormWidgets(), function(widget){
if(!widget.name){ return; }
var entry = map[widget.name] || (map[widget.name] = [] );
entry.push(widget);
});
for(var name in map){
if(!map.hasOwnProperty(name)){
continue;
}
var widgets = map[name], // array of widgets w/this name
values = lang.getObject(name, false, obj); // list of values for those widgets
if(values === undefined){
continue;
}
if(!lang.isArray(values)){
values = [ values ];
}
if(typeof widgets[0].checked == 'boolean'){
// for checkbox/radio, values is a list of which widgets should be checked
array.forEach(widgets, function(w){
w.set('value', array.indexOf(values, w.value) != -1);
});
}else if(widgets[0].multiple){
// it takes an array (e.g. multi-select)
widgets[0].set('value', values);
}else{
// otherwise, values is a list of values to be assigned sequentially to each widget
array.forEach(widgets, function(w, i){
w.set('value', values[i]);
});
}
}
/***
* TODO: code for plain input boxes (this shouldn't run for inputs that are part of widgets)
array.forEach(this.containerNode.elements, function(element){
if(element.name == ''){return}; // like "continue"
var namePath = element.name.split(".");
var myObj=obj;
var name=namePath[namePath.length-1];
for(var j=1,len2=namePath.length;j<len2;++j){
var p=namePath[j - 1];
// repeater support block
var nameA=p.split("[");
if(nameA.length > 1){
if(typeof(myObj[nameA[0]]) == "undefined"){
myObj[nameA[0]]=[ ];
} // if
nameIndex=parseInt(nameA[1]);
if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
myObj[nameA[0]][nameIndex] = { };
}
myObj=myObj[nameA[0]][nameIndex];
continue;
} // repeater support ends
if(typeof(myObj[p]) == "undefined"){
myObj=undefined;
break;
};
myObj=myObj[p];
}
if(typeof(myObj) == "undefined"){
return; // like "continue"
}
if(typeof(myObj[name]) == "undefined" && this.ignoreNullValues){
return; // like "continue"
}
// TODO: widget values (just call set('value', ...) on the widget)
// TODO: maybe should call dojo.getNodeProp() instead
switch(element.type){
case "checkbox":
element.checked = (name in myObj) &&
array.some(myObj[name], function(val){ return val == element.value; });
break;
case "radio":
element.checked = (name in myObj) && myObj[name] == element.value;
break;
case "select-multiple":
element.selectedIndex=-1;
array.forEach(element.options, function(option){
option.selected = array.some(myObj[name], function(val){ return option.value == val; });
});
break;
case "select-one":
element.selectedIndex="0";
array.forEach(element.options, function(option){
option.selected = option.value == myObj[name];
});
break;
case "hidden":
case "text":
case "textarea":
case "password":
element.value = myObj[name] || "";
break;
}
});
*/
// Note: no need to call this._set("value", ...) as the child updates will trigger onChange events
// which I am monitoring.
},
getValues: function(){
kernel.deprecated(this.declaredClass+"::getValues() is deprecated. Use get('value') instead.", "", "2.0");
return this.get('value');
},
_getValueAttr: function(){
// summary:
// Returns Object representing form values. See description of `value` for details.
// description:
// The value is updated into this.value every time a child has an onChange event,
// so in the common case this function could just return this.value. However,
// that wouldn't work when:
//
// 1. User presses return key to submit a form. That doesn't fire an onchange event,
// and even if it did it would come too late due to the setTimeout(..., 0) in _handleOnChange()
//
// 2. app for some reason calls this.get("value") while the user is typing into a
// form field. Not sure if that case needs to be supported or not.
// get widget values
var obj = { };
array.forEach(this._getDescendantFormWidgets(), function(widget){
var name = widget.name;
if(!name || widget.disabled){ return; }
// Single value widget (checkbox, radio, or plain <input> type widget)
var value = widget.get('value');
// Store widget's value(s) as a scalar, except for checkboxes which are automatically arrays
if(typeof widget.checked == 'boolean'){
if(/Radio/.test(widget.declaredClass)){
// radio button
if(value !== false){
lang.setObject(name, value, obj);
}else{
// give radio widgets a default of null
value = lang.getObject(name, false, obj);
if(value === undefined){
lang.setObject(name, null, obj);
}
}
}else{
// checkbox/toggle button
var ary=lang.getObject(name, false, obj);
if(!ary){
ary=[];
lang.setObject(name, ary, obj);
}
if(value !== false){
ary.push(value);
}
}
}else{
var prev=lang.getObject(name, false, obj);
if(typeof prev != "undefined"){
if(lang.isArray(prev)){
prev.push(value);
}else{
lang.setObject(name, [prev, value], obj);
}
}else{
// unique name
lang.setObject(name, value, obj);
}
}
});
/***
* code for plain input boxes (see also domForm.formToObject, can we use that instead of this code?
* but it doesn't understand [] notation, presumably)
var obj = { };
array.forEach(this.containerNode.elements, function(elm){
if(!elm.name) {
return; // like "continue"
}
var namePath = elm.name.split(".");
var myObj=obj;
var name=namePath[namePath.length-1];
for(var j=1,len2=namePath.length;j<len2;++j){
var nameIndex = null;
var p=namePath[j - 1];
var nameA=p.split("[");
if(nameA.length > 1){
if(typeof(myObj[nameA[0]]) == "undefined"){
myObj[nameA[0]]=[ ];
} // if
nameIndex=parseInt(nameA[1]);
if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
myObj[nameA[0]][nameIndex] = { };
}
}else if(typeof(myObj[nameA[0]]) == "undefined"){
myObj[nameA[0]] = { }
} // if
if(nameA.length == 1){
myObj=myObj[nameA[0]];
}else{
myObj=myObj[nameA[0]][nameIndex];
} // if
} // for
if((elm.type != "select-multiple" && elm.type != "checkbox" && elm.type != "radio") || (elm.type == "radio" && elm.checked)){
if(name == name.split("[")[0]){
myObj[name]=elm.value;
}else{
// can not set value when there is no name
}
}else if(elm.type == "checkbox" && elm.checked){
if(typeof(myObj[name]) == 'undefined'){
myObj[name]=[ ];
}
myObj[name].push(elm.value);
}else if(elm.type == "select-multiple"){
if(typeof(myObj[name]) == 'undefined'){
myObj[name]=[ ];
}
for(var jdx=0,len3=elm.options.length; jdx<len3; ++jdx){
if(elm.options[jdx].selected){
myObj[name].push(elm.options[jdx].value);
}
}
} // if
name=undefined;
}); // forEach
***/
return obj;
},
isValid: function(){
// summary:
// Returns true if all of the widgets are valid.
// Deprecated, will be removed in 2.0. Use get("state") instead.
return this.state == "";
},
onValidStateChange: function(/*Boolean*/ /*===== isValid =====*/){
// summary:
// Stub function to connect to if you want to do something
// (like disable/enable a submit button) when the valid
// state changes on the form as a whole.
//
// Deprecated. Will be removed in 2.0. Use watch("state", ...) instead.
},
_getState: function(){
// summary:
// Compute what this.state should be based on state of children
var states = array.map(this._descendants, function(w){
return w.get("state") || "";
});
return array.indexOf(states, "Error") >= 0 ? "Error" :
array.indexOf(states, "Incomplete") >= 0 ? "Incomplete" : "";
},
disconnectChildren: function(){
// summary:
// Remove connections to monitor changes to children's value, error state, and disabled state,
// in order to update Form.value and Form.state.
array.forEach(this._childConnections || [], lang.hitch(this, "disconnect"));
array.forEach(this._childWatches || [], function(w){ w.unwatch(); });
},
connectChildren: function(/*Boolean*/ inStartup){
// summary:
// Setup connections to monitor changes to children's value, error state, and disabled state,
// in order to update Form.value and Form.state.
//
// You can call this function directly, ex. in the event that you
// programmatically add a widget to the form *after* the form has been
// initialized.
var _this = this;
// Remove old connections, if any
this.disconnectChildren();
this._descendants = this._getDescendantFormWidgets();
// (Re)set this.value and this.state. Send watch() notifications but not on startup.
var set = inStartup ? function(name, val){ _this[name] = val; } : lang.hitch(this, "_set");
set("value", this.get("value"));
set("state", this._getState());
// Monitor changes to error state and disabled state in order to update
// Form.state
var conns = (this._childConnections = []),
watches = (this._childWatches = []);
array.forEach(array.filter(this._descendants,
function(item){ return item.validate; }
),
function(widget){
// We are interested in whenever the widget changes validity state - or
// whenever the disabled attribute on that widget is changed.
array.forEach(["state", "disabled"], function(attr){
watches.push(widget.watch(attr, function(){
_this.set("state", _this._getState());
}));
});
});
// And monitor calls to child.onChange so we can update this.value
var onChange = function(){
// summary:
// Called when child's value or disabled state changes
// Use setTimeout() to collapse value changes in multiple children into a single
// update to my value. Multiple updates will occur on:
// 1. Form.set()
// 2. Form.reset()
// 3. user selecting a radio button (which will de-select another radio button,
// causing two onChange events)
if(_this._onChangeDelayTimer){
clearTimeout(_this._onChangeDelayTimer);
}
_this._onChangeDelayTimer = setTimeout(function(){
delete _this._onChangeDelayTimer;
_this._set("value", _this.get("value"));
}, 10);
};
array.forEach(
array.filter(this._descendants, function(item){ return item.onChange; } ),
function(widget){
// When a child widget's value changes,
// the efficient thing to do is to just update that one attribute in this.value,
// but that gets a little complicated when a checkbox is checked/unchecked
// since this.value["checkboxName"] contains an array of all the checkboxes w/the same name.
// Doing simple thing for now.
conns.push(_this.connect(widget, "onChange", onChange));
// Disabling/enabling a child widget should remove it's value from this.value.
// Again, this code could be more efficient, doing simple thing for now.
watches.push(widget.watch("disabled", onChange));
}
);
},
startup: function(){
this.inherited(arguments);
// Initialize value and valid/invalid state tracking. Needs to be done in startup()
// so that children are initialized.
this.connectChildren(true);
// Make state change call onValidStateChange(), will be removed in 2.0
this.watch("state", function(attr, oldVal, newVal){ this.onValidStateChange(newVal == ""); });
},
destroy: function(){
this.disconnectChildren();
this.inherited(arguments);
}
});
});
},
'dijit/_editor/plugins/LinkDialog':function(){
define([
"require",
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.get
"dojo/keys", // keys.ENTER
"dojo/_base/lang", // lang.delegate lang.hitch lang.trim
"dojo/_base/sniff", // has("ie")
"dojo/string", // string.substitute
"dojo/_base/window", // win.withGlobal
"../../_Widget",
"../_Plugin",
"../../form/DropDownButton",
"../range",
"../selection"
], function(require, declare, domAttr, keys, lang, has, string, win,
_Widget, _Plugin, DropDownButton, rangeapi, selectionapi){
/*=====
var _Plugin = dijit._editor._Plugin;
=====*/
// module:
// dijit/_editor/plugins/LinkDialog
// summary:
// Editor plugins: LinkDialog (for inserting links) and ImgLinkDialog (for inserting images)
var LinkDialog = declare("dijit._editor.plugins.LinkDialog", _Plugin, {
// summary:
// This plugin provides the basis for an 'anchor' (link) dialog and an extension of it
// provides the image link dialog.
//
// description:
// The command provided by this plugin is:
// * createLink
// Override _Plugin.buttonClass. This plugin is controlled by a DropDownButton
// (which triggers a TooltipDialog).
buttonClass: DropDownButton,
// Override _Plugin.useDefaultCommand... processing is handled by this plugin, not by dijit.Editor.
useDefaultCommand: false,
// urlRegExp: [protected] String
// Used for validating input as correct URL. While file:// urls are not terribly
// useful, they are technically valid.
urlRegExp: "((https?|ftps?|file)\\://|\./|/|)(/[a-zA-Z]{1,1}:/|)(((?:(?:[\\da-zA-Z](?:[-\\da-zA-Z]{0,61}[\\da-zA-Z])?)\\.)*(?:[a-zA-Z](?:[-\\da-zA-Z]{0,80}[\\da-zA-Z])?)\\.?)|(((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])|(0[xX]0*[\\da-fA-F]?[\\da-fA-F]\\.){3}0[xX]0*[\\da-fA-F]?[\\da-fA-F]|(0+[0-3][0-7][0-7]\\.){3}0+[0-3][0-7][0-7]|(0|[1-9]\\d{0,8}|[1-3]\\d{9}|4[01]\\d{8}|42[0-8]\\d{7}|429[0-3]\\d{6}|4294[0-8]\\d{5}|42949[0-5]\\d{4}|429496[0-6]\\d{3}|4294967[01]\\d{2}|42949672[0-8]\\d|429496729[0-5])|0[xX]0*[\\da-fA-F]{1,8}|([\\da-fA-F]{1,4}\\:){7}[\\da-fA-F]{1,4}|([\\da-fA-F]{1,4}\\:){6}((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])))(\\:\\d+)?(/(?:[^?#\\s/]+/)*(?:[^?#\\s/]{0,}(?:\\?[^?#\\s/]*)?(?:#.*)?)?)?",
// emailRegExp: [protected] String
// Used for validating input as correct email address. Taken from dojox.validate
emailRegExp: "<?(mailto\\:)([!#-'*+\\-\\/-9=?A-Z^-~]+[.])*[!#-'*+\\-\\/-9=?A-Z^-~]+" /*username*/ + "@" +
"((?:(?:[\\da-zA-Z](?:[-\\da-zA-Z]{0,61}[\\da-zA-Z])?)\\.)+(?:[a-zA-Z](?:[-\\da-zA-Z]{0,6}[\\da-zA-Z])?)\\.?)|localhost|^[^-][a-zA-Z0-9_-]*>?", // host.
// htmlTemplate: [protected] String
// String used for templating the HTML to insert at the desired point.
htmlTemplate: "<a href=\"${urlInput}\" _djrealurl=\"${urlInput}\"" +
" target=\"${targetSelect}\"" +
">${textInput}</a>",
// tag: [protected] String
// Tag used for the link type.
tag: "a",
// _hostRxp [private] RegExp
// Regular expression used to validate url fragments (ip address, hostname, etc)
_hostRxp: /^((([^\[:]+):)?([^@]+)@)?(\[([^\]]+)\]|([^\[:]*))(:([0-9]+))?$/,
// _userAtRxp [private] RegExp
// Regular expression used to validate e-mail address fragment.
_userAtRxp: /^([!#-'*+\-\/-9=?A-Z^-~]+[.])*[!#-'*+\-\/-9=?A-Z^-~]+@/i,
// linkDialogTemplate: [protected] String
// Template for contents of TooltipDialog to pick URL
linkDialogTemplate: [
"<table><tr><td>",
"<label for='${id}_urlInput'>${url}</label>",
"</td><td>",
"<input data-dojo-type='dijit.form.ValidationTextBox' required='true' " +
"id='${id}_urlInput' name='urlInput' data-dojo-props='intermediateChanges:true'/>",
"</td></tr><tr><td>",
"<label for='${id}_textInput'>${text}</label>",
"</td><td>",
"<input data-dojo-type='dijit.form.ValidationTextBox' required='true' id='${id}_textInput' " +
"name='textInput' data-dojo-props='intermediateChanges:true'/>",
"</td></tr><tr><td>",
"<label for='${id}_targetSelect'>${target}</label>",
"</td><td>",
"<select id='${id}_targetSelect' name='targetSelect' data-dojo-type='dijit.form.Select'>",
"<option selected='selected' value='_self'>${currentWindow}</option>",
"<option value='_blank'>${newWindow}</option>",
"<option value='_top'>${topWindow}</option>",
"<option value='_parent'>${parentWindow}</option>",
"</select>",
"</td></tr><tr><td colspan='2'>",
"<button data-dojo-type='dijit.form.Button' type='submit' id='${id}_setButton'>${set}</button>",
"<button data-dojo-type='dijit.form.Button' type='button' id='${id}_cancelButton'>${buttonCancel}</button>",
"</td></tr></table>"
].join(""),
_initButton: function(){
this.inherited(arguments);
// Setup to lazy create TooltipDialog first time the button is clicked
this.button.loadDropDown = lang.hitch(this, "_loadDropDown");
this._connectTagEvents();
},
_loadDropDown: function(callback){
// Called the first time the button is pressed. Initialize TooltipDialog.
require([
"dojo/i18n", // i18n.getLocalization
"../../TooltipDialog",
"../../registry", // registry.byId, registry.getUniqueId
"../../form/Button", // used by template
"../../form/Select", // used by template
"../../form/ValidationTextBox", // used by template
"dojo/i18n!../../nls/common",
"dojo/i18n!../nls/LinkDialog"
], lang.hitch(this, function(i18n, TooltipDialog, registry){
var _this = this;
this.tag = this.command == 'insertImage' ? 'img' : 'a';
var messages = lang.delegate(i18n.getLocalization("dijit", "common", this.lang),
i18n.getLocalization("dijit._editor", "LinkDialog", this.lang));
var dropDown = (this.dropDown = this.button.dropDown = new TooltipDialog({
title: messages[this.command + "Title"],
execute: lang.hitch(this, "setValue"),
onOpen: function(){
_this._onOpenDialog();
TooltipDialog.prototype.onOpen.apply(this, arguments);
},
onCancel: function(){
setTimeout(lang.hitch(_this, "_onCloseDialog"),0);
}
}));
messages.urlRegExp = this.urlRegExp;
messages.id = registry.getUniqueId(this.editor.id);
this._uniqueId = messages.id;
this._setContent(dropDown.title +
"<div style='border-bottom: 1px black solid;padding-bottom:2pt;margin-bottom:4pt'></div>" +
string.substitute(this.linkDialogTemplate, messages));
dropDown.startup();
this._urlInput = registry.byId(this._uniqueId + "_urlInput");
this._textInput = registry.byId(this._uniqueId + "_textInput");
this._setButton = registry.byId(this._uniqueId + "_setButton");
this.connect(registry.byId(this._uniqueId + "_cancelButton"), "onClick", function(){
this.dropDown.onCancel();
});
if(this._urlInput){
this.connect(this._urlInput, "onChange", "_checkAndFixInput");
}
if(this._textInput){
this.connect(this._textInput, "onChange", "_checkAndFixInput");
}
// Build up the dual check for http/https/file:, and mailto formats.
this._urlRegExp = new RegExp("^" + this.urlRegExp + "$", "i");
this._emailRegExp = new RegExp("^" + this.emailRegExp + "$", "i");
this._urlInput.isValid = lang.hitch(this, function(){
// Function over-ride of isValid to test if the input matches a url or a mailto style link.
var value = this._urlInput.get("value");
return this._urlRegExp.test(value) || this._emailRegExp.test(value);
});
// Listen for enter and execute if valid.
this.connect(dropDown.domNode, "onkeypress", function(e){
if(e && e.charOrCode == keys.ENTER &&
!e.shiftKey && !e.metaKey && !e.ctrlKey && !e.altKey){
if(!this._setButton.get("disabled")){
dropDown.onExecute();
dropDown.execute(dropDown.get('value'));
}
}
});
callback();
}));
},
_checkAndFixInput: function(){
// summary:
// A function to listen for onChange events and test the input contents
// for valid information, such as valid urls with http/https/ftp and if
// not present, try and guess if the input url is relative or not, and if
// not, append http:// to it. Also validates other fields as determined by
// the internal _isValid function.
var self = this;
var url = this._urlInput.get("value");
var fixupUrl = function(url){
var appendHttp = false;
var appendMailto = false;
if(url && url.length > 1){
url = lang.trim(url);
if(url.indexOf("mailto:") !== 0){
if(url.indexOf("/") > 0){
if(url.indexOf("://") === -1){
// Check that it doesn't start with / or ./, which would
// imply 'target server relativeness'
if(url.charAt(0) !== '/' && url.indexOf("./") !== 0){
if(self._hostRxp.test(url)){
appendHttp = true;
}
}
}
}else if(self._userAtRxp.test(url)){
// If it looks like a foo@, append a mailto.
appendMailto = true;
}
}
}
if(appendHttp){
self._urlInput.set("value", "http://" + url);
}
if(appendMailto){
self._urlInput.set("value", "mailto:" + url);
}
self._setButton.set("disabled", !self._isValid());
};
if(this._delayedCheck){
clearTimeout(this._delayedCheck);
this._delayedCheck = null;
}
this._delayedCheck = setTimeout(function(){
fixupUrl(url);
}, 250);
},
_connectTagEvents: function(){
// summary:
// Over-ridable function that connects tag specific events.
this.editor.onLoadDeferred.addCallback(lang.hitch(this, function(){
this.connect(this.editor.editNode, "ondblclick", this._onDblClick);
}));
},
_isValid: function(){
// summary:
// Internal function to allow validating of the inputs
// for a link to determine if set should be disabled or not
// tags:
// protected
return this._urlInput.isValid() && this._textInput.isValid();
},
_setContent: function(staticPanel){
// summary:
// Helper for _initButton above. Not sure why it's a separate method.
this.dropDown.set({
parserScope: "dojo", // make parser search for dojoType/data-dojo-type even if page is multi-version
content: staticPanel
});
},
_checkValues: function(args){
// summary:
// Function to check the values in args and 'fix' them up as needed.
// args: Object
// Content being set.
// tags:
// protected
if(args && args.urlInput){
args.urlInput = args.urlInput.replace(/"/g, "&quot;");
}
return args;
},
setValue: function(args){
// summary:
// Callback from the dialog when user presses "set" button.
// tags:
// private
//TODO: prevent closing popup if the text is empty
this._onCloseDialog();
if(has("ie") < 9){ //see #4151
var sel = rangeapi.getSelection(this.editor.window);
var range = sel.getRangeAt(0);
var a = range.endContainer;
if(a.nodeType === 3){
// Text node, may be the link contents, so check parent.
// This plugin doesn't really support nested HTML elements
// in the link, it assumes all link content is text.
a = a.parentNode;
}
if(a && (a.nodeName && a.nodeName.toLowerCase() !== this.tag)){
// Still nothing, one last thing to try on IE, as it might be 'img'
// and thus considered a control.
a = win.withGlobal(this.editor.window,
"getSelectedElement", selectionapi, [this.tag]);
}
if(a && (a.nodeName && a.nodeName.toLowerCase() === this.tag)){
// Okay, we do have a match. IE, for some reason, sometimes pastes before
// instead of removing the targeted paste-over element, so we unlink the
// old one first. If we do not the <a> tag remains, but it has no content,
// so isn't readily visible (but is wrong for the action).
if(this.editor.queryCommandEnabled("unlink")){
// Select all the link children, then unlink. The following insert will
// then replace the selected text.
win.withGlobal(this.editor.window,
"selectElementChildren", selectionapi, [a]);
this.editor.execCommand("unlink");
}
}
}
// make sure values are properly escaped, etc.
args = this._checkValues(args);
this.editor.execCommand('inserthtml',
string.substitute(this.htmlTemplate, args));
},
_onCloseDialog: function(){
// summary:
// Handler for close event on the dialog
this.editor.focus();
},
_getCurrentValues: function(a){
// summary:
// Over-ride for getting the values to set in the dropdown.
// a:
// The anchor/link to process for data for the dropdown.
// tags:
// protected
var url, text, target;
if(a && a.tagName.toLowerCase() === this.tag){
url = a.getAttribute('_djrealurl') || a.getAttribute('href');
target = a.getAttribute('target') || "_self";
text = a.textContent || a.innerText;
win.withGlobal(this.editor.window, "selectElement", selectionapi, [a, true]);
}else{
text = win.withGlobal(this.editor.window, selectionapi.getSelectedText);
}
return {urlInput: url || '', textInput: text || '', targetSelect: target || ''}; //Object;
},
_onOpenDialog: function(){
// summary:
// Handler for when the dialog is opened.
// If the caret is currently in a URL then populate the URL's info into the dialog.
var a;
if(has("ie") < 9){
// IE is difficult to select the element in, using the range unified
// API seems to work reasonably well.
var sel = rangeapi.getSelection(this.editor.window);
var range = sel.getRangeAt(0);
a = range.endContainer;
if(a.nodeType === 3){
// Text node, may be the link contents, so check parent.
// This plugin doesn't really support nested HTML elements
// in the link, it assumes all link content is text.
a = a.parentNode;
}
if(a && (a.nodeName && a.nodeName.toLowerCase() !== this.tag)){
// Still nothing, one last thing to try on IE, as it might be 'img'
// and thus considered a control.
a = win.withGlobal(this.editor.window,
"getSelectedElement", selectionapi, [this.tag]);
}
}else{
a = win.withGlobal(this.editor.window,
"getAncestorElement", selectionapi, [this.tag]);
}
this.dropDown.reset();
this._setButton.set("disabled", true);
this.dropDown.set("value", this._getCurrentValues(a));
},
_onDblClick: function(e){
// summary:
// Function to define a behavior on double clicks on the element
// type this dialog edits to select it and pop up the editor
// dialog.
// e: Object
// The double-click event.
// tags:
// protected.
if(e && e.target){
var t = e.target;
var tg = t.tagName? t.tagName.toLowerCase() : "";
if(tg === this.tag && domAttr.get(t,"href")){
var editor = this.editor;
win.withGlobal(editor.window,
"selectElement",
selectionapi, [t]);
editor.onDisplayChanged();
// Call onNormalizedDisplayChange() now, rather than on timer.
// On IE, when focus goes to the first <input> in the TooltipDialog, the editor loses it's selection.
// Later if onNormalizedDisplayChange() gets called via the timer it will disable the LinkDialog button
// (actually, all the toolbar buttons), at which point clicking the <input> will close the dialog,
// since (for unknown reasons) focus.js ignores disabled controls.
if(editor._updateTimer){
clearTimeout(editor._updateTimer);
delete editor._updateTimer;
}
editor.onNormalizedDisplayChanged();
var button = this.button;
setTimeout(function(){
// Focus shift outside the event handler.
// IE doesn't like focus changes in event handles.
button.set("disabled", false);
button.loadAndOpenDropDown().then(function(){
if(button.dropDown.focus){
button.dropDown.focus();
}
});
}, 10);
}
}
}
});
var ImgLinkDialog = declare("dijit._editor.plugins.ImgLinkDialog", [LinkDialog], {
// summary:
// This plugin extends LinkDialog and adds in a plugin for handling image links.
// provides the image link dialog.
//
// description:
// The command provided by this plugin is:
// * insertImage
// linkDialogTemplate: [protected] String
// Over-ride for template since img dialog doesn't need target that anchor tags may.
linkDialogTemplate: [
"<table><tr><td>",
"<label for='${id}_urlInput'>${url}</label>",
"</td><td>",
"<input dojoType='dijit.form.ValidationTextBox' regExp='${urlRegExp}' " +
"required='true' id='${id}_urlInput' name='urlInput' data-dojo-props='intermediateChanges:true'/>",
"</td></tr><tr><td>",
"<label for='${id}_textInput'>${text}</label>",
"</td><td>",
"<input data-dojo-type='dijit.form.ValidationTextBox' required='false' id='${id}_textInput' " +
"name='textInput' data-dojo-props='intermediateChanges:true'/>",
"</td></tr><tr><td>",
"</td><td>",
"</td></tr><tr><td colspan='2'>",
"<button data-dojo-type='dijit.form.Button' type='submit' id='${id}_setButton'>${set}</button>",
"<button data-dojo-type='dijit.form.Button' type='button' id='${id}_cancelButton'>${buttonCancel}</button>",
"</td></tr></table>"
].join(""),
// htmlTemplate: [protected] String
// String used for templating the <img> HTML to insert at the desired point.
htmlTemplate: "<img src=\"${urlInput}\" _djrealurl=\"${urlInput}\" alt=\"${textInput}\" />",
// tag: [protected] String
// Tag used for the link type (img).
tag: "img",
_getCurrentValues: function(img){
// summary:
// Over-ride for getting the values to set in the dropdown.
// a:
// The anchor/link to process for data for the dropdown.
// tags:
// protected
var url, text;
if(img && img.tagName.toLowerCase() === this.tag){
url = img.getAttribute('_djrealurl') || img.getAttribute('src');
text = img.getAttribute('alt');
win.withGlobal(this.editor.window,
"selectElement", selectionapi, [img, true]);
}else{
text = win.withGlobal(this.editor.window, selectionapi.getSelectedText);
}
return {urlInput: url || '', textInput: text || ''}; //Object;
},
_isValid: function(){
// summary:
// Over-ride for images. You can have alt text of blank, it is valid.
// tags:
// protected
return this._urlInput.isValid();
},
_connectTagEvents: function(){
// summary:
// Over-ridable function that connects tag specific events.
this.inherited(arguments);
this.editor.onLoadDeferred.addCallback(lang.hitch(this, function(){
// Use onmousedown instead of onclick. Seems that IE eats the first onclick
// to wrap it in a selector box, then the second one acts as onclick. See #10420
this.connect(this.editor.editNode, "onmousedown", this._selectTag);
}));
},
_selectTag: function(e){
// summary:
// A simple event handler that lets me select an image if it is clicked on.
// makes it easier to select images in a standard way across browsers. Otherwise
// selecting an image for edit becomes difficult.
// e: Event
// The mousedown event.
// tags:
// private
if(e && e.target){
var t = e.target;
var tg = t.tagName? t.tagName.toLowerCase() : "";
if(tg === this.tag){
win.withGlobal(this.editor.window,
"selectElement",
selectionapi, [t]);
}
}
},
_checkValues: function(args){
// summary:
// Function to check the values in args and 'fix' them up as needed
// (special characters in the url or alt text)
// args: Object
// Content being set.
// tags:
// protected
if(args && args.urlInput){
args.urlInput = args.urlInput.replace(/"/g, "&quot;");
}
if(args && args.textInput){
args.textInput = args.textInput.replace(/"/g, "&quot;");
}
return args;
},
_onDblClick: function(e){
// summary:
// Function to define a behavior on double clicks on the element
// type this dialog edits to select it and pop up the editor
// dialog.
// e: Object
// The double-click event.
// tags:
// protected.
if(e && e.target){
var t = e.target;
var tg = t.tagName ? t.tagName.toLowerCase() : "";
if(tg === this.tag && domAttr.get(t,"src")){
var editor = this.editor;
win.withGlobal(editor.window,
"selectElement",
selectionapi, [t]);
editor.onDisplayChanged();
// Call onNormalizedDisplayChange() now, rather than on timer.
// On IE, when focus goes to the first <input> in the TooltipDialog, the editor loses it's selection.
// Later if onNormalizedDisplayChange() gets called via the timer it will disable the LinkDialog button
// (actually, all the toolbar buttons), at which point clicking the <input> will close the dialog,
// since (for unknown reasons) focus.js ignores disabled controls.
if(editor._updateTimer){
clearTimeout(editor._updateTimer);
delete editor._updateTimer;
}
editor.onNormalizedDisplayChanged();
var button = this.button;
setTimeout(function(){
// Focus shift outside the event handler.
// IE doesn't like focus changes in event handles.
button.set("disabled", false);
button.loadAndOpenDropDown().then(function(){
if(button.dropDown.focus){
button.dropDown.focus();
}
});
}, 10);
}
}
}
});
// Register these plugins
_Plugin.registry["createLink"] = function(){
return new LinkDialog({command: "createLink"});
};
_Plugin.registry["insertImage"] = function(){
return new ImgLinkDialog({command: "insertImage"});
};
// Export both LinkDialog and ImgLinkDialog
LinkDialog.ImgLinkDialog = ImgLinkDialog;
return LinkDialog;
});
},
'dijit/DropDownMenu':function(){
define([
"dojo/_base/declare", // declare
"dojo/_base/event", // event.stop
"dojo/keys", // keys
"dojo/text!./templates/Menu.html",
"./_OnDijitClickMixin",
"./_MenuBase"
], function(declare, event, keys, template, _OnDijitClickMixin, _MenuBase){
/*=====
var _MenuBase = dijit._MenuBase;
var _OnDijitClickMixin = dijit._OnDijitClickMixin;
=====*/
// module:
// dijit/DropDownMenu
// summary:
// dijit.DropDownMenu widget
return declare("dijit.DropDownMenu", [_MenuBase, _OnDijitClickMixin], {
// summary:
// A menu, without features for context menu (Meaning, drop down menu)
templateString: template,
baseClass: "dijitMenu",
postCreate: function(){
var l = this.isLeftToRight();
this._openSubMenuKey = l ? keys.RIGHT_ARROW : keys.LEFT_ARROW;
this._closeSubMenuKey = l ? keys.LEFT_ARROW : keys.RIGHT_ARROW;
this.connectKeyNavHandlers([keys.UP_ARROW], [keys.DOWN_ARROW]);
},
_onKeyPress: function(/*Event*/ evt){
// summary:
// Handle keyboard based menu navigation.
// tags:
// protected
if(evt.ctrlKey || evt.altKey){ return; }
switch(evt.charOrCode){
case this._openSubMenuKey:
this._moveToPopup(evt);
event.stop(evt);
break;
case this._closeSubMenuKey:
if(this.parentMenu){
if(this.parentMenu._isMenuBar){
this.parentMenu.focusPrev();
}else{
this.onCancel(false);
}
}else{
event.stop(evt);
}
break;
}
}
});
});
},
'dijit/Menu':function(){
define("dijit/Menu", [
"require",
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/_base/event", // event.stop
"dojo/dom", // dom.byId dom.isDescendant
"dojo/dom-attr", // domAttr.get domAttr.set domAttr.has domAttr.remove
"dojo/dom-geometry", // domStyle.getComputedStyle domGeometry.position
"dojo/dom-style", // domStyle.getComputedStyle
"dojo/_base/kernel",
"dojo/keys", // keys.F10
"dojo/_base/lang", // lang.hitch
"dojo/on",
"dojo/_base/sniff", // has("ie"), has("quirks")
"dojo/_base/window", // win.body win.doc.documentElement win.doc.frames win.withGlobal
"dojo/window", // winUtils.get
"./popup",
"./DropDownMenu",
"dojo/ready"
], function(require, array, declare, event, dom, domAttr, domGeometry, domStyle, kernel, keys, lang, on,
has, win, winUtils, pm, DropDownMenu, ready){
/*=====
var DropDownMenu = dijit.DropDownMenu;
=====*/
// module:
// dijit/Menu
// summary:
// Includes dijit.Menu widget and base class dijit._MenuBase
// Back compat w/1.6, remove for 2.0
if(!kernel.isAsync){
ready(0, function(){
var requires = ["dijit/MenuItem", "dijit/PopupMenuItem", "dijit/CheckedMenuItem", "dijit/MenuSeparator"];
require(requires); // use indirection so modules not rolled into a build
});
}
return declare("dijit.Menu", DropDownMenu, {
// summary:
// A context menu you can assign to multiple elements
constructor: function(){
this._bindings = [];
},
// targetNodeIds: [const] String[]
// Array of dom node ids of nodes to attach to.
// Fill this with nodeIds upon widget creation and it becomes context menu for those nodes.
targetNodeIds: [],
// contextMenuForWindow: [const] Boolean
// If true, right clicking anywhere on the window will cause this context menu to open.
// If false, must specify targetNodeIds.
contextMenuForWindow: false,
// leftClickToOpen: [const] Boolean
// If true, menu will open on left click instead of right click, similar to a file menu.
leftClickToOpen: false,
// refocus: Boolean
// When this menu closes, re-focus the element which had focus before it was opened.
refocus: true,
postCreate: function(){
if(this.contextMenuForWindow){
this.bindDomNode(win.body());
}else{
// TODO: should have _setTargetNodeIds() method to handle initialization and a possible
// later set('targetNodeIds', ...) call. There's also a problem that targetNodeIds[]
// gets stale after calls to bindDomNode()/unBindDomNode() as it still is just the original list (see #9610)
array.forEach(this.targetNodeIds, this.bindDomNode, this);
}
this.inherited(arguments);
},
// thanks burstlib!
_iframeContentWindow: function(/* HTMLIFrameElement */iframe_el){
// summary:
// Returns the window reference of the passed iframe
// tags:
// private
return winUtils.get(this._iframeContentDocument(iframe_el)) ||
// Moz. TODO: is this available when defaultView isn't?
this._iframeContentDocument(iframe_el)['__parent__'] ||
(iframe_el.name && win.doc.frames[iframe_el.name]) || null; // Window
},
_iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){
// summary:
// Returns a reference to the document object inside iframe_el
// tags:
// protected
return iframe_el.contentDocument // W3
|| (iframe_el.contentWindow && iframe_el.contentWindow.document) // IE
|| (iframe_el.name && win.doc.frames[iframe_el.name] && win.doc.frames[iframe_el.name].document)
|| null; // HTMLDocument
},
bindDomNode: function(/*String|DomNode*/ node){
// summary:
// Attach menu to given node
node = dom.byId(node);
var cn; // Connect node
// Support context menus on iframes. Rather than binding to the iframe itself we need
// to bind to the <body> node inside the iframe.
if(node.tagName.toLowerCase() == "iframe"){
var iframe = node,
window = this._iframeContentWindow(iframe);
cn = win.withGlobal(window, win.body);
}else{
// To capture these events at the top level, attach to <html>, not <body>.
// Otherwise right-click context menu just doesn't work.
cn = (node == win.body() ? win.doc.documentElement : node);
}
// "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode())
var binding = {
node: node,
iframe: iframe
};
// Save info about binding in _bindings[], and make node itself record index(+1) into
// _bindings[] array. Prefix w/_dijitMenu to avoid setting an attribute that may
// start with a number, which fails on FF/safari.
domAttr.set(node, "_dijitMenu" + this.id, this._bindings.push(binding));
// Setup the connections to monitor click etc., unless we are connecting to an iframe which hasn't finished
// loading yet, in which case we need to wait for the onload event first, and then connect
// On linux Shift-F10 produces the oncontextmenu event, but on Windows it doesn't, so
// we need to monitor keyboard events in addition to the oncontextmenu event.
var doConnects = lang.hitch(this, function(cn){
return [
// TODO: when leftClickToOpen is true then shouldn't space/enter key trigger the menu,
// rather than shift-F10?
on(cn, this.leftClickToOpen ? "click" : "contextmenu", lang.hitch(this, function(evt){
// Schedule context menu to be opened unless it's already been scheduled from onkeydown handler
event.stop(evt);
this._scheduleOpen(evt.target, iframe, {x: evt.pageX, y: evt.pageY});
})),
on(cn, "keydown", lang.hitch(this, function(evt){
if(evt.shiftKey && evt.keyCode == keys.F10){
event.stop(evt);
this._scheduleOpen(evt.target, iframe); // no coords - open near target node
}
}))
];
});
binding.connects = cn ? doConnects(cn) : [];
if(iframe){
// Setup handler to [re]bind to the iframe when the contents are initially loaded,
// and every time the contents change.
// Need to do this b/c we are actually binding to the iframe's <body> node.
// Note: can't use connect.connect(), see #9609.
binding.onloadHandler = lang.hitch(this, function(){
// want to remove old connections, but IE throws exceptions when trying to
// access the <body> node because it's already gone, or at least in a state of limbo
var window = this._iframeContentWindow(iframe);
cn = win.withGlobal(window, win.body);
binding.connects = doConnects(cn);
});
if(iframe.addEventListener){
iframe.addEventListener("load", binding.onloadHandler, false);
}else{
iframe.attachEvent("onload", binding.onloadHandler);
}
}
},
unBindDomNode: function(/*String|DomNode*/ nodeName){
// summary:
// Detach menu from given node
var node;
try{
node = dom.byId(nodeName);
}catch(e){
// On IE the dom.byId() call will get an exception if the attach point was
// the <body> node of an <iframe> that has since been reloaded (and thus the
// <body> node is in a limbo state of destruction.
return;
}
// node["_dijitMenu" + this.id] contains index(+1) into my _bindings[] array
var attrName = "_dijitMenu" + this.id;
if(node && domAttr.has(node, attrName)){
var bid = domAttr.get(node, attrName)-1, b = this._bindings[bid], h;
while(h = b.connects.pop()){
h.remove();
}
// Remove listener for iframe onload events
var iframe = b.iframe;
if(iframe){
if(iframe.removeEventListener){
iframe.removeEventListener("load", b.onloadHandler, false);
}else{
iframe.detachEvent("onload", b.onloadHandler);
}
}
domAttr.remove(node, attrName);
delete this._bindings[bid];
}
},
_scheduleOpen: function(/*DomNode?*/ target, /*DomNode?*/ iframe, /*Object?*/ coords){
// summary:
// Set timer to display myself. Using a timer rather than displaying immediately solves
// two problems:
//
// 1. IE: without the delay, focus work in "open" causes the system
// context menu to appear in spite of stopEvent.
//
// 2. Avoid double-shows on linux, where shift-F10 generates an oncontextmenu event
// even after a event.stop(e). (Shift-F10 on windows doesn't generate the
// oncontextmenu event.)
if(!this._openTimer){
this._openTimer = setTimeout(lang.hitch(this, function(){
delete this._openTimer;
this._openMyself({
target: target,
iframe: iframe,
coords: coords
});
}), 1);
}
},
_openMyself: function(args){
// summary:
// Internal function for opening myself when the user does a right-click or something similar.
// args:
// This is an Object containing:
// * target:
// The node that is being clicked
// * iframe:
// If an <iframe> is being clicked, iframe points to that iframe
// * coords:
// Put menu at specified x/y position in viewport, or if iframe is
// specified, then relative to iframe.
//
// _openMyself() formerly took the event object, and since various code references
// evt.target (after connecting to _openMyself()), using an Object for parameters
// (so that old code still works).
var target = args.target,
iframe = args.iframe,
coords = args.coords;
// Get coordinates to open menu, either at specified (mouse) position or (if triggered via keyboard)
// then near the node the menu is assigned to.
if(coords){
if(iframe){
// Specified coordinates are on <body> node of an <iframe>, convert to match main document
var ifc = domGeometry.position(iframe, true),
window = this._iframeContentWindow(iframe),
scroll = win.withGlobal(window, "_docScroll", dojo);
var cs = domStyle.getComputedStyle(iframe),
tp = domStyle.toPixelValue,
left = (has("ie") && has("quirks") ? 0 : tp(iframe, cs.paddingLeft)) + (has("ie") && has("quirks") ? tp(iframe, cs.borderLeftWidth) : 0),
top = (has("ie") && has("quirks") ? 0 : tp(iframe, cs.paddingTop)) + (has("ie") && has("quirks") ? tp(iframe, cs.borderTopWidth) : 0);
coords.x += ifc.x + left - scroll.x;
coords.y += ifc.y + top - scroll.y;
}
}else{
coords = domGeometry.position(target, true);
coords.x += 10;
coords.y += 10;
}
var self=this;
var prevFocusNode = this._focusManager.get("prevNode");
var curFocusNode = this._focusManager.get("curNode");
var savedFocusNode = !curFocusNode || (dom.isDescendant(curFocusNode, this.domNode)) ? prevFocusNode : curFocusNode;
function closeAndRestoreFocus(){
// user has clicked on a menu or popup
if(self.refocus && savedFocusNode){
savedFocusNode.focus();
}
pm.close(self);
}
pm.open({
popup: this,
x: coords.x,
y: coords.y,
onExecute: closeAndRestoreFocus,
onCancel: closeAndRestoreFocus,
orient: this.isLeftToRight() ? 'L' : 'R'
});
this.focus();
this._onBlur = function(){
this.inherited('_onBlur', arguments);
// Usually the parent closes the child widget but if this is a context
// menu then there is no parent
pm.close(this);
// don't try to restore focus; user has clicked another part of the screen
// and set focus there
};
},
uninitialize: function(){
array.forEach(this._bindings, function(b){ if(b){ this.unBindDomNode(b.node); } }, this);
this.inherited(arguments);
}
});
});
},
'dijit/form/_CheckBoxMixin':function(){
define([
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/_base/event" // event.stop
], function(declare, domAttr, event){
// module:
// dijit/form/_CheckBoxMixin
// summary:
// Mixin to provide widget functionality corresponding to an HTML checkbox
return declare("dijit.form._CheckBoxMixin", null, {
// summary:
// Mixin to provide widget functionality corresponding to an HTML checkbox
//
// description:
// User interacts with real html inputs.
// On onclick (which occurs by mouse click, space-bar, or
// using the arrow keys to switch the selected radio button),
// we update the state of the checkbox/radio.
//
// type: [private] String
// type attribute on <input> node.
// Overrides `dijit.form.Button.type`. Users should not change this value.
type: "checkbox",
// value: String
// As an initialization parameter, equivalent to value field on normal checkbox
// (if checked, the value is passed as the value when form is submitted).
value: "on",
// readOnly: Boolean
// Should this widget respond to user input?
// In markup, this is specified as "readOnly".
// Similar to disabled except readOnly form values are submitted.
readOnly: false,
// aria-pressed for toggle buttons, and aria-checked for checkboxes
_aria_attr: "aria-checked",
_setReadOnlyAttr: function(/*Boolean*/ value){
this._set("readOnly", value);
domAttr.set(this.focusNode, 'readOnly', value);
this.focusNode.setAttribute("aria-readonly", value);
},
// Override dijit.form.Button._setLabelAttr() since we don't even have a containerNode.
// Normally users won't try to set label, except when CheckBox or RadioButton is the child of a dojox.layout.TabContainer
_setLabelAttr: undefined,
postMixInProperties: function(){
if(this.value == ""){
this.value = "on";
}
this.inherited(arguments);
},
reset: function(){
this.inherited(arguments);
// Handle unlikely event that the <input type=checkbox> value attribute has changed
this._set("value", this.params.value || "on");
domAttr.set(this.focusNode, 'value', this.value);
},
_onClick: function(/*Event*/ e){
// summary:
// Internal function to handle click actions - need to check
// readOnly, since button no longer does that check.
if(this.readOnly){
event.stop(e);
return false;
}
return this.inherited(arguments);
}
});
});
},
'dijit/layout/ContentPane':function(){
define([
"dojo/_base/kernel", // kernel.deprecated
"dojo/_base/lang", // lang.mixin lang.delegate lang.hitch lang.isFunction lang.isObject
"../_Widget",
"./_ContentPaneResizeMixin",
"dojo/string", // string.substitute
"dojo/html", // html._ContentSetter html._emptyNode
"dojo/i18n!../nls/loading",
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/_base/Deferred", // Deferred
"dojo/dom", // dom.byId
"dojo/dom-attr", // domAttr.attr
"dojo/_base/window", // win.body win.doc.createDocumentFragment
"dojo/_base/xhr", // xhr.get
"dojo/i18n" // i18n.getLocalization
], function(kernel, lang, _Widget, _ContentPaneResizeMixin, string, html, nlsLoading,
array, declare, Deferred, dom, domAttr, win, xhr, i18n){
/*=====
var _Widget = dijit._Widget;
var _ContentPaneResizeMixin = dijit.layout._ContentPaneResizeMixin;
=====*/
// module:
// dijit/layout/ContentPane
// summary:
// A widget containing an HTML fragment, specified inline
// or by uri. Fragment may include widgets.
return declare("dijit.layout.ContentPane", [_Widget, _ContentPaneResizeMixin], {
// summary:
// A widget containing an HTML fragment, specified inline
// or by uri. Fragment may include widgets.
//
// description:
// This widget embeds a document fragment in the page, specified
// either by uri, javascript generated markup or DOM reference.
// Any widgets within this content are instantiated and managed,
// but laid out according to the HTML structure. Unlike IFRAME,
// ContentPane embeds a document fragment as would be found
// inside the BODY tag of a full HTML document. It should not
// contain the HTML, HEAD, or BODY tags.
// For more advanced functionality with scripts and
// stylesheets, see dojox.layout.ContentPane. This widget may be
// used stand alone or as a base class for other widgets.
// ContentPane is useful as a child of other layout containers
// such as BorderContainer or TabContainer, but note that those
// widgets can contain any widget as a child.
//
// example:
// Some quick samples:
// To change the innerHTML: cp.set('content', '<b>new content</b>')
//
// Or you can send it a NodeList: cp.set('content', dojo.query('div [class=selected]', userSelection))
//
// To do an ajax update: cp.set('href', url)
// href: String
// The href of the content that displays now.
// Set this at construction if you want to load data externally when the
// pane is shown. (Set preload=true to load it immediately.)
// Changing href after creation doesn't have any effect; Use set('href', ...);
href: "",
// content: String || DomNode || NodeList || dijit._Widget
// The innerHTML of the ContentPane.
// Note that the initialization parameter / argument to set("content", ...)
// can be a String, DomNode, Nodelist, or _Widget.
content: "",
// extractContent: Boolean
// Extract visible content from inside of <body> .... </body>.
// I.e., strip <html> and <head> (and it's contents) from the href
extractContent: false,
// parseOnLoad: Boolean
// Parse content and create the widgets, if any.
parseOnLoad: true,
// parserScope: String
// Flag passed to parser. Root for attribute names to search for. If scopeName is dojo,
// will search for data-dojo-type (or dojoType). For backwards compatibility
// reasons defaults to dojo._scopeName (which is "dojo" except when
// multi-version support is used, when it will be something like dojo16, dojo20, etc.)
parserScope: kernel._scopeName,
// preventCache: Boolean
// Prevent caching of data from href's by appending a timestamp to the href.
preventCache: false,
// preload: Boolean
// Force load of data on initialization even if pane is hidden.
preload: false,
// refreshOnShow: Boolean
// Refresh (re-download) content when pane goes from hidden to shown
refreshOnShow: false,
// loadingMessage: String
// Message that shows while downloading
loadingMessage: "<span class='dijitContentPaneLoading'><span class='dijitInline dijitIconLoading'></span>${loadingState}</span>",
// errorMessage: String
// Message that shows if an error occurs
errorMessage: "<span class='dijitContentPaneError'><span class='dijitInline dijitIconError'></span>${errorState}</span>",
// isLoaded: [readonly] Boolean
// True if the ContentPane has data in it, either specified
// during initialization (via href or inline content), or set
// via set('content', ...) / set('href', ...)
//
// False if it doesn't have any content, or if ContentPane is
// still in the process of downloading href.
isLoaded: false,
baseClass: "dijitContentPane",
/*======
// ioMethod: dojo.xhrGet|dojo.xhrPost
// Function that should grab the content specified via href.
ioMethod: dojo.xhrGet,
======*/
// ioArgs: Object
// Parameters to pass to xhrGet() request, for example:
// | <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="href: './bar', ioArgs: {timeout: 500}">
ioArgs: {},
// onLoadDeferred: [readonly] dojo.Deferred
// This is the `dojo.Deferred` returned by set('href', ...) and refresh().
// Calling onLoadDeferred.addCallback() or addErrback() registers your
// callback to be called only once, when the prior set('href', ...) call or
// the initial href parameter to the constructor finishes loading.
//
// This is different than an onLoad() handler which gets called any time any href
// or content is loaded.
onLoadDeferred: null,
// Cancel _WidgetBase's _setTitleAttr because we don't want the title attribute (used to specify
// tab labels) to be copied to ContentPane.domNode... otherwise a tooltip shows up over the
// entire pane.
_setTitleAttr: null,
// Flag to parser that I'll parse my contents, so it shouldn't.
stopParser: true,
// template: [private] Boolean
// Flag from the parser that this ContentPane is inside a template
// so the contents are pre-parsed.
// (TODO: this declaration can be commented out in 2.0)
template: false,
create: function(params, srcNodeRef){
// Convert a srcNodeRef argument into a content parameter, so that the original contents are
// processed in the same way as contents set via set("content", ...), calling the parser etc.
// Avoid modifying original params object since that breaks NodeList instantiation, see #11906.
if((!params || !params.template) && srcNodeRef && !("href" in params) && !("content" in params)){
var df = win.doc.createDocumentFragment();
srcNodeRef = dom.byId(srcNodeRef);
while(srcNodeRef.firstChild){
df.appendChild(srcNodeRef.firstChild);
}
params = lang.delegate(params, {content: df});
}
this.inherited(arguments, [params, srcNodeRef]);
},
postMixInProperties: function(){
this.inherited(arguments);
var messages = i18n.getLocalization("dijit", "loading", this.lang);
this.loadingMessage = string.substitute(this.loadingMessage, messages);
this.errorMessage = string.substitute(this.errorMessage, messages);
},
buildRendering: function(){
this.inherited(arguments);
// Since we have no template we need to set this.containerNode ourselves, to make getChildren() work.
// For subclasses of ContentPane that do have a template, does nothing.
if(!this.containerNode){
this.containerNode = this.domNode;
}
// remove the title attribute so it doesn't show up when hovering
// over a node (TODO: remove in 2.0, no longer needed after #11490)
this.domNode.title = "";
if(!domAttr.get(this.domNode,"role")){
this.domNode.setAttribute("role", "group");
}
},
startup: function(){
// summary:
// Call startup() on all children including non _Widget ones like dojo.dnd.Source objects
// This starts all the widgets
this.inherited(arguments);
// And this catches stuff like dojo.dnd.Source
if(this._contentSetter){
array.forEach(this._contentSetter.parseResults, function(obj){
if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){
obj.startup();
obj._started = true;
}
}, this);
}
},
setHref: function(/*String|Uri*/ href){
// summary:
// Deprecated. Use set('href', ...) instead.
kernel.deprecated("dijit.layout.ContentPane.setHref() is deprecated. Use set('href', ...) instead.", "", "2.0");
return this.set("href", href);
},
_setHrefAttr: function(/*String|Uri*/ href){
// summary:
// Hook so set("href", ...) works.
// description:
// Reset the (external defined) content of this pane and replace with new url
// Note: It delays the download until widget is shown if preload is false.
// href:
// url to the page you want to get, must be within the same domain as your mainpage
// Cancel any in-flight requests (a set('href', ...) will cancel any in-flight set('href', ...))
this.cancel();
this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel"));
this.onLoadDeferred.addCallback(lang.hitch(this, "onLoad"));
this._set("href", href);
// _setHrefAttr() is called during creation and by the user, after creation.
// Assuming preload == false, only in the second case do we actually load the URL;
// otherwise it's done in startup(), and only if this widget is shown.
if(this.preload || (this._created && this._isShown())){
this._load();
}else{
// Set flag to indicate that href needs to be loaded the next time the
// ContentPane is made visible
this._hrefChanged = true;
}
return this.onLoadDeferred; // Deferred
},
setContent: function(/*String|DomNode|Nodelist*/data){
// summary:
// Deprecated. Use set('content', ...) instead.
kernel.deprecated("dijit.layout.ContentPane.setContent() is deprecated. Use set('content', ...) instead.", "", "2.0");
this.set("content", data);
},
_setContentAttr: function(/*String|DomNode|Nodelist*/data){
// summary:
// Hook to make set("content", ...) work.
// Replaces old content with data content, include style classes from old content
// data:
// the new Content may be String, DomNode or NodeList
//
// if data is a NodeList (or an array of nodes) nodes are copied
// so you can import nodes from another document implicitly
// clear href so we can't run refresh and clear content
// refresh should only work if we downloaded the content
this._set("href", "");
// Cancel any in-flight requests (a set('content', ...) will cancel any in-flight set('href', ...))
this.cancel();
// Even though user is just setting content directly, still need to define an onLoadDeferred
// because the _onLoadHandler() handler is still getting called from setContent()
this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel"));
if(this._created){
// For back-compat reasons, call onLoad() for set('content', ...)
// calls but not for content specified in srcNodeRef (ie: <div data-dojo-type=ContentPane>...</div>)
// or as initialization parameter (ie: new ContentPane({content: ...})
this.onLoadDeferred.addCallback(lang.hitch(this, "onLoad"));
}
this._setContent(data || "");
this._isDownloaded = false; // mark that content is from a set('content') not a set('href')
return this.onLoadDeferred; // Deferred
},
_getContentAttr: function(){
// summary:
// Hook to make get("content") work
return this.containerNode.innerHTML;
},
cancel: function(){
// summary:
// Cancels an in-flight download of content
if(this._xhrDfd && (this._xhrDfd.fired == -1)){
this._xhrDfd.cancel();
}
delete this._xhrDfd; // garbage collect
this.onLoadDeferred = null;
},
uninitialize: function(){
if(this._beingDestroyed){
this.cancel();
}
this.inherited(arguments);
},
destroyRecursive: function(/*Boolean*/ preserveDom){
// summary:
// Destroy the ContentPane and its contents
// if we have multiple controllers destroying us, bail after the first
if(this._beingDestroyed){
return;
}
this.inherited(arguments);
},
_onShow: function(){
// summary:
// Called when the ContentPane is made visible
// description:
// For a plain ContentPane, this is called on initialization, from startup().
// If the ContentPane is a hidden pane of a TabContainer etc., then it's
// called whenever the pane is made visible.
//
// Does necessary processing, including href download and layout/resize of
// child widget(s)
this.inherited(arguments);
if(this.href){
if(!this._xhrDfd && // if there's an href that isn't already being loaded
(!this.isLoaded || this._hrefChanged || this.refreshOnShow)
){
return this.refresh(); // If child has an href, promise that fires when the load is complete
}
}
},
refresh: function(){
// summary:
// [Re]download contents of href and display
// description:
// 1. cancels any currently in-flight requests
// 2. posts "loading..." message
// 3. sends XHR to download new data
// Cancel possible prior in-flight request
this.cancel();
this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel"));
this.onLoadDeferred.addCallback(lang.hitch(this, "onLoad"));
this._load();
return this.onLoadDeferred; // If child has an href, promise that fires when refresh is complete
},
_load: function(){
// summary:
// Load/reload the href specified in this.href
// display loading message
this._setContent(this.onDownloadStart(), true);
var self = this;
var getArgs = {
preventCache: (this.preventCache || this.refreshOnShow),
url: this.href,
handleAs: "text"
};
if(lang.isObject(this.ioArgs)){
lang.mixin(getArgs, this.ioArgs);
}
var hand = (this._xhrDfd = (this.ioMethod || xhr.get)(getArgs));
hand.addCallback(function(html){
try{
self._isDownloaded = true;
self._setContent(html, false);
self.onDownloadEnd();
}catch(err){
self._onError('Content', err); // onContentError
}
delete self._xhrDfd;
return html;
});
hand.addErrback(function(err){
if(!hand.canceled){
// show error message in the pane
self._onError('Download', err); // onDownloadError
}
delete self._xhrDfd;
return err;
});
// Remove flag saying that a load is needed
delete this._hrefChanged;
},
_onLoadHandler: function(data){
// summary:
// This is called whenever new content is being loaded
this._set("isLoaded", true);
try{
this.onLoadDeferred.callback(data);
}catch(e){
console.error('Error '+this.widgetId+' running custom onLoad code: ' + e.message);
}
},
_onUnloadHandler: function(){
// summary:
// This is called whenever the content is being unloaded
this._set("isLoaded", false);
try{
this.onUnload();
}catch(e){
console.error('Error '+this.widgetId+' running custom onUnload code: ' + e.message);
}
},
destroyDescendants: function(/*Boolean*/ preserveDom){
// summary:
// Destroy all the widgets inside the ContentPane and empty containerNode
// Make sure we call onUnload (but only when the ContentPane has real content)
if(this.isLoaded){
this._onUnloadHandler();
}
// Even if this.isLoaded == false there might still be a "Loading..." message
// to erase, so continue...
// For historical reasons we need to delete all widgets under this.containerNode,
// even ones that the user has created manually.
var setter = this._contentSetter;
array.forEach(this.getChildren(), function(widget){
if(widget.destroyRecursive){
widget.destroyRecursive(preserveDom);
}
});
if(setter){
// Most of the widgets in setter.parseResults have already been destroyed, but
// things like Menu that have been moved to <body> haven't yet
array.forEach(setter.parseResults, function(widget){
if(widget.destroyRecursive && widget.domNode && widget.domNode.parentNode == win.body()){
widget.destroyRecursive(preserveDom);
}
});
delete setter.parseResults;
}
// And then clear away all the DOM nodes
if(!preserveDom){
html._emptyNode(this.containerNode);
}
// Delete any state information we have about current contents
delete this._singleChild;
},
_setContent: function(/*String|DocumentFragment*/ cont, /*Boolean*/ isFakeContent){
// summary:
// Insert the content into the container node
// first get rid of child widgets
this.destroyDescendants();
// html.set will take care of the rest of the details
// we provide an override for the error handling to ensure the widget gets the errors
// configure the setter instance with only the relevant widget instance properties
// NOTE: unless we hook into attr, or provide property setters for each property,
// we need to re-configure the ContentSetter with each use
var setter = this._contentSetter;
if(! (setter && setter instanceof html._ContentSetter)){
setter = this._contentSetter = new html._ContentSetter({
node: this.containerNode,
_onError: lang.hitch(this, this._onError),
onContentError: lang.hitch(this, function(e){
// fires if a domfault occurs when we are appending this.errorMessage
// like for instance if domNode is a UL and we try append a DIV
var errMess = this.onContentError(e);
try{
this.containerNode.innerHTML = errMess;
}catch(e){
console.error('Fatal '+this.id+' could not change content due to '+e.message, e);
}
})/*,
_onError */
});
}
var setterParams = lang.mixin({
cleanContent: this.cleanContent,
extractContent: this.extractContent,
parseContent: !cont.domNode && this.parseOnLoad,
parserScope: this.parserScope,
startup: false,
dir: this.dir,
lang: this.lang,
textDir: this.textDir
}, this._contentSetterParams || {});
setter.set( (lang.isObject(cont) && cont.domNode) ? cont.domNode : cont, setterParams );
// setter params must be pulled afresh from the ContentPane each time
delete this._contentSetterParams;
if(this.doLayout){
this._checkIfSingleChild();
}
if(!isFakeContent){
if(this._started){
// Startup each top level child widget (and they will start their children, recursively)
delete this._started;
this.startup();
// Call resize() on each of my child layout widgets,
// or resize() on my single child layout widget...
// either now (if I'm currently visible) or when I become visible
this._scheduleLayout();
}
this._onLoadHandler(cont);
}
},
_onError: function(type, err, consoleText){
this.onLoadDeferred.errback(err);
// shows user the string that is returned by on[type]Error
// override on[type]Error and return your own string to customize
var errText = this['on' + type + 'Error'].call(this, err);
if(consoleText){
console.error(consoleText, err);
}else if(errText){// a empty string won't change current content
this._setContent(errText, true);
}
},
// EVENT's, should be overide-able
onLoad: function(/*===== data =====*/){
// summary:
// Event hook, is called after everything is loaded and widgetified
// tags:
// callback
},
onUnload: function(){
// summary:
// Event hook, is called before old content is cleared
// tags:
// callback
},
onDownloadStart: function(){
// summary:
// Called before download starts.
// description:
// The string returned by this function will be the html
// that tells the user we are loading something.
// Override with your own function if you want to change text.
// tags:
// extension
return this.loadingMessage;
},
onContentError: function(/*Error*/ /*===== error =====*/){
// summary:
// Called on DOM faults, require faults etc. in content.
//
// In order to display an error message in the pane, return
// the error message from this method, as an HTML string.
//
// By default (if this method is not overriden), it returns
// nothing, so the error message is just printed to the console.
// tags:
// extension
},
onDownloadError: function(/*Error*/ /*===== error =====*/){
// summary:
// Called when download error occurs.
//
// In order to display an error message in the pane, return
// the error message from this method, as an HTML string.
//
// Default behavior (if this method is not overriden) is to display
// the error message inside the pane.
// tags:
// extension
return this.errorMessage;
},
onDownloadEnd: function(){
// summary:
// Called when download is finished.
// tags:
// callback
}
});
});
},
'url:dijit/form/templates/ValidationTextBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\"\n\tid=\"widget_${id}\" role=\"presentation\"\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n",
'url:dijit/form/templates/TextBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\" id=\"widget_${id}\" role=\"presentation\"\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n",
'dijit/_KeyNavContainer':function(){
define([
"dojo/_base/kernel", // kernel.deprecated
"./_Container",
"./_FocusMixin",
"dojo/_base/array", // array.forEach
"dojo/keys", // keys.END keys.HOME
"dojo/_base/declare", // declare
"dojo/_base/event", // event.stop
"dojo/dom-attr", // domAttr.set
"dojo/_base/lang" // lang.hitch
], function(kernel, _Container, _FocusMixin, array, keys, declare, event, domAttr, lang){
/*=====
var _FocusMixin = dijit._FocusMixin;
var _Container = dijit._Container;
=====*/
// module:
// dijit/_KeyNavContainer
// summary:
// A _Container with keyboard navigation of its children.
return declare("dijit._KeyNavContainer", [_FocusMixin, _Container], {
// summary:
// A _Container with keyboard navigation of its children.
// description:
// To use this mixin, call connectKeyNavHandlers() in
// postCreate().
// It provides normalized keyboard and focusing code for Container
// widgets.
/*=====
// focusedChild: [protected] Widget
// The currently focused child widget, or null if there isn't one
focusedChild: null,
=====*/
// tabIndex: Integer
// Tab index of the container; same as HTML tabIndex attribute.
// Note then when user tabs into the container, focus is immediately
// moved to the first item in the container.
tabIndex: "0",
connectKeyNavHandlers: function(/*keys[]*/ prevKeyCodes, /*keys[]*/ nextKeyCodes){
// summary:
// Call in postCreate() to attach the keyboard handlers
// to the container.
// preKeyCodes: keys[]
// Key codes for navigating to the previous child.
// nextKeyCodes: keys[]
// Key codes for navigating to the next child.
// tags:
// protected
// TODO: call this automatically from my own postCreate()
var keyCodes = (this._keyNavCodes = {});
var prev = lang.hitch(this, "focusPrev");
var next = lang.hitch(this, "focusNext");
array.forEach(prevKeyCodes, function(code){ keyCodes[code] = prev; });
array.forEach(nextKeyCodes, function(code){ keyCodes[code] = next; });
keyCodes[keys.HOME] = lang.hitch(this, "focusFirstChild");
keyCodes[keys.END] = lang.hitch(this, "focusLastChild");
this.connect(this.domNode, "onkeypress", "_onContainerKeypress");
this.connect(this.domNode, "onfocus", "_onContainerFocus");
},
startupKeyNavChildren: function(){
kernel.deprecated("startupKeyNavChildren() call no longer needed", "", "2.0");
},
startup: function(){
this.inherited(arguments);
array.forEach(this.getChildren(), lang.hitch(this, "_startupChild"));
},
addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){
this.inherited(arguments);
this._startupChild(widget);
},
focus: function(){
// summary:
// Default focus() implementation: focus the first child.
this.focusFirstChild();
},
focusFirstChild: function(){
// summary:
// Focus the first focusable child in the container.
// tags:
// protected
this.focusChild(this._getFirstFocusableChild());
},
focusLastChild: function(){
// summary:
// Focus the last focusable child in the container.
// tags:
// protected
this.focusChild(this._getLastFocusableChild());
},
focusNext: function(){
// summary:
// Focus the next widget
// tags:
// protected
this.focusChild(this._getNextFocusableChild(this.focusedChild, 1));
},
focusPrev: function(){
// summary:
// Focus the last focusable node in the previous widget
// (ex: go to the ComboButton icon section rather than button section)
// tags:
// protected
this.focusChild(this._getNextFocusableChild(this.focusedChild, -1), true);
},
focusChild: function(/*dijit._Widget*/ widget, /*Boolean*/ last){
// summary:
// Focus specified child widget.
// widget:
// Reference to container's child widget
// last:
// If true and if widget has multiple focusable nodes, focus the
// last one instead of the first one
// tags:
// protected
if(!widget){ return; }
if(this.focusedChild && widget !== this.focusedChild){
this._onChildBlur(this.focusedChild); // used by _MenuBase
}
widget.set("tabIndex", this.tabIndex); // for IE focus outline to appear, must set tabIndex before focs
widget.focus(last ? "end" : "start");
this._set("focusedChild", widget);
},
_startupChild: function(/*dijit._Widget*/ widget){
// summary:
// Setup for each child widget
// description:
// Sets tabIndex=-1 on each child, so that the tab key will
// leave the container rather than visiting each child.
// tags:
// private
widget.set("tabIndex", "-1");
this.connect(widget, "_onFocus", function(){
// Set valid tabIndex so tabbing away from widget goes to right place, see #10272
widget.set("tabIndex", this.tabIndex);
});
this.connect(widget, "_onBlur", function(){
widget.set("tabIndex", "-1");
});
},
_onContainerFocus: function(evt){
// summary:
// Handler for when the container gets focus
// description:
// Initially the container itself has a tabIndex, but when it gets
// focus, switch focus to first child...
// tags:
// private
// Note that we can't use _onFocus() because switching focus from the
// _onFocus() handler confuses the focus.js code
// (because it causes _onFocusNode() to be called recursively)
// Also, _onFocus() would fire when focus went directly to a child widget due to mouse click.
// Ignore spurious focus events:
// 1. focus on a child widget bubbles on FF
// 2. on IE, clicking the scrollbar of a select dropdown moves focus from the focused child item to me
if(evt.target !== this.domNode || this.focusedChild){ return; }
this.focusFirstChild();
// and then set the container's tabIndex to -1,
// (don't remove as that breaks Safari 4)
// so that tab or shift-tab will go to the fields after/before
// the container, rather than the container itself
domAttr.set(this.domNode, "tabIndex", "-1");
},
_onBlur: function(evt){
// When focus is moved away the container, and its descendant (popup) widgets,
// then restore the container's tabIndex so that user can tab to it again.
// Note that using _onBlur() so that this doesn't happen when focus is shifted
// to one of my child widgets (typically a popup)
if(this.tabIndex){
domAttr.set(this.domNode, "tabIndex", this.tabIndex);
}
this.focusedChild = null;
this.inherited(arguments);
},
_onContainerKeypress: function(evt){
// summary:
// When a key is pressed, if it's an arrow key etc. then
// it's handled here.
// tags:
// private
if(evt.ctrlKey || evt.altKey){ return; }
var func = this._keyNavCodes[evt.charOrCode];
if(func){
func();
event.stop(evt);
}
},
_onChildBlur: function(/*dijit._Widget*/ /*===== widget =====*/){
// summary:
// Called when focus leaves a child widget to go
// to a sibling widget.
// Used by MenuBase.js (TODO: move code there)
// tags:
// protected
},
_getFirstFocusableChild: function(){
// summary:
// Returns first child that can be focused
return this._getNextFocusableChild(null, 1); // dijit._Widget
},
_getLastFocusableChild: function(){
// summary:
// Returns last child that can be focused
return this._getNextFocusableChild(null, -1); // dijit._Widget
},
_getNextFocusableChild: function(child, dir){
// summary:
// Returns the next or previous focusable child, compared
// to "child"
// child: Widget
// The current widget
// dir: Integer
// * 1 = after
// * -1 = before
if(child){
child = this._getSiblingOfChild(child, dir);
}
var children = this.getChildren();
for(var i=0; i < children.length; i++){
if(!child){
child = children[(dir>0) ? 0 : (children.length-1)];
}
if(child.isFocusable()){
return child; // dijit._Widget
}
child = this._getSiblingOfChild(child, dir);
}
// no focusable child found
return null; // dijit._Widget
}
});
});
},
'dijit/layout/utils':function(){
define([
"dojo/_base/array", // array.filter array.forEach
"dojo/dom-class", // domClass.add domClass.remove
"dojo/dom-geometry", // domGeometry.marginBox
"dojo/dom-style", // domStyle.getComputedStyle
"dojo/_base/lang", // lang.mixin
".." // for exporting symbols to dijit, remove in 2.0
], function(array, domClass, domGeometry, domStyle, lang, dijit){
// module:
// dijit/layout/utils
// summary:
// marginBox2contentBox() and layoutChildren()
var layout = lang.getObject("layout", true, dijit);
/*===== layout = dijit.layout =====*/
layout.marginBox2contentBox = function(/*DomNode*/ node, /*Object*/ mb){
// summary:
// Given the margin-box size of a node, return its content box size.
// Functions like domGeometry.contentBox() but is more reliable since it doesn't have
// to wait for the browser to compute sizes.
var cs = domStyle.getComputedStyle(node);
var me = domGeometry.getMarginExtents(node, cs);
var pb = domGeometry.getPadBorderExtents(node, cs);
return {
l: domStyle.toPixelValue(node, cs.paddingLeft),
t: domStyle.toPixelValue(node, cs.paddingTop),
w: mb.w - (me.w + pb.w),
h: mb.h - (me.h + pb.h)
};
};
function capitalize(word){
return word.substring(0,1).toUpperCase() + word.substring(1);
}
function size(widget, dim){
// size the child
var newSize = widget.resize ? widget.resize(dim) : domGeometry.setMarginBox(widget.domNode, dim);
// record child's size
if(newSize){
// if the child returned it's new size then use that
lang.mixin(widget, newSize);
}else{
// otherwise, call getMarginBox(), but favor our own numbers when we have them.
// the browser lies sometimes
lang.mixin(widget, domGeometry.getMarginBox(widget.domNode));
lang.mixin(widget, dim);
}
}
layout.layoutChildren = function(/*DomNode*/ container, /*Object*/ dim, /*Widget[]*/ children,
/*String?*/ changedRegionId, /*Number?*/ changedRegionSize){
// summary:
// Layout a bunch of child dom nodes within a parent dom node
// container:
// parent node
// dim:
// {l, t, w, h} object specifying dimensions of container into which to place children
// children:
// an array of Widgets or at least objects containing:
// * domNode: pointer to DOM node to position
// * region or layoutAlign: position to place DOM node
// * resize(): (optional) method to set size of node
// * id: (optional) Id of widgets, referenced from resize object, below.
// changedRegionId:
// If specified, the slider for the region with the specified id has been dragged, and thus
// the region's height or width should be adjusted according to changedRegionSize
// changedRegionSize:
// See changedRegionId.
// copy dim because we are going to modify it
dim = lang.mixin({}, dim);
domClass.add(container, "dijitLayoutContainer");
// Move "client" elements to the end of the array for layout. a11y dictates that the author
// needs to be able to put them in the document in tab-order, but this algorithm requires that
// client be last. TODO: move these lines to LayoutContainer? Unneeded other places I think.
children = array.filter(children, function(item){ return item.region != "center" && item.layoutAlign != "client"; })
.concat(array.filter(children, function(item){ return item.region == "center" || item.layoutAlign == "client"; }));
// set positions/sizes
array.forEach(children, function(child){
var elm = child.domNode,
pos = (child.region || child.layoutAlign);
if(!pos){
throw new Error("No region setting for " + child.id)
}
// set elem to upper left corner of unused space; may move it later
var elmStyle = elm.style;
elmStyle.left = dim.l+"px";
elmStyle.top = dim.t+"px";
elmStyle.position = "absolute";
domClass.add(elm, "dijitAlign" + capitalize(pos));
// Size adjustments to make to this child widget
var sizeSetting = {};
// Check for optional size adjustment due to splitter drag (height adjustment for top/bottom align
// panes and width adjustment for left/right align panes.
if(changedRegionId && changedRegionId == child.id){
sizeSetting[child.region == "top" || child.region == "bottom" ? "h" : "w"] = changedRegionSize;
}
// set size && adjust record of remaining space.
// note that setting the width of a <div> may affect its height.
if(pos == "top" || pos == "bottom"){
sizeSetting.w = dim.w;
size(child, sizeSetting);
dim.h -= child.h;
if(pos == "top"){
dim.t += child.h;
}else{
elmStyle.top = dim.t + dim.h + "px";
}
}else if(pos == "left" || pos == "right"){
sizeSetting.h = dim.h;
size(child, sizeSetting);
dim.w -= child.w;
if(pos == "left"){
dim.l += child.w;
}else{
elmStyle.left = dim.l + dim.w + "px";
}
}else if(pos == "client" || pos == "center"){
size(child, dim);
}
});
};
return {
marginBox2contentBox: layout.marginBox2contentBox,
layoutChildren: layout.layoutChildren
};
});
},
'dijit/form/DataList':function(){
define([
"dojo/_base/declare", // declare
"dojo/dom", // dom.byId
"dojo/_base/lang", // lang.trim
"dojo/query", // query
"dojo/store/Memory", // dojo.store.Memory
"../registry" // registry.add registry.remove
], function(declare, dom, lang, query, MemoryStore, registry){
// module:
// dijit/form/DataList
// summary:
// Inefficient but small data store specialized for inlined data via OPTION tags
function toItem(/*DOMNode*/ option){
// summary:
// Convert <option> node to hash
return {
id: option.value,
value: option.value,
name: lang.trim(option.innerText || option.textContent || '')
};
}
return declare("dijit.form.DataList", MemoryStore, {
// summary:
// Inefficient but small data store specialized for inlined data via OPTION tags
//
// description:
// Provides a store for inlined data like:
//
// | <datalist>
// | <option value="AL">Alabama</option>
// | ...
constructor: function(/*Object?*/ params, /*DomNode|String*/ srcNodeRef){
// store pointer to original DOM tree
this.domNode = dom.byId(srcNodeRef);
lang.mixin(this, params);
if(this.id){
registry.add(this); // add to registry so it can be easily found by id
}
this.domNode.style.display = "none";
this.inherited(arguments, [{
data: query("option", this.domNode).map(toItem)
}]);
},
destroy: function(){
registry.remove(this.id);
},
fetchSelectedItem: function(){
// summary:
// Get the option marked as selected, like `<option selected>`.
// Not part of dojo.data API.
var option = query("> option[selected]", this.domNode)[0] || query("> option", this.domNode)[0];
return option && toItem(option);
}
});
});
},
'url:dijit/templates/Dialog.html':"<div class=\"dijitDialog\" role=\"dialog\" aria-labelledby=\"${id}_title\">\n\t<div data-dojo-attach-point=\"titleBar\" class=\"dijitDialogTitleBar\">\n\t<span data-dojo-attach-point=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"></span>\n\t<span data-dojo-attach-point=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" data-dojo-attach-event=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabIndex=\"-1\">\n\t\t<span data-dojo-attach-point=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t</span>\n\t</div>\n\t\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n</div>\n",
'dijit/_editor/_Plugin':function(){
define([
"dojo/_base/connect", // connect.connect
"dojo/_base/declare", // declare
"dojo/_base/lang", // lang.mixin, lang.hitch
"../form/Button"
], function(connect, declare, lang, Button){
// module:
// dijit/_editor/_Plugin
// summary:
// Base class for a "plugin" to the editor, which is usually
// a single button on the Toolbar and some associated code
var _Plugin = declare("dijit._editor._Plugin", null, {
// summary:
// Base class for a "plugin" to the editor, which is usually
// a single button on the Toolbar and some associated code
constructor: function(/*Object?*/args){
this.params = args || {};
lang.mixin(this, this.params);
this._connects=[];
this._attrPairNames = {};
},
// editor: [const] dijit.Editor
// Points to the parent editor
editor: null,
// iconClassPrefix: [const] String
// The CSS class name for the button node is formed from `iconClassPrefix` and `command`
iconClassPrefix: "dijitEditorIcon",
// button: dijit._Widget?
// Pointer to `dijit.form.Button` or other widget (ex: `dijit.form.FilteringSelect`)
// that is added to the toolbar to control this plugin.
// If not specified, will be created on initialization according to `buttonClass`
button: null,
// command: String
// String like "insertUnorderedList", "outdent", "justifyCenter", etc. that represents an editor command.
// Passed to editor.execCommand() if `useDefaultCommand` is true.
command: "",
// useDefaultCommand: Boolean
// If true, this plugin executes by calling Editor.execCommand() with the argument specified in `command`.
useDefaultCommand: true,
// buttonClass: Widget Class
// Class of widget (ex: dijit.form.Button or dijit.form.FilteringSelect)
// that is added to the toolbar to control this plugin.
// This is used to instantiate the button, unless `button` itself is specified directly.
buttonClass: Button,
// disabled: Boolean
// Flag to indicate if this plugin has been disabled and should do nothing
// helps control button state, among other things. Set via the setter api.
disabled: false,
getLabel: function(/*String*/key){
// summary:
// Returns the label to use for the button
// tags:
// private
return this.editor.commands[key]; // String
},
_initButton: function(){
// summary:
// Initialize the button or other widget that will control this plugin.
// This code only works for plugins controlling built-in commands in the editor.
// tags:
// protected extension
if(this.command.length){
var label = this.getLabel(this.command),
editor = this.editor,
className = this.iconClassPrefix+" "+this.iconClassPrefix + this.command.charAt(0).toUpperCase() + this.command.substr(1);
if(!this.button){
var props = lang.mixin({
label: label,
dir: editor.dir,
lang: editor.lang,
showLabel: false,
iconClass: className,
dropDown: this.dropDown,
tabIndex: "-1"
}, this.params || {});
this.button = new this.buttonClass(props);
}
}
if(this.get("disabled") && this.button){
this.button.set("disabled", this.get("disabled"));
}
},
destroy: function(){
// summary:
// Destroy this plugin
var h;
while(h = this._connects.pop()){ h.remove(); }
if(this.dropDown){
this.dropDown.destroyRecursive();
}
},
connect: function(o, f, tf){
// summary:
// Make a connect.connect() that is automatically disconnected when this plugin is destroyed.
// Similar to `dijit._Widget.connect`.
// tags:
// protected
this._connects.push(connect.connect(o, f, this, tf));
},
updateState: function(){
// summary:
// Change state of the plugin to respond to events in the editor.
// description:
// This is called on meaningful events in the editor, such as change of selection
// or caret position (but not simple typing of alphanumeric keys). It gives the
// plugin a chance to update the CSS of its button.
//
// For example, the "bold" plugin will highlight/unhighlight the bold button depending on whether the
// characters next to the caret are bold or not.
//
// Only makes sense when `useDefaultCommand` is true, as it calls Editor.queryCommandEnabled(`command`).
var e = this.editor,
c = this.command,
checked, enabled;
if(!e || !e.isLoaded || !c.length){ return; }
var disabled = this.get("disabled");
if(this.button){
try{
enabled = !disabled && e.queryCommandEnabled(c);
if(this.enabled !== enabled){
this.enabled = enabled;
this.button.set('disabled', !enabled);
}
if(typeof this.button.checked == 'boolean'){
checked = e.queryCommandState(c);
if(this.checked !== checked){
this.checked = checked;
this.button.set('checked', e.queryCommandState(c));
}
}
}catch(e){
console.log(e); // FIXME: we shouldn't have debug statements in our code. Log as an error?
}
}
},
setEditor: function(/*dijit.Editor*/ editor){
// summary:
// Tell the plugin which Editor it is associated with.
// TODO: refactor code to just pass editor to constructor.
// FIXME: detach from previous editor!!
this.editor = editor;
// FIXME: prevent creating this if we don't need to (i.e., editor can't handle our command)
this._initButton();
// Processing for buttons that execute by calling editor.execCommand()
if(this.button && this.useDefaultCommand){
if(this.editor.queryCommandAvailable(this.command)){
this.connect(this.button, "onClick",
lang.hitch(this.editor, "execCommand", this.command, this.commandArg)
);
}else{
// hide button because editor doesn't support command (due to browser limitations)
this.button.domNode.style.display = "none";
}
}
this.connect(this.editor, "onNormalizedDisplayChanged", "updateState");
},
setToolbar: function(/*dijit.Toolbar*/ toolbar){
// summary:
// Tell the plugin to add it's controller widget (often a button)
// to the toolbar. Does nothing if there is no controller widget.
// TODO: refactor code to just pass toolbar to constructor.
if(this.button){
toolbar.addChild(this.button);
}
// console.debug("adding", this.button, "to:", toolbar);
},
set: function(/* attribute */ name, /* anything */ value){
// summary:
// Set a property on a plugin
// name:
// The property to set.
// value:
// The value to set in the property.
// description:
// Sets named properties on a plugin which may potentially be handled by a
// setter in the plugin.
// For example, if the plugin has a properties "foo"
// and "bar" and a method named "_setFooAttr", calling:
// | plugin.set("foo", "Howdy!");
// would be equivalent to writing:
// | plugin._setFooAttr("Howdy!");
// and:
// | plugin.set("bar", 3);
// would be equivalent to writing:
// | plugin.bar = 3;
//
// set() may also be called with a hash of name/value pairs, ex:
// | plugin.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);
if(this[names.s]){
// use the explicit setter
var result = this[names.s].apply(this, Array.prototype.slice.call(arguments, 1));
}else{
this._set(name, value);
}
return result || this;
},
get: function(name){
// summary:
// Get a property from a plugin.
// name:
// The property to get.
// description:
// Get a named property from a plugin. 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 plugin has a properties "foo"
// and "bar" and a method named "_getFooAttr", calling:
// | plugin.get("foo");
// would be equivalent to writing:
// | plugin._getFooAttr();
// and:
// | plugin.get("bar");
// would be equivalent to writing:
// | plugin.bar;
var names = this._getAttrNames(name);
return this[names.g] ? this[names.g]() : this[name];
},
_setDisabledAttr: function(disabled){
// summary:
// Function to set the plugin state and call updateState to make sure the
// button is updated appropriately.
this.disabled = disabled;
this.updateState();
},
_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.charAt(0).toUpperCase() + name.substr(1);
return (apn[name] = {
s: "_set"+uc+"Attr",
g: "_get"+uc+"Attr"
});
},
_set: function(/*String*/ name, /*anything*/ value){
// summary:
// Helper function to set new value for specified attribute
this[name] = value;
}
});
// Hash mapping plugin name to factory, used for registering plugins
_Plugin.registry = {};
return _Plugin;
});
},
'dijit/form/CheckBox':function(){
define([
"require",
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/_base/kernel",
"dojo/query", // query
"dojo/ready",
"./ToggleButton",
"./_CheckBoxMixin",
"dojo/text!./templates/CheckBox.html",
"dojo/NodeList-dom" // NodeList.addClass/removeClass
], function(require, declare, domAttr, kernel, query, ready, ToggleButton, _CheckBoxMixin, template){
/*=====
var ToggleButton = dijit.form.ToggleButton;
var _CheckBoxMixin = dijit.form._CheckBoxMixin;
=====*/
// module:
// dijit/form/CheckBox
// summary:
// Checkbox widget
// Back compat w/1.6, remove for 2.0
if(!kernel.isAsync){
ready(0, function(){
var requires = ["dijit/form/RadioButton"];
require(requires); // use indirection so modules not rolled into a build
});
}
return declare("dijit.form.CheckBox", [ToggleButton, _CheckBoxMixin], {
// summary:
// Same as an HTML checkbox, but with fancy styling.
//
// description:
// User interacts with real html inputs.
// On onclick (which occurs by mouse click, space-bar, or
// using the arrow keys to switch the selected radio button),
// we update the state of the checkbox/radio.
//
// There are two modes:
// 1. High contrast mode
// 2. Normal mode
//
// In case 1, the regular html inputs are shown and used by the user.
// In case 2, the regular html inputs are invisible but still used by
// the user. They are turned quasi-invisible and overlay the background-image.
templateString: template,
baseClass: "dijitCheckBox",
_setValueAttr: function(/*String|Boolean*/ newValue, /*Boolean*/ priorityChange){
// summary:
// Handler for value= attribute to constructor, and also calls to
// set('value', val).
// description:
// During initialization, just saves as attribute to the <input type=checkbox>.
//
// After initialization,
// when passed a boolean, controls whether or not the CheckBox is checked.
// If passed a string, changes the value attribute of the CheckBox (the one
// specified as "value" when the CheckBox was constructed (ex: <input
// data-dojo-type="dijit.CheckBox" value="chicken">)
// widget.set('value', string) will check the checkbox and change the value to the
// specified string
// widget.set('value', boolean) will change the checked state.
if(typeof newValue == "string"){
this._set("value", newValue);
domAttr.set(this.focusNode, 'value', newValue);
newValue = true;
}
if(this._created){
this.set('checked', newValue, priorityChange);
}
},
_getValueAttr: function(){
// summary:
// Hook so get('value') works.
// description:
// If the CheckBox is checked, returns the value attribute.
// Otherwise returns false.
return (this.checked ? this.value : false);
},
// Override behavior from Button, since we don't have an iconNode
_setIconClassAttr: null,
postMixInProperties: function(){
this.inherited(arguments);
// Need to set initial checked state as part of template, so that form submit works.
// domAttr.set(node, "checked", bool) doesn't work on IE until node has been attached
// to <body>, see #8666
this.checkedAttrSetting = this.checked ? "checked" : "";
},
_fillContent: function(){
// Override Button::_fillContent() since it doesn't make sense for CheckBox,
// since CheckBox doesn't even have a container
},
_onFocus: function(){
if(this.id){
query("label[for='"+this.id+"']").addClass("dijitFocusedLabel");
}
this.inherited(arguments);
},
_onBlur: function(){
if(this.id){
query("label[for='"+this.id+"']").removeClass("dijitFocusedLabel");
}
this.inherited(arguments);
}
});
});
},
'url:dijit/templates/MenuBar.html':"<div class=\"dijitMenuBar dijitMenuPassive\" data-dojo-attach-point=\"containerNode\" role=\"menubar\" tabIndex=\"${tabIndex}\" data-dojo-attach-event=\"onkeypress: _onKeyPress\"></div>\n",
'dijit/tree/_dndSelector':function(){
define([
"dojo/_base/array", // array.filter array.forEach array.map
"dojo/_base/connect", // connect.isCopyKey
"dojo/_base/declare", // declare
"dojo/_base/lang", // lang.hitch
"dojo/mouse", // mouse.isLeft
"dojo/on",
"dojo/touch",
"dojo/_base/window", // win.global
"./_dndContainer"
], function(array, connect, declare, lang, mouse, on, touch, win, _dndContainer){
// module:
// dijit/tree/_dndSelector
// summary:
// This is a base class for `dijit.tree.dndSource` , and isn't meant to be used directly.
// It's based on `dojo.dnd.Selector`.
return declare("dijit.tree._dndSelector", _dndContainer, {
// summary:
// This is a base class for `dijit.tree.dndSource` , and isn't meant to be used directly.
// It's based on `dojo.dnd.Selector`.
// tags:
// protected
/*=====
// selection: Hash<String, DomNode>
// (id, DomNode) map for every TreeNode that's currently selected.
// The DOMNode is the TreeNode.rowNode.
selection: {},
=====*/
constructor: function(){
// summary:
// Initialization
// tags:
// private
this.selection={};
this.anchor = null;
this.tree.domNode.setAttribute("aria-multiselect", !this.singular);
this.events.push(
on(this.tree.domNode, touch.press, lang.hitch(this,"onMouseDown")),
on(this.tree.domNode, touch.release, lang.hitch(this,"onMouseUp")),
on(this.tree.domNode, touch.move, lang.hitch(this,"onMouseMove"))
);
},
// singular: Boolean
// Allows selection of only one element, if true.
// Tree hasn't been tested in singular=true mode, unclear if it works.
singular: false,
// methods
getSelectedTreeNodes: function(){
// summary:
// Returns a list of selected node(s).
// Used by dndSource on the start of a drag.
// tags:
// protected
var nodes=[], sel = this.selection;
for(var i in sel){
nodes.push(sel[i]);
}
return nodes;
},
selectNone: function(){
// summary:
// Unselects all items
// tags:
// private
this.setSelection([]);
return this; // self
},
destroy: function(){
// summary:
// Prepares the object to be garbage-collected
this.inherited(arguments);
this.selection = this.anchor = null;
},
addTreeNode: function(/*dijit._TreeNode*/node, /*Boolean?*/isAnchor){
// summary:
// add node to current selection
// node: Node
// node to add
// isAnchor: Boolean
// Whether the node should become anchor.
this.setSelection(this.getSelectedTreeNodes().concat( [node] ));
if(isAnchor){ this.anchor = node; }
return node;
},
removeTreeNode: function(/*dijit._TreeNode*/node){
// summary:
// remove node from current selection
// node: Node
// node to remove
this.setSelection(this._setDifference(this.getSelectedTreeNodes(), [node]));
return node;
},
isTreeNodeSelected: function(/*dijit._TreeNode*/node){
// summary:
// return true if node is currently selected
// node: Node
// the node to check whether it's in the current selection
return node.id && !!this.selection[node.id];
},
setSelection: function(/*dijit._treeNode[]*/ newSelection){
// summary:
// set the list of selected nodes to be exactly newSelection. All changes to the
// selection should be passed through this function, which ensures that derived
// attributes are kept up to date. Anchor will be deleted if it has been removed
// from the selection, but no new anchor will be added by this function.
// newSelection: Node[]
// list of tree nodes to make selected
var oldSelection = this.getSelectedTreeNodes();
array.forEach(this._setDifference(oldSelection, newSelection), lang.hitch(this, function(node){
node.setSelected(false);
if(this.anchor == node){
delete this.anchor;
}
delete this.selection[node.id];
}));
array.forEach(this._setDifference(newSelection, oldSelection), lang.hitch(this, function(node){
node.setSelected(true);
this.selection[node.id] = node;
}));
this._updateSelectionProperties();
},
_setDifference: function(xs,ys){
// summary:
// Returns a copy of xs which lacks any objects
// occurring in ys. Checks for membership by
// modifying and then reading the object, so it will
// not properly handle sets of numbers or strings.
array.forEach(ys, function(y){ y.__exclude__ = true; });
var ret = array.filter(xs, function(x){ return !x.__exclude__; });
// clean up after ourselves.
array.forEach(ys, function(y){ delete y['__exclude__'] });
return ret;
},
_updateSelectionProperties: function(){
// summary:
// Update the following tree properties from the current selection:
// path[s], selectedItem[s], selectedNode[s]
var selected = this.getSelectedTreeNodes();
var paths = [], nodes = [];
array.forEach(selected, function(node){
nodes.push(node);
paths.push(node.getTreePath());
});
var items = array.map(nodes,function(node){ return node.item; });
this.tree._set("paths", paths);
this.tree._set("path", paths[0] || []);
this.tree._set("selectedNodes", nodes);
this.tree._set("selectedNode", nodes[0] || null);
this.tree._set("selectedItems", items);
this.tree._set("selectedItem", items[0] || null);
},
// mouse events
onMouseDown: function(e){
// summary:
// Event processor for onmousedown/ontouchstart
// e: Event
// onmousedown/ontouchstart event
// tags:
// protected
// ignore click on expando node
if(!this.current || this.tree.isExpandoNode(e.target, this.current)){ return; }
if(!mouse.isLeft(e)){ return; } // ignore right-click
e.preventDefault();
var treeNode = this.current,
copy = connect.isCopyKey(e), id = treeNode.id;
// if shift key is not pressed, and the node is already in the selection,
// delay deselection until onmouseup so in the case of DND, deselection
// will be canceled by onmousemove.
if(!this.singular && !e.shiftKey && this.selection[id]){
this._doDeselect = true;
return;
}else{
this._doDeselect = false;
}
this.userSelect(treeNode, copy, e.shiftKey);
},
onMouseUp: function(e){
// summary:
// Event processor for onmouseup/ontouchend
// e: Event
// onmouseup/ontouchend event
// tags:
// protected
// _doDeselect is the flag to indicate that the user wants to either ctrl+click on
// a already selected item (to deselect the item), or click on a not-yet selected item
// (which should remove all current selection, and add the clicked item). This can not
// be done in onMouseDown, because the user may start a drag after mousedown. By moving
// the deselection logic here, the user can drags an already selected item.
if(!this._doDeselect){ return; }
this._doDeselect = false;
this.userSelect(this.current, connect.isCopyKey(e), e.shiftKey);
},
onMouseMove: function(/*===== e =====*/){
// summary:
// event processor for onmousemove/ontouchmove
// e: Event
// onmousemove/ontouchmove event
this._doDeselect = false;
},
_compareNodes: function(n1, n2){
if(n1 === n2){
return 0;
}
if('sourceIndex' in document.documentElement){ //IE
//TODO: does not yet work if n1 and/or n2 is a text node
return n1.sourceIndex - n2.sourceIndex;
}else if('compareDocumentPosition' in document.documentElement){ //FF, Opera
return n1.compareDocumentPosition(n2) & 2 ? 1: -1;
}else if(document.createRange){ //Webkit
var r1 = doc.createRange();
r1.setStartBefore(n1);
var r2 = doc.createRange();
r2.setStartBefore(n2);
return r1.compareBoundaryPoints(r1.END_TO_END, r2);
}else{
throw Error("dijit.tree._compareNodes don't know how to compare two different nodes in this browser");
}
},
userSelect: function(node, multi, range){
// summary:
// Add or remove the given node from selection, responding
// to a user action such as a click or keypress.
// multi: Boolean
// Indicates whether this is meant to be a multi-select action (e.g. ctrl-click)
// range: Boolean
// Indicates whether this is meant to be a ranged action (e.g. shift-click)
// tags:
// protected
if(this.singular){
if(this.anchor == node && multi){
this.selectNone();
}else{
this.setSelection([node]);
this.anchor = node;
}
}else{
if(range && this.anchor){
var cr = this._compareNodes(this.anchor.rowNode, node.rowNode),
begin, end, anchor = this.anchor;
if(cr < 0){ //current is after anchor
begin = anchor;
end = node;
}else{ //current is before anchor
begin = node;
end = anchor;
}
var nodes = [];
//add everything betweeen begin and end inclusively
while(begin != end){
nodes.push(begin);
begin = this.tree._getNextNode(begin);
}
nodes.push(end);
this.setSelection(nodes);
}else{
if( this.selection[ node.id ] && multi ){
this.removeTreeNode( node );
}else if(multi){
this.addTreeNode(node, true);
}else{
this.setSelection([node]);
this.anchor = node;
}
}
}
},
getItem: function(/*String*/ key){
// summary:
// Returns the dojo.dnd.Item (representing a dragged node) by it's key (id).
// Called by dojo.dnd.Source.checkAcceptance().
// tags:
// protected
var widget = this.selection[key];
return {
data: widget,
type: ["treeNode"]
}; // dojo.dnd.Item
},
forInSelectedItems: function(/*Function*/ f, /*Object?*/ o){
// summary:
// Iterates over selected items;
// see `dojo.dnd.Container.forInItems()` for details
o = o || win.global;
for(var id in this.selection){
// console.log("selected item id: " + id);
f.call(o, this.getItem(id), id, this);
}
}
});
});
},
'dojo/html':function(){
define(["./_base/kernel", "./_base/lang", "./_base/array", "./_base/declare", "./dom", "./dom-construct", "./parser"], function(dojo, lang, darray, declare, dom, domConstruct, parser) {
// module:
// dojo/html
// summary:
// TODOC
lang.getObject("html", true, dojo);
// the parser might be needed..
// idCounter is incremented with each instantiation to allow asignment of a unique id for tracking, logging purposes
var idCounter = 0;
dojo.html._secureForInnerHtml = function(/*String*/ cont){
// summary:
// removes !DOCTYPE and title elements from the html string.
//
// khtml is picky about dom faults, you can't attach a style or <title> node as child of body
// must go into head, so we need to cut out those tags
// cont:
// An html string for insertion into the dom
//
return cont.replace(/(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig, ""); // String
};
/*====
dojo.html._emptyNode = function(node){
// summary:
// removes all child nodes from the given node
// node: DOMNode
// the parent element
};
=====*/
dojo.html._emptyNode = domConstruct.empty;
dojo.html._setNodeContent = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont){
// summary:
// inserts the given content into the given node
// node:
// the parent element
// content:
// the content to be set on the parent element.
// This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
// always empty
domConstruct.empty(node);
if(cont) {
if(typeof cont == "string") {
cont = domConstruct.toDom(cont, node.ownerDocument);
}
if(!cont.nodeType && lang.isArrayLike(cont)) {
// handle as enumerable, but it may shrink as we enumerate it
for(var startlen=cont.length, i=0; i<cont.length; i=startlen==cont.length ? i+1 : 0) {
domConstruct.place( cont[i], node, "last");
}
} else {
// pass nodes, documentFragments and unknowns through to dojo.place
domConstruct.place(cont, node, "last");
}
}
// return DomNode
return node;
};
// we wrap up the content-setting operation in a object
declare("dojo.html._ContentSetter", null,
{
// node: DomNode|String
// An node which will be the parent element that we set content into
node: "",
// content: String|DomNode|DomNode[]
// The content to be placed in the node. Can be an HTML string, a node reference, or a enumerable list of nodes
content: "",
// id: String?
// Usually only used internally, and auto-generated with each instance
id: "",
// cleanContent: Boolean
// Should the content be treated as a full html document,
// and the real content stripped of <html>, <body> wrapper before injection
cleanContent: false,
// extractContent: Boolean
// Should the content be treated as a full html document, and the real content stripped of <html>, <body> wrapper before injection
extractContent: false,
// parseContent: Boolean
// Should the node by passed to the parser after the new content is set
parseContent: false,
// parserScope: String
// Flag passed to parser. Root for attribute names to search for. If scopeName is dojo,
// will search for data-dojo-type (or dojoType). For backwards compatibility
// reasons defaults to dojo._scopeName (which is "dojo" except when
// multi-version support is used, when it will be something like dojo16, dojo20, etc.)
parserScope: dojo._scopeName,
// startup: Boolean
// Start the child widgets after parsing them. Only obeyed if parseContent is true.
startup: true,
// lifecyle methods
constructor: function(/* Object */params, /* String|DomNode */node){
// summary:
// Provides a configurable, extensible object to wrap the setting on content on a node
// call the set() method to actually set the content..
// the original params are mixed directly into the instance "this"
lang.mixin(this, params || {});
// give precedence to params.node vs. the node argument
// and ensure its a node, not an id string
node = this.node = dom.byId( this.node || node );
if(!this.id){
this.id = [
"Setter",
(node) ? node.id || node.tagName : "",
idCounter++
].join("_");
}
},
set: function(/* String|DomNode|NodeList? */ cont, /* Object? */ params){
// summary:
// front-end to the set-content sequence
// cont:
// An html string, node or enumerable list of nodes for insertion into the dom
// If not provided, the object's content property will be used
if(undefined !== cont){
this.content = cont;
}
// in the re-use scenario, set needs to be able to mixin new configuration
if(params){
this._mixin(params);
}
this.onBegin();
this.setContent();
this.onEnd();
return this.node;
},
setContent: function(){
// summary:
// sets the content on the node
var node = this.node;
if(!node) {
// can't proceed
throw new Error(this.declaredClass + ": setContent given no node");
}
try{
node = dojo.html._setNodeContent(node, this.content);
}catch(e){
// check if a domfault occurs when we are appending this.errorMessage
// like for instance if domNode is a UL and we try append a DIV
// FIXME: need to allow the user to provide a content error message string
var errMess = this.onContentError(e);
try{
node.innerHTML = errMess;
}catch(e){
console.error('Fatal ' + this.declaredClass + '.setContent could not change content due to '+e.message, e);
}
}
// always put back the node for the next method
this.node = node; // DomNode
},
empty: function() {
// summary
// cleanly empty out existing content
// destroy any widgets from a previous run
// NOTE: if you dont want this you'll need to empty
// the parseResults array property yourself to avoid bad things happenning
if(this.parseResults && this.parseResults.length) {
darray.forEach(this.parseResults, function(w) {
if(w.destroy){
w.destroy();
}
});
delete this.parseResults;
}
// this is fast, but if you know its already empty or safe, you could
// override empty to skip this step
dojo.html._emptyNode(this.node);
},
onBegin: function(){
// summary
// Called after instantiation, but before set();
// It allows modification of any of the object properties
// - including the node and content provided - before the set operation actually takes place
// This default implementation checks for cleanContent and extractContent flags to
// optionally pre-process html string content
var cont = this.content;
if(lang.isString(cont)){
if(this.cleanContent){
cont = dojo.html._secureForInnerHtml(cont);
}
if(this.extractContent){
var match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
if(match){ cont = match[1]; }
}
}
// clean out the node and any cruft associated with it - like widgets
this.empty();
this.content = cont;
return this.node; /* DomNode */
},
onEnd: function(){
// summary
// Called after set(), when the new content has been pushed into the node
// It provides an opportunity for post-processing before handing back the node to the caller
// This default implementation checks a parseContent flag to optionally run the dojo parser over the new content
if(this.parseContent){
// populates this.parseResults if you need those..
this._parse();
}
return this.node; /* DomNode */
},
tearDown: function(){
// summary
// manually reset the Setter instance if its being re-used for example for another set()
// description
// tearDown() is not called automatically.
// In normal use, the Setter instance properties are simply allowed to fall out of scope
// but the tearDown method can be called to explicitly reset this instance.
delete this.parseResults;
delete this.node;
delete this.content;
},
onContentError: function(err){
return "Error occured setting content: " + err;
},
_mixin: function(params){
// mix properties/methods into the instance
// TODO: the intention with tearDown is to put the Setter's state
// back to that of the original constructor (vs. deleting/resetting everything regardless of ctor params)
// so we could do something here to move the original properties aside for later restoration
var empty = {}, key;
for(key in params){
if(key in empty){ continue; }
// TODO: here's our opportunity to mask the properties we dont consider configurable/overridable
// .. but history shows we'll almost always guess wrong
this[key] = params[key];
}
},
_parse: function(){
// summary:
// runs the dojo parser over the node contents, storing any results in this.parseResults
// Any errors resulting from parsing are passed to _onError for handling
var rootNode = this.node;
try{
// store the results (widgets, whatever) for potential retrieval
var inherited = {};
darray.forEach(["dir", "lang", "textDir"], function(name){
if(this[name]){
inherited[name] = this[name];
}
}, this);
this.parseResults = parser.parse({
rootNode: rootNode,
noStart: !this.startup,
inherited: inherited,
scope: this.parserScope
});
}catch(e){
this._onError('Content', e, "Error parsing in _ContentSetter#"+this.id);
}
},
_onError: function(type, err, consoleText){
// summary:
// shows user the string that is returned by on[type]Error
// overide/implement on[type]Error and return your own string to customize
var errText = this['on' + type + 'Error'].call(this, err);
if(consoleText){
console.error(consoleText, err);
}else if(errText){ // a empty string won't change current content
dojo.html._setNodeContent(this.node, errText, true);
}
}
}); // end dojo.declare()
dojo.html.set = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont, /* Object? */ params){
// summary:
// inserts (replaces) the given content into the given node. dojo.place(cont, node, "only")
// may be a better choice for simple HTML insertion.
// description:
// Unless you need to use the params capabilities of this method, you should use
// dojo.place(cont, node, "only"). dojo.place() has more robust support for injecting
// an HTML string into the DOM, but it only handles inserting an HTML string as DOM
// elements, or inserting a DOM node. dojo.place does not handle NodeList insertions
// or the other capabilities as defined by the params object for this method.
// node:
// the parent element that will receive the content
// cont:
// the content to be set on the parent element.
// This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
// params:
// Optional flags/properties to configure the content-setting. See dojo.html._ContentSetter
// example:
// A safe string/node/nodelist content replacement/injection with hooks for extension
// Example Usage:
// dojo.html.set(node, "some string");
// dojo.html.set(node, contentNode, {options});
// dojo.html.set(node, myNode.childNodes, {options});
if(undefined == cont){
console.warn("dojo.html.set: no cont argument provided, using empty string");
cont = "";
}
if(!params){
// simple and fast
return dojo.html._setNodeContent(node, cont, true);
}else{
// more options but slower
// note the arguments are reversed in order, to match the convention for instantiation via the parser
var op = new dojo.html._ContentSetter(lang.mixin(
params,
{ content: cont, node: node }
));
return op.set();
}
};
return dojo.html;
});
},
'dijit/_PaletteMixin':function(){
define([
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/dom-class", // domClass.add domClass.remove
"dojo/dom-construct", // domConstruct.create domConstruct.place
"dojo/_base/event", // event.stop
"dojo/keys", // keys
"dojo/_base/lang", // lang.getObject
"./_CssStateMixin",
"./focus",
"./typematic"
], function(declare, domAttr, domClass, domConstruct, event, keys, lang, _CssStateMixin, focus, typematic){
/*=====
var _CssStateMixin = dijit._CssStateMixin;
=====*/
// module:
// dijit/_PaletteMixin
// summary:
// A keyboard accessible palette, for picking a color/emoticon/etc.
return declare("dijit._PaletteMixin", [_CssStateMixin], {
// summary:
// A keyboard accessible palette, for picking a color/emoticon/etc.
// description:
// A mixin for a grid showing various entities, so the user can pick a certain entity.
// defaultTimeout: Number
// Number of milliseconds before a held key or button becomes typematic
defaultTimeout: 500,
// timeoutChangeRate: Number
// Fraction of time used to change the typematic timer between events
// 1.0 means that each typematic event fires at defaultTimeout intervals
// < 1.0 means that each typematic event fires at an increasing faster rate
timeoutChangeRate: 0.90,
// value: String
// Currently selected color/emoticon/etc.
value: "",
// _selectedCell: [private] Integer
// Index of the currently selected cell. Initially, none selected
_selectedCell: -1,
/*=====
// _currentFocus: [private] DomNode
// The currently focused cell (if the palette itself has focus), or otherwise
// the cell to be focused when the palette itself gets focus.
// Different from value, which represents the selected (i.e. clicked) cell.
_currentFocus: null,
=====*/
/*=====
// _xDim: [protected] Integer
// This is the number of cells horizontally across.
_xDim: null,
=====*/
/*=====
// _yDim: [protected] Integer
// This is the number of cells vertically down.
_yDim: null,
=====*/
// tabIndex: String
// Widget tab index.
tabIndex: "0",
// cellClass: [protected] String
// CSS class applied to each cell in the palette
cellClass: "dijitPaletteCell",
// dyeClass: [protected] String
// Name of javascript class for Object created for each cell of the palette.
// dyeClass should implements dijit.Dye interface
dyeClass: '',
// summary: String
// Localized summary for the palette table
summary: '',
_setSummaryAttr: "paletteTableNode",
_dyeFactory: function(value /*===== , row, col =====*/){
// summary:
// Return instance of dijit.Dye for specified cell of palette
// tags:
// extension
var dyeClassObj = lang.getObject(this.dyeClass);
return new dyeClassObj(value);
},
_preparePalette: function(choices, titles) {
// summary:
// Subclass must call _preparePalette() from postCreate(), passing in the tooltip
// for each cell
// choices: String[][]
// id's for each cell of the palette, used to create Dye JS object for each cell
// titles: String[]
// Localized tooltip for each cell
this._cells = [];
var url = this._blankGif;
this.connect(this.gridNode, "ondijitclick", "_onCellClick");
for(var row=0; row < choices.length; row++){
var rowNode = domConstruct.create("tr", {tabIndex: "-1"}, this.gridNode);
for(var col=0; col < choices[row].length; col++){
var value = choices[row][col];
if(value){
var cellObject = this._dyeFactory(value, row, col);
var cellNode = domConstruct.create("td", {
"class": this.cellClass,
tabIndex: "-1",
title: titles[value],
role: "gridcell"
});
// prepare cell inner structure
cellObject.fillCell(cellNode, url);
domConstruct.place(cellNode, rowNode);
cellNode.index = this._cells.length;
// save cell info into _cells
this._cells.push({node:cellNode, dye:cellObject});
}
}
}
this._xDim = choices[0].length;
this._yDim = choices.length;
// Now set all events
// The palette itself is navigated to with the tab key on the keyboard
// Keyboard navigation within the Palette is with the arrow keys
// Spacebar selects the cell.
// For the up key the index is changed by negative the x dimension.
var keyIncrementMap = {
UP_ARROW: -this._xDim,
// The down key the index is increase by the x dimension.
DOWN_ARROW: this._xDim,
// Right and left move the index by 1.
RIGHT_ARROW: this.isLeftToRight() ? 1 : -1,
LEFT_ARROW: this.isLeftToRight() ? -1 : 1
};
for(var key in keyIncrementMap){
this._connects.push(
typematic.addKeyListener(
this.domNode,
{charOrCode:keys[key], ctrlKey:false, altKey:false, shiftKey:false},
this,
function(){
var increment = keyIncrementMap[key];
return function(count){ this._navigateByKey(increment, count); };
}(),
this.timeoutChangeRate,
this.defaultTimeout
)
);
}
},
postCreate: function(){
this.inherited(arguments);
// Set initial navigable node.
this._setCurrent(this._cells[0].node);
},
focus: function(){
// summary:
// Focus this widget. Puts focus on the most recently focused cell.
// The cell already has tabIndex set, just need to set CSS and focus it
focus.focus(this._currentFocus);
},
_onCellClick: function(/*Event*/ evt){
// summary:
// Handler for click, enter key & space key. Selects the cell.
// evt:
// The event.
// tags:
// private
var target = evt.target;
// Find TD associated with click event. For ColorPalette user likely clicked IMG inside of TD
while(target.tagName != "TD"){
if(!target.parentNode || target == this.gridNode){ // probably can never happen, but just in case
return;
}
target = target.parentNode;
}
var value = this._getDye(target).getValue();
// First focus the clicked cell, and then send onChange() notification.
// onChange() (via _setValueAttr) must be after the focus call, because
// it may trigger a refocus to somewhere else (like the Editor content area), and that
// second focus should win.
this._setCurrent(target);
focus.focus(target);
this._setValueAttr(value, true);
event.stop(evt);
},
_setCurrent: function(/*DomNode*/ node){
// summary:
// Sets which node is the focused cell.
// description:
// At any point in time there's exactly one
// cell with tabIndex != -1. If focus is inside the palette then
// focus is on that cell.
//
// After calling this method, arrow key handlers and mouse click handlers
// should focus the cell in a setTimeout().
// tags:
// protected
if("_currentFocus" in this){
// Remove tabIndex on old cell
domAttr.set(this._currentFocus, "tabIndex", "-1");
}
// Set tabIndex of new cell
this._currentFocus = node;
if(node){
domAttr.set(node, "tabIndex", this.tabIndex);
}
},
_setValueAttr: function(value, priorityChange){
// summary:
// This selects a cell. It triggers the onChange event.
// value: String value of the cell to select
// tags:
// protected
// priorityChange:
// Optional parameter used to tell the select whether or not to fire
// onChange event.
// clear old selected cell
if(this._selectedCell >= 0){
domClass.remove(this._cells[this._selectedCell].node, this.cellClass + "Selected");
}
this._selectedCell = -1;
// search for cell matching specified value
if(value){
for(var i = 0; i < this._cells.length; i++){
if(value == this._cells[i].dye.getValue()){
this._selectedCell = i;
domClass.add(this._cells[i].node, this.cellClass + "Selected");
break;
}
}
}
// record new value, or null if no matching cell
this._set("value", this._selectedCell >= 0 ? value : null);
if(priorityChange || priorityChange === undefined){
this.onChange(value);
}
},
onChange: function(/*===== value =====*/){
// summary:
// Callback when a cell is selected.
// value: String
// Value corresponding to cell.
},
_navigateByKey: function(increment, typeCount){
// summary:
// This is the callback for typematic.
// It changes the focus and the highlighed cell.
// increment:
// How much the key is navigated.
// typeCount:
// How many times typematic has fired.
// tags:
// private
// typecount == -1 means the key is released.
if(typeCount == -1){ return; }
var newFocusIndex = this._currentFocus.index + increment;
if(newFocusIndex < this._cells.length && newFocusIndex > -1){
var focusNode = this._cells[newFocusIndex].node;
this._setCurrent(focusNode);
// Actually focus the node, for the benefit of screen readers.
// Use setTimeout because IE doesn't like changing focus inside of an event handler
setTimeout(lang.hitch(dijit, "focus", focusNode), 0);
}
},
_getDye: function(/*DomNode*/ cell){
// summary:
// Get JS object for given cell DOMNode
return this._cells[cell.index].dye;
}
});
/*=====
declare("dijit.Dye",
null,
{
// summary:
// Interface for the JS Object associated with a palette cell (i.e. DOMNode)
constructor: function(alias, row, col){
// summary:
// Initialize according to value or alias like "white"
// alias: String
},
getValue: function(){
// summary:
// Return "value" of cell; meaning of "value" varies by subclass.
// description:
// For example color hex value, emoticon ascii value etc, entity hex value.
},
fillCell: function(cell, blankGif){
// summary:
// Add cell DOMNode inner structure
// cell: DomNode
// The surrounding cell
// blankGif: String
// URL for blank cell image
}
}
);
=====*/
});
},
'url:dijit/templates/TitlePane.html':"<div>\n\t<div data-dojo-attach-event=\"onclick:_onTitleClick, onkeypress:_onTitleKey\"\n\t\t\tclass=\"dijitTitlePaneTitle\" data-dojo-attach-point=\"titleBarNode\">\n\t\t<div class=\"dijitTitlePaneTitleFocus\" data-dojo-attach-point=\"focusNode\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"arrowNode\" class=\"dijitArrowNode\" role=\"presentation\"\n\t\t\t/><span data-dojo-attach-point=\"arrowNodeInner\" class=\"dijitArrowNodeInner\"></span\n\t\t\t><span data-dojo-attach-point=\"titleNode\" class=\"dijitTitlePaneTextNode\"></span>\n\t\t</div>\n\t</div>\n\t<div class=\"dijitTitlePaneContentOuter\" data-dojo-attach-point=\"hideNode\" role=\"presentation\">\n\t\t<div class=\"dijitReset\" data-dojo-attach-point=\"wipeNode\" role=\"presentation\">\n\t\t\t<div class=\"dijitTitlePaneContentInner\" data-dojo-attach-point=\"containerNode\" role=\"region\" id=\"${id}_pane\">\n\t\t\t\t<!-- nested divs because wipeIn()/wipeOut() doesn't work right on node w/padding etc. Put padding on inner div. -->\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n",
'dijit/form/ValidationTextBox':function(){
define([
"dojo/_base/declare", // declare
"dojo/i18n", // i18n.getLocalization
"./TextBox",
"../Tooltip",
"dojo/text!./templates/ValidationTextBox.html",
"dojo/i18n!./nls/validate"
], function(declare, i18n, TextBox, Tooltip, template){
/*=====
var Tooltip = dijit.Tooltip;
var TextBox = dijit.form.TextBox;
=====*/
// module:
// dijit/form/ValidationTextBox
// summary:
// Base class for textbox widgets with the ability to validate content of various types and provide user feedback.
/*=====
dijit.form.ValidationTextBox.__Constraints = function(){
// locale: String
// locale used for validation, picks up value from this widget's lang attribute
// _flags_: anything
// various flags passed to regExpGen function
this.locale = "";
this._flags_ = "";
}
=====*/
return declare("dijit.form.ValidationTextBox", TextBox, {
// summary:
// Base class for textbox widgets with the ability to validate content of various types and provide user feedback.
// tags:
// protected
templateString: template,
baseClass: "dijitTextBox dijitValidationTextBox",
// required: Boolean
// User is required to enter data into this field.
required: false,
// promptMessage: String
// If defined, display this hint string immediately on focus to the textbox, if empty.
// Also displays if the textbox value is Incomplete (not yet valid but will be with additional input).
// Think of this like a tooltip that tells the user what to do, not an error message
// that tells the user what they've done wrong.
//
// Message disappears when user starts typing.
promptMessage: "",
// invalidMessage: String
// The message to display if value is invalid.
// The translated string value is read from the message file by default.
// Set to "" to use the promptMessage instead.
invalidMessage: "$_unset_$",
// missingMessage: String
// The message to display if value is empty and the field is required.
// The translated string value is read from the message file by default.
// Set to "" to use the invalidMessage instead.
missingMessage: "$_unset_$",
// message: String
// Currently error/prompt message.
// When using the default tooltip implementation, this will only be
// displayed when the field is focused.
message: "",
// constraints: dijit.form.ValidationTextBox.__Constraints
// user-defined object needed to pass parameters to the validator functions
constraints: {},
// regExp: [extension protected] String
// regular expression string used to validate the input
// Do not specify both regExp and regExpGen
regExp: ".*",
regExpGen: function(/*dijit.form.ValidationTextBox.__Constraints*/ /*===== constraints =====*/){
// summary:
// Overridable function used to generate regExp when dependent on constraints.
// Do not specify both regExp and regExpGen.
// tags:
// extension protected
return this.regExp; // String
},
// state: [readonly] String
// Shows current state (ie, validation result) of input (""=Normal, Incomplete, or Error)
state: "",
// tooltipPosition: String[]
// See description of `dijit.Tooltip.defaultPosition` for details on this parameter.
tooltipPosition: [],
_setValueAttr: function(){
// summary:
// Hook so set('value', ...) works.
this.inherited(arguments);
this.validate(this.focused);
},
validator: function(/*anything*/ value, /*dijit.form.ValidationTextBox.__Constraints*/ constraints){
// summary:
// Overridable function used to validate the text input against the regular expression.
// tags:
// protected
return (new RegExp("^(?:" + this.regExpGen(constraints) + ")"+(this.required?"":"?")+"$")).test(value) &&
(!this.required || !this._isEmpty(value)) &&
(this._isEmpty(value) || this.parse(value, constraints) !== undefined); // Boolean
},
_isValidSubset: function(){
// summary:
// Returns true if the value is either already valid or could be made valid by appending characters.
// This is used for validation while the user [may be] still typing.
return this.textbox.value.search(this._partialre) == 0;
},
isValid: function(/*Boolean*/ /*===== isFocused =====*/){
// summary:
// Tests if value is valid.
// Can override with your own routine in a subclass.
// tags:
// protected
return this.validator(this.textbox.value, this.constraints);
},
_isEmpty: function(value){
// summary:
// Checks for whitespace
return (this.trim ? /^\s*$/ : /^$/).test(value); // Boolean
},
getErrorMessage: function(/*Boolean*/ /*===== isFocused =====*/){
// summary:
// Return an error message to show if appropriate
// tags:
// protected
return (this.required && this._isEmpty(this.textbox.value)) ? this.missingMessage : this.invalidMessage; // String
},
getPromptMessage: function(/*Boolean*/ /*===== isFocused =====*/){
// summary:
// Return a hint message to show when widget is first focused
// tags:
// protected
return this.promptMessage; // String
},
_maskValidSubsetError: true,
validate: function(/*Boolean*/ isFocused){
// summary:
// Called by oninit, onblur, and onkeypress.
// description:
// Show missing or invalid messages if appropriate, and highlight textbox field.
// tags:
// protected
var message = "";
var isValid = this.disabled || this.isValid(isFocused);
if(isValid){ this._maskValidSubsetError = true; }
var isEmpty = this._isEmpty(this.textbox.value);
var isValidSubset = !isValid && isFocused && this._isValidSubset();
this._set("state", isValid ? "" : (((((!this._hasBeenBlurred || isFocused) && isEmpty) || isValidSubset) && this._maskValidSubsetError) ? "Incomplete" : "Error"));
this.focusNode.setAttribute("aria-invalid", isValid ? "false" : "true");
if(this.state == "Error"){
this._maskValidSubsetError = isFocused && isValidSubset; // we want the error to show up after a blur and refocus
message = this.getErrorMessage(isFocused);
}else if(this.state == "Incomplete"){
message = this.getPromptMessage(isFocused); // show the prompt whenever the value is not yet complete
this._maskValidSubsetError = !this._hasBeenBlurred || isFocused; // no Incomplete warnings while focused
}else if(isEmpty){
message = this.getPromptMessage(isFocused); // show the prompt whenever there's no error and no text
}
this.set("message", message);
return isValid;
},
displayMessage: function(/*String*/ message){
// summary:
// Overridable method to display validation errors/hints.
// By default uses a tooltip.
// tags:
// extension
if(message && this.focused){
Tooltip.show(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
}else{
Tooltip.hide(this.domNode);
}
},
_refreshState: function(){
// Overrides TextBox._refreshState()
this.validate(this.focused);
this.inherited(arguments);
},
//////////// INITIALIZATION METHODS ///////////////////////////////////////
constructor: function(){
this.constraints = {};
},
_setConstraintsAttr: function(/*Object*/ constraints){
if(!constraints.locale && this.lang){
constraints.locale = this.lang;
}
this._set("constraints", constraints);
this._computePartialRE();
},
_computePartialRE: function(){
var p = this.regExpGen(this.constraints);
this.regExp = p;
var partialre = "";
// parse the regexp and produce a new regexp that matches valid subsets
// if the regexp is .* then there's no use in matching subsets since everything is valid
if(p != ".*"){ this.regExp.replace(/\\.|\[\]|\[.*?[^\\]{1}\]|\{.*?\}|\(\?[=:!]|./g,
function(re){
switch(re.charAt(0)){
case '{':
case '+':
case '?':
case '*':
case '^':
case '$':
case '|':
case '(':
partialre += re;
break;
case ")":
partialre += "|$)";
break;
default:
partialre += "(?:"+re+"|$)";
break;
}
}
);}
try{ // this is needed for now since the above regexp parsing needs more test verification
"".search(partialre);
}catch(e){ // should never be here unless the original RE is bad or the parsing is bad
partialre = this.regExp;
console.warn('RegExp error in ' + this.declaredClass + ': ' + this.regExp);
} // should never be here unless the original RE is bad or the parsing is bad
this._partialre = "^(?:" + partialre + ")$";
},
postMixInProperties: function(){
this.inherited(arguments);
this.messages = i18n.getLocalization("dijit.form", "validate", this.lang);
if(this.invalidMessage == "$_unset_$"){ this.invalidMessage = this.messages.invalidMessage; }
if(!this.invalidMessage){ this.invalidMessage = this.promptMessage; }
if(this.missingMessage == "$_unset_$"){ this.missingMessage = this.messages.missingMessage; }
if(!this.missingMessage){ this.missingMessage = this.invalidMessage; }
this._setConstraintsAttr(this.constraints); // this needs to happen now (and later) due to codependency on _set*Attr calls attachPoints
},
_setDisabledAttr: function(/*Boolean*/ value){
this.inherited(arguments); // call FormValueWidget._setDisabledAttr()
this._refreshState();
},
_setRequiredAttr: function(/*Boolean*/ value){
this._set("required", value);
this.focusNode.setAttribute("aria-required", value);
this._refreshState();
},
_setMessageAttr: function(/*String*/ message){
this._set("message", message);
this.displayMessage(message);
},
reset:function(){
// Overrides dijit.form.TextBox.reset() by also
// hiding errors about partial matches
this._maskValidSubsetError = true;
this.inherited(arguments);
},
_onBlur: function(){
// the message still exists but for back-compat, and to erase the tooltip
// (if the message is being displayed as a tooltip), call displayMessage('')
this.displayMessage('');
this.inherited(arguments);
}
});
});
},
'dijit/layout/BorderContainer':function(){
define([
"dojo/_base/array", // array.filter array.forEach array.map
"dojo/cookie", // cookie
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.add domClass.remove domClass.toggle
"dojo/dom-construct", // domConstruct.destroy domConstruct.place
"dojo/dom-geometry", // domGeometry.marginBox
"dojo/dom-style", // domStyle.style
"dojo/_base/event", // event.stop
"dojo/keys",
"dojo/_base/lang", // lang.getObject lang.hitch
"dojo/on",
"dojo/touch",
"dojo/_base/window", // win.body win.doc win.doc.createElement
"../_WidgetBase",
"../_Widget",
"../_TemplatedMixin",
"./_LayoutWidget",
"./utils" // layoutUtils.layoutChildren
], function(array, cookie, declare, domClass, domConstruct, domGeometry, domStyle, event, keys, lang, on, touch, win,
_WidgetBase, _Widget, _TemplatedMixin, _LayoutWidget, layoutUtils){
/*=====
var _WidgetBase = dijit._WidgetBase;
var _Widget = dijit._Widget;
var _TemplatedMixin = dijit._TemplatedMixin;
var _LayoutWidget = dijit.layout._LayoutWidget;
=====*/
// module:
// dijit/layout/BorderContainer
// summary:
// Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides.
var _Splitter = declare("dijit.layout._Splitter", [_Widget, _TemplatedMixin ],
{
// summary:
// A draggable spacer between two items in a `dijit.layout.BorderContainer`.
// description:
// This is instantiated by `dijit.layout.BorderContainer`. Users should not
// create it directly.
// tags:
// private
/*=====
// container: [const] dijit.layout.BorderContainer
// Pointer to the parent BorderContainer
container: null,
// child: [const] dijit.layout._LayoutWidget
// Pointer to the pane associated with this splitter
child: null,
// region: [const] String
// Region of pane associated with this splitter.
// "top", "bottom", "left", "right".
region: null,
=====*/
// live: [const] Boolean
// If true, the child's size changes and the child widget is redrawn as you drag the splitter;
// otherwise, the size doesn't change until you drop the splitter (by mouse-up)
live: true,
templateString: '<div class="dijitSplitter" data-dojo-attach-event="onkeypress:_onKeyPress,press:_startDrag,onmouseenter:_onMouse,onmouseleave:_onMouse" tabIndex="0" role="separator"><div class="dijitSplitterThumb"></div></div>',
constructor: function(){
this._handlers = [];
},
postMixInProperties: function(){
this.inherited(arguments);
this.horizontal = /top|bottom/.test(this.region);
this._factor = /top|left/.test(this.region) ? 1 : -1;
this._cookieName = this.container.id + "_" + this.region;
},
buildRendering: function(){
this.inherited(arguments);
domClass.add(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V"));
if(this.container.persist){
// restore old size
var persistSize = cookie(this._cookieName);
if(persistSize){
this.child.domNode.style[this.horizontal ? "height" : "width"] = persistSize;
}
}
},
_computeMaxSize: function(){
// summary:
// Return the maximum size that my corresponding pane can be set to
var dim = this.horizontal ? 'h' : 'w',
childSize = domGeometry.getMarginBox(this.child.domNode)[dim],
center = array.filter(this.container.getChildren(), function(child){ return child.region == "center";})[0],
spaceAvailable = domGeometry.getMarginBox(center.domNode)[dim]; // can expand until center is crushed to 0
return Math.min(this.child.maxSize, childSize + spaceAvailable);
},
_startDrag: function(e){
if(!this.cover){
this.cover = win.doc.createElement('div');
domClass.add(this.cover, "dijitSplitterCover");
domConstruct.place(this.cover, this.child.domNode, "after");
}
domClass.add(this.cover, "dijitSplitterCoverActive");
// Safeguard in case the stop event was missed. Shouldn't be necessary if we always get the mouse up.
if(this.fake){ domConstruct.destroy(this.fake); }
if(!(this._resize = this.live)){ //TODO: disable live for IE6?
// create fake splitter to display at old position while we drag
(this.fake = this.domNode.cloneNode(true)).removeAttribute("id");
domClass.add(this.domNode, "dijitSplitterShadow");
domConstruct.place(this.fake, this.domNode, "after");
}
domClass.add(this.domNode, "dijitSplitterActive dijitSplitter" + (this.horizontal ? "H" : "V") + "Active");
if(this.fake){
domClass.remove(this.fake, "dijitSplitterHover dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover");
}
//Performance: load data info local vars for onmousevent function closure
var factor = this._factor,
isHorizontal = this.horizontal,
axis = isHorizontal ? "pageY" : "pageX",
pageStart = e[axis],
splitterStyle = this.domNode.style,
dim = isHorizontal ? 'h' : 'w',
childStart = domGeometry.getMarginBox(this.child.domNode)[dim],
max = this._computeMaxSize(),
min = this.child.minSize || 20,
region = this.region,
splitterAttr = region == "top" || region == "bottom" ? "top" : "left", // style attribute of splitter to adjust
splitterStart = parseInt(splitterStyle[splitterAttr], 10),
resize = this._resize,
layoutFunc = lang.hitch(this.container, "_layoutChildren", this.child.id),
de = win.doc;
this._handlers = this._handlers.concat([
on(de, touch.move, this._drag = function(e, forceResize){
var delta = e[axis] - pageStart,
childSize = factor * delta + childStart,
boundChildSize = Math.max(Math.min(childSize, max), min);
if(resize || forceResize){
layoutFunc(boundChildSize);
}
// TODO: setting style directly (usually) sets content box size, need to set margin box size
splitterStyle[splitterAttr] = delta + splitterStart + factor*(boundChildSize - childSize) + "px";
}),
on(de, "dragstart", event.stop),
on(win.body(), "selectstart", event.stop),
on(de, touch.release, lang.hitch(this, "_stopDrag"))
]);
event.stop(e);
},
_onMouse: function(e){
// summary:
// Handler for onmouseenter / onmouseleave events
var o = (e.type == "mouseover" || e.type == "mouseenter");
domClass.toggle(this.domNode, "dijitSplitterHover", o);
domClass.toggle(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover", o);
},
_stopDrag: function(e){
try{
if(this.cover){
domClass.remove(this.cover, "dijitSplitterCoverActive");
}
if(this.fake){ domConstruct.destroy(this.fake); }
domClass.remove(this.domNode, "dijitSplitterActive dijitSplitter"
+ (this.horizontal ? "H" : "V") + "Active dijitSplitterShadow");
this._drag(e); //TODO: redundant with onmousemove?
this._drag(e, true);
}finally{
this._cleanupHandlers();
delete this._drag;
}
if(this.container.persist){
cookie(this._cookieName, this.child.domNode.style[this.horizontal ? "height" : "width"], {expires:365});
}
},
_cleanupHandlers: function(){
var h;
while(h = this._handlers.pop()){ h.remove(); }
},
_onKeyPress: function(/*Event*/ e){
// should we apply typematic to this?
this._resize = true;
var horizontal = this.horizontal;
var tick = 1;
switch(e.charOrCode){
case horizontal ? keys.UP_ARROW : keys.LEFT_ARROW:
tick *= -1;
// break;
case horizontal ? keys.DOWN_ARROW : keys.RIGHT_ARROW:
break;
default:
// this.inherited(arguments);
return;
}
var childSize = domGeometry.getMarginSize(this.child.domNode)[ horizontal ? 'h' : 'w' ] + this._factor * tick;
this.container._layoutChildren(this.child.id, Math.max(Math.min(childSize, this._computeMaxSize()), this.child.minSize));
event.stop(e);
},
destroy: function(){
this._cleanupHandlers();
delete this.child;
delete this.container;
delete this.cover;
delete this.fake;
this.inherited(arguments);
}
});
var _Gutter = declare("dijit.layout._Gutter", [_Widget, _TemplatedMixin],
{
// summary:
// Just a spacer div to separate side pane from center pane.
// Basically a trick to lookup the gutter/splitter width from the theme.
// description:
// Instantiated by `dijit.layout.BorderContainer`. Users should not
// create directly.
// tags:
// private
templateString: '<div class="dijitGutter" role="presentation"></div>',
postMixInProperties: function(){
this.inherited(arguments);
this.horizontal = /top|bottom/.test(this.region);
},
buildRendering: function(){
this.inherited(arguments);
domClass.add(this.domNode, "dijitGutter" + (this.horizontal ? "H" : "V"));
}
});
var BorderContainer = declare("dijit.layout.BorderContainer", _LayoutWidget, {
// summary:
// Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides.
//
// description:
// A BorderContainer is a box with a specified size, such as style="width: 500px; height: 500px;",
// that contains a child widget marked region="center" and optionally children widgets marked
// region equal to "top", "bottom", "leading", "trailing", "left" or "right".
// Children along the edges will be laid out according to width or height dimensions and may
// include optional splitters (splitter="true") to make them resizable by the user. The remaining
// space is designated for the center region.
//
// The outer size must be specified on the BorderContainer node. Width must be specified for the sides
// and height for the top and bottom, respectively. No dimensions should be specified on the center;
// it will fill the remaining space. Regions named "leading" and "trailing" may be used just like
// "left" and "right" except that they will be reversed in right-to-left environments.
//
// For complex layouts, multiple children can be specified for a single region. In this case, the
// layoutPriority flag on the children determines which child is closer to the edge (low layoutPriority)
// and which child is closer to the center (high layoutPriority). layoutPriority can also be used
// instead of the design attribute to control layout precedence of horizontal vs. vertical panes.
// example:
// | <div data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="design: 'sidebar', gutters: false"
// | style="width: 400px; height: 300px;">
// | <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region: 'top'">header text</div>
// | <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region: 'right', splitter: true" style="width: 200px;">table of contents</div>
// | <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region: 'center'">client area</div>
// | </div>
// design: String
// Which design is used for the layout:
// - "headline" (default) where the top and bottom extend
// the full width of the container
// - "sidebar" where the left and right sides extend from top to bottom.
design: "headline",
// gutters: [const] Boolean
// Give each pane a border and margin.
// Margin determined by domNode.paddingLeft.
// When false, only resizable panes have a gutter (i.e. draggable splitter) for resizing.
gutters: true,
// liveSplitters: [const] Boolean
// Specifies whether splitters resize as you drag (true) or only upon mouseup (false)
liveSplitters: true,
// persist: Boolean
// Save splitter positions in a cookie.
persist: false,
baseClass: "dijitBorderContainer",
// _splitterClass: Function||String
// Optional hook to override the default Splitter widget used by BorderContainer
_splitterClass: _Splitter,
postMixInProperties: function(){
// change class name to indicate that BorderContainer is being used purely for
// layout (like LayoutContainer) rather than for pretty formatting.
if(!this.gutters){
this.baseClass += "NoGutter";
}
this.inherited(arguments);
},
startup: function(){
if(this._started){ return; }
array.forEach(this.getChildren(), this._setupChild, this);
this.inherited(arguments);
},
_setupChild: function(/*dijit._Widget*/ child){
// Override _LayoutWidget._setupChild().
var region = child.region;
if(region){
this.inherited(arguments);
domClass.add(child.domNode, this.baseClass+"Pane");
var ltr = this.isLeftToRight();
if(region == "leading"){ region = ltr ? "left" : "right"; }
if(region == "trailing"){ region = ltr ? "right" : "left"; }
// Create draggable splitter for resizing pane,
// or alternately if splitter=false but BorderContainer.gutters=true then
// insert dummy div just for spacing
if(region != "center" && (child.splitter || this.gutters) && !child._splitterWidget){
var _Splitter = child.splitter ? this._splitterClass : _Gutter;
if(lang.isString(_Splitter)){
_Splitter = lang.getObject(_Splitter); // for back-compat, remove in 2.0
}
var splitter = new _Splitter({
id: child.id + "_splitter",
container: this,
child: child,
region: region,
live: this.liveSplitters
});
splitter.isSplitter = true;
child._splitterWidget = splitter;
domConstruct.place(splitter.domNode, child.domNode, "after");
// Splitters aren't added as Contained children, so we need to call startup explicitly
splitter.startup();
}
child.region = region; // TODO: technically wrong since it overwrites "trailing" with "left" etc.
}
},
layout: function(){
// Implement _LayoutWidget.layout() virtual method.
this._layoutChildren();
},
addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
// Override _LayoutWidget.addChild().
this.inherited(arguments);
if(this._started){
this.layout(); //OPT
}
},
removeChild: function(/*dijit._Widget*/ child){
// Override _LayoutWidget.removeChild().
var region = child.region;
var splitter = child._splitterWidget;
if(splitter){
splitter.destroy();
delete child._splitterWidget;
}
this.inherited(arguments);
if(this._started){
this._layoutChildren();
}
// Clean up whatever style changes we made to the child pane.
// Unclear how height and width should be handled.
domClass.remove(child.domNode, this.baseClass+"Pane");
domStyle.set(child.domNode, {
top: "auto",
bottom: "auto",
left: "auto",
right: "auto",
position: "static"
});
domStyle.set(child.domNode, region == "top" || region == "bottom" ? "width" : "height", "auto");
},
getChildren: function(){
// Override _LayoutWidget.getChildren() to only return real children, not the splitters.
return array.filter(this.inherited(arguments), function(widget){
return !widget.isSplitter;
});
},
// TODO: remove in 2.0
getSplitter: function(/*String*/region){
// summary:
// Returns the widget responsible for rendering the splitter associated with region
// tags:
// deprecated
return array.filter(this.getChildren(), function(child){
return child.region == region;
})[0]._splitterWidget;
},
resize: function(newSize, currentSize){
// Overrides _LayoutWidget.resize().
// resetting potential padding to 0px to provide support for 100% width/height + padding
// TODO: this hack doesn't respect the box model and is a temporary fix
if(!this.cs || !this.pe){
var node = this.domNode;
this.cs = domStyle.getComputedStyle(node);
this.pe = domGeometry.getPadExtents(node, this.cs);
this.pe.r = domStyle.toPixelValue(node, this.cs.paddingRight);
this.pe.b = domStyle.toPixelValue(node, this.cs.paddingBottom);
domStyle.set(node, "padding", "0px");
}
this.inherited(arguments);
},
_layoutChildren: function(/*String?*/ changedChildId, /*Number?*/ changedChildSize){
// summary:
// This is the main routine for setting size/position of each child.
// description:
// With no arguments, measures the height of top/bottom panes, the width
// of left/right panes, and then sizes all panes accordingly.
//
// With changedRegion specified (as "left", "top", "bottom", or "right"),
// it changes that region's width/height to changedRegionSize and
// then resizes other regions that were affected.
// changedChildId:
// Id of the child which should be resized because splitter was dragged.
// changedChildSize:
// The new width/height (in pixels) to make specified child
if(!this._borderBox || !this._borderBox.h){
// We are currently hidden, or we haven't been sized by our parent yet.
// Abort. Someone will resize us later.
return;
}
// Generate list of wrappers of my children in the order that I want layoutChildren()
// to process them (i.e. from the outside to the inside)
var wrappers = array.map(this.getChildren(), function(child, idx){
return {
pane: child,
weight: [
child.region == "center" ? Infinity : 0,
child.layoutPriority,
(this.design == "sidebar" ? 1 : -1) * (/top|bottom/.test(child.region) ? 1 : -1),
idx
]
};
}, this);
wrappers.sort(function(a, b){
var aw = a.weight, bw = b.weight;
for(var i=0; i<aw.length; i++){
if(aw[i] != bw[i]){
return aw[i] - bw[i];
}
}
return 0;
});
// Make new list, combining the externally specified children with splitters and gutters
var childrenAndSplitters = [];
array.forEach(wrappers, function(wrapper){
var pane = wrapper.pane;
childrenAndSplitters.push(pane);
if(pane._splitterWidget){
childrenAndSplitters.push(pane._splitterWidget);
}
});
// Compute the box in which to lay out my children
var dim = {
l: this.pe.l,
t: this.pe.t,
w: this._borderBox.w - this.pe.w,
h: this._borderBox.h - this.pe.h
};
// Layout the children, possibly changing size due to a splitter drag
layoutUtils.layoutChildren(this.domNode, dim, childrenAndSplitters,
changedChildId, changedChildSize);
},
destroyRecursive: function(){
// Destroy splitters first, while getChildren() still works
array.forEach(this.getChildren(), function(child){
var splitter = child._splitterWidget;
if(splitter){
splitter.destroy();
}
delete child._splitterWidget;
});
// Then destroy the real children, and myself
this.inherited(arguments);
}
});
// This argument can be specified for the children of a BorderContainer.
// Since any widget can be specified as a LayoutContainer child, mix it
// into the base widget class. (This is a hack, but it's effective.)
lang.extend(_WidgetBase, {
// region: [const] String
// Parameter for children of `dijit.layout.BorderContainer`.
// Values: "top", "bottom", "leading", "trailing", "left", "right", "center".
// See the `dijit.layout.BorderContainer` description for details.
region: '',
// layoutPriority: [const] Number
// Parameter for children of `dijit.layout.BorderContainer`.
// Children with a higher layoutPriority will be placed closer to the BorderContainer center,
// between children with a lower layoutPriority.
layoutPriority: 0,
// splitter: [const] Boolean
// Parameter for child of `dijit.layout.BorderContainer` where region != "center".
// If true, enables user to resize the widget by putting a draggable splitter between
// this widget and the region=center widget.
splitter: false,
// minSize: [const] Number
// Parameter for children of `dijit.layout.BorderContainer`.
// Specifies a minimum size (in pixels) for this widget when resized by a splitter.
minSize: 0,
// maxSize: [const] Number
// Parameter for children of `dijit.layout.BorderContainer`.
// Specifies a maximum size (in pixels) for this widget when resized by a splitter.
maxSize: Infinity
});
// For monkey patching
BorderContainer._Splitter = _Splitter;
BorderContainer._Gutter = _Gutter;
return BorderContainer;
});
},
'dojo/number':function(){
define(["./_base/kernel", "./_base/lang", "./i18n", "./i18n!./cldr/nls/number", "./string", "./regexp"],
function(dojo, lang, i18n, nlsNumber, dstring, dregexp) {
// module:
// dojo/number
// summary:
// TODOC
lang.getObject("number", true, dojo);
/*=====
dojo.number = {
// summary: localized formatting and parsing routines for Number
}
dojo.number.__FormatOptions = function(){
// pattern: String?
// override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
// with this string. Default value is based on locale. Overriding this property will defeat
// localization. Literal characters in patterns are not supported.
// type: String?
// choose a format type based on the locale from the following:
// decimal, scientific (not yet supported), percent, currency. decimal by default.
// places: Number?
// fixed number of decimal places to show. This overrides any
// information in the provided pattern.
// round: Number?
// 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
// means do not round.
// locale: String?
// override the locale used to determine formatting rules
// fractional: Boolean?
// If false, show no decimal places, overriding places and pattern settings.
this.pattern = pattern;
this.type = type;
this.places = places;
this.round = round;
this.locale = locale;
this.fractional = fractional;
}
=====*/
dojo.number.format = function(/*Number*/value, /*dojo.number.__FormatOptions?*/options){
// summary:
// Format a Number as a String, using locale-specific settings
// description:
// Create a string from a Number using a known localized pattern.
// Formatting patterns appropriate to the locale are chosen from the
// [Common Locale Data Repository](http://unicode.org/cldr) as well as the appropriate symbols and
// delimiters.
// If value is Infinity, -Infinity, or is not a valid JavaScript number, return null.
// value:
// the number to be formatted
options = lang.mixin({}, options || {});
var locale = i18n.normalizeLocale(options.locale),
bundle = i18n.getLocalization("dojo.cldr", "number", locale);
options.customs = bundle;
var pattern = options.pattern || bundle[(options.type || "decimal") + "Format"];
if(isNaN(value) || Math.abs(value) == Infinity){ return null; } // null
return dojo.number._applyPattern(value, pattern, options); // String
};
//dojo.number._numberPatternRE = /(?:[#0]*,?)*[#0](?:\.0*#*)?/; // not precise, but good enough
dojo.number._numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough
dojo.number._applyPattern = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatOptions?*/options){
// summary:
// Apply pattern to format value as a string using options. Gives no
// consideration to local customs.
// value:
// the number to be formatted.
// pattern:
// a pattern string as described by
// [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
// options: dojo.number.__FormatOptions?
// _applyPattern is usually called via `dojo.number.format()` which
// populates an extra property in the options parameter, "customs".
// The customs object specifies group and decimal parameters if set.
//TODO: support escapes
options = options || {};
var group = options.customs.group,
decimal = options.customs.decimal,
patternList = pattern.split(';'),
positivePattern = patternList[0];
pattern = patternList[(value < 0) ? 1 : 0] || ("-" + positivePattern);
//TODO: only test against unescaped
if(pattern.indexOf('%') != -1){
value *= 100;
}else if(pattern.indexOf('\u2030') != -1){
value *= 1000; // per mille
}else if(pattern.indexOf('\u00a4') != -1){
group = options.customs.currencyGroup || group;//mixins instead?
decimal = options.customs.currencyDecimal || decimal;// Should these be mixins instead?
pattern = pattern.replace(/\u00a4{1,3}/, function(match){
var prop = ["symbol", "currency", "displayName"][match.length-1];
return options[prop] || options.currency || "";
});
}else if(pattern.indexOf('E') != -1){
throw new Error("exponential notation not supported");
}
//TODO: support @ sig figs?
var numberPatternRE = dojo.number._numberPatternRE;
var numberPattern = positivePattern.match(numberPatternRE);
if(!numberPattern){
throw new Error("unable to find a number expression in pattern: "+pattern);
}
if(options.fractional === false){ options.places = 0; }
return pattern.replace(numberPatternRE,
dojo.number._formatAbsolute(value, numberPattern[0], {decimal: decimal, group: group, places: options.places, round: options.round}));
};
dojo.number.round = function(/*Number*/value, /*Number?*/places, /*Number?*/increment){
// summary:
// Rounds to the nearest value with the given number of decimal places, away from zero
// description:
// Rounds to the nearest value with the given number of decimal places, away from zero if equal.
// Similar to Number.toFixed(), but compensates for browser quirks. Rounding can be done by
// fractional increments also, such as the nearest quarter.
// NOTE: Subject to floating point errors. See dojox.math.round for experimental workaround.
// value:
// The number to round
// places:
// The number of decimal places where rounding takes place. Defaults to 0 for whole rounding.
// Must be non-negative.
// increment:
// Rounds next place to nearest value of increment/10. 10 by default.
// example:
// >>> dojo.number.round(-0.5)
// -1
// >>> dojo.number.round(162.295, 2)
// 162.29 // note floating point error. Should be 162.3
// >>> dojo.number.round(10.71, 0, 2.5)
// 10.75
var factor = 10 / (increment || 10);
return (factor * +value).toFixed(places) / factor; // Number
};
if((0.9).toFixed() == 0){
// (isIE) toFixed() bug workaround: Rounding fails on IE when most significant digit
// is just after the rounding place and is >=5
var round = dojo.number.round;
dojo.number.round = function(v, p, m){
var d = Math.pow(10, -p || 0), a = Math.abs(v);
if(!v || a >= d || a * Math.pow(10, p + 1) < 5){
d = 0;
}
return round(v, p, m) + (v > 0 ? d : -d);
};
}
/*=====
dojo.number.__FormatAbsoluteOptions = function(){
// decimal: String?
// the decimal separator
// group: String?
// the group separator
// places: Number?|String?
// number of decimal places. the range "n,m" will format to m places.
// round: Number?
// 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
// means don't round.
this.decimal = decimal;
this.group = group;
this.places = places;
this.round = round;
}
=====*/
dojo.number._formatAbsolute = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatAbsoluteOptions?*/options){
// summary:
// Apply numeric pattern to absolute value using options. Gives no
// consideration to local customs.
// value:
// the number to be formatted, ignores sign
// pattern:
// the number portion of a pattern (e.g. `#,##0.00`)
options = options || {};
if(options.places === true){options.places=0;}
if(options.places === Infinity){options.places=6;} // avoid a loop; pick a limit
var patternParts = pattern.split("."),
comma = typeof options.places == "string" && options.places.indexOf(","),
maxPlaces = options.places;
if(comma){
maxPlaces = options.places.substring(comma + 1);
}else if(!(maxPlaces >= 0)){
maxPlaces = (patternParts[1] || []).length;
}
if(!(options.round < 0)){
value = dojo.number.round(value, maxPlaces, options.round);
}
var valueParts = String(Math.abs(value)).split("."),
fractional = valueParts[1] || "";
if(patternParts[1] || options.places){
if(comma){
options.places = options.places.substring(0, comma);
}
// Pad fractional with trailing zeros
var pad = options.places !== undefined ? options.places : (patternParts[1] && patternParts[1].lastIndexOf("0") + 1);
if(pad > fractional.length){
valueParts[1] = dstring.pad(fractional, pad, '0', true);
}
// Truncate fractional
if(maxPlaces < fractional.length){
valueParts[1] = fractional.substr(0, maxPlaces);
}
}else{
if(valueParts[1]){ valueParts.pop(); }
}
// Pad whole with leading zeros
var patternDigits = patternParts[0].replace(',', '');
pad = patternDigits.indexOf("0");
if(pad != -1){
pad = patternDigits.length - pad;
if(pad > valueParts[0].length){
valueParts[0] = dstring.pad(valueParts[0], pad);
}
// Truncate whole
if(patternDigits.indexOf("#") == -1){
valueParts[0] = valueParts[0].substr(valueParts[0].length - pad);
}
}
// Add group separators
var index = patternParts[0].lastIndexOf(','),
groupSize, groupSize2;
if(index != -1){
groupSize = patternParts[0].length - index - 1;
var remainder = patternParts[0].substr(0, index);
index = remainder.lastIndexOf(',');
if(index != -1){
groupSize2 = remainder.length - index - 1;
}
}
var pieces = [];
for(var whole = valueParts[0]; whole;){
var off = whole.length - groupSize;
pieces.push((off > 0) ? whole.substr(off) : whole);
whole = (off > 0) ? whole.slice(0, off) : "";
if(groupSize2){
groupSize = groupSize2;
delete groupSize2;
}
}
valueParts[0] = pieces.reverse().join(options.group || ",");
return valueParts.join(options.decimal || ".");
};
/*=====
dojo.number.__RegexpOptions = function(){
// pattern: String?
// override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
// with this string. Default value is based on locale. Overriding this property will defeat
// localization.
// type: String?
// choose a format type based on the locale from the following:
// decimal, scientific (not yet supported), percent, currency. decimal by default.
// locale: String?
// override the locale used to determine formatting rules
// strict: Boolean?
// strict parsing, false by default. Strict parsing requires input as produced by the format() method.
// Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
// places: Number|String?
// number of decimal places to accept: Infinity, a positive number, or
// a range "n,m". Defined by pattern or Infinity if pattern not provided.
this.pattern = pattern;
this.type = type;
this.locale = locale;
this.strict = strict;
this.places = places;
}
=====*/
dojo.number.regexp = function(/*dojo.number.__RegexpOptions?*/options){
// summary:
// Builds the regular needed to parse a number
// description:
// Returns regular expression with positive and negative match, group
// and decimal separators
return dojo.number._parseInfo(options).regexp; // String
};
dojo.number._parseInfo = function(/*Object?*/options){
options = options || {};
var locale = i18n.normalizeLocale(options.locale),
bundle = i18n.getLocalization("dojo.cldr", "number", locale),
pattern = options.pattern || bundle[(options.type || "decimal") + "Format"],
//TODO: memoize?
group = bundle.group,
decimal = bundle.decimal,
factor = 1;
if(pattern.indexOf('%') != -1){
factor /= 100;
}else if(pattern.indexOf('\u2030') != -1){
factor /= 1000; // per mille
}else{
var isCurrency = pattern.indexOf('\u00a4') != -1;
if(isCurrency){
group = bundle.currencyGroup || group;
decimal = bundle.currencyDecimal || decimal;
}
}
//TODO: handle quoted escapes
var patternList = pattern.split(';');
if(patternList.length == 1){
patternList.push("-" + patternList[0]);
}
var re = dregexp.buildGroupRE(patternList, function(pattern){
pattern = "(?:"+dregexp.escapeString(pattern, '.')+")";
return pattern.replace(dojo.number._numberPatternRE, function(format){
var flags = {
signed: false,
separator: options.strict ? group : [group,""],
fractional: options.fractional,
decimal: decimal,
exponent: false
},
parts = format.split('.'),
places = options.places;
// special condition for percent (factor != 1)
// allow decimal places even if not specified in pattern
if(parts.length == 1 && factor != 1){
parts[1] = "###";
}
if(parts.length == 1 || places === 0){
flags.fractional = false;
}else{
if(places === undefined){ places = options.pattern ? parts[1].lastIndexOf('0') + 1 : Infinity; }
if(places && options.fractional == undefined){flags.fractional = true;} // required fractional, unless otherwise specified
if(!options.places && (places < parts[1].length)){ places += "," + parts[1].length; }
flags.places = places;
}
var groups = parts[0].split(',');
if(groups.length > 1){
flags.groupSize = groups.pop().length;
if(groups.length > 1){
flags.groupSize2 = groups.pop().length;
}
}
return "("+dojo.number._realNumberRegexp(flags)+")";
});
}, true);
if(isCurrency){
// substitute the currency symbol for the placeholder in the pattern
re = re.replace(/([\s\xa0]*)(\u00a4{1,3})([\s\xa0]*)/g, function(match, before, target, after){
var prop = ["symbol", "currency", "displayName"][target.length-1],
symbol = dregexp.escapeString(options[prop] || options.currency || "");
before = before ? "[\\s\\xa0]" : "";
after = after ? "[\\s\\xa0]" : "";
if(!options.strict){
if(before){before += "*";}
if(after){after += "*";}
return "(?:"+before+symbol+after+")?";
}
return before+symbol+after;
});
}
//TODO: substitute localized sign/percent/permille/etc.?
// normalize whitespace and return
return {regexp: re.replace(/[\xa0 ]/g, "[\\s\\xa0]"), group: group, decimal: decimal, factor: factor}; // Object
};
/*=====
dojo.number.__ParseOptions = function(){
// pattern: String?
// override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
// with this string. Default value is based on locale. Overriding this property will defeat
// localization. Literal characters in patterns are not supported.
// type: String?
// choose a format type based on the locale from the following:
// decimal, scientific (not yet supported), percent, currency. decimal by default.
// locale: String?
// override the locale used to determine formatting rules
// strict: Boolean?
// strict parsing, false by default. Strict parsing requires input as produced by the format() method.
// Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
// fractional: Boolean?|Array?
// Whether to include the fractional portion, where the number of decimal places are implied by pattern
// or explicit 'places' parameter. The value [true,false] makes the fractional portion optional.
this.pattern = pattern;
this.type = type;
this.locale = locale;
this.strict = strict;
this.fractional = fractional;
}
=====*/
dojo.number.parse = function(/*String*/expression, /*dojo.number.__ParseOptions?*/options){
// summary:
// Convert a properly formatted string to a primitive Number, using
// locale-specific settings.
// description:
// Create a Number from a string using a known localized pattern.
// Formatting patterns are chosen appropriate to the locale
// and follow the syntax described by
// [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
// Note that literal characters in patterns are not supported.
// expression:
// A string representation of a Number
var info = dojo.number._parseInfo(options),
results = (new RegExp("^"+info.regexp+"$")).exec(expression);
if(!results){
return NaN; //NaN
}
var absoluteMatch = results[1]; // match for the positive expression
if(!results[1]){
if(!results[2]){
return NaN; //NaN
}
// matched the negative pattern
absoluteMatch =results[2];
info.factor *= -1;
}
// Transform it to something Javascript can parse as a number. Normalize
// decimal point and strip out group separators or alternate forms of whitespace
absoluteMatch = absoluteMatch.
replace(new RegExp("["+info.group + "\\s\\xa0"+"]", "g"), "").
replace(info.decimal, ".");
// Adjust for negative sign, percent, etc. as necessary
return absoluteMatch * info.factor; //Number
};
/*=====
dojo.number.__RealNumberRegexpFlags = function(){
// places: Number?
// The integer number of decimal places or a range given as "n,m". If
// not given, the decimal part is optional and the number of places is
// unlimited.
// decimal: String?
// A string for the character used as the decimal point. Default
// is ".".
// fractional: Boolean?|Array?
// Whether decimal places are used. Can be true, false, or [true,
// false]. Default is [true, false] which means optional.
// exponent: Boolean?|Array?
// Express in exponential notation. Can be true, false, or [true,
// false]. Default is [true, false], (i.e. will match if the
// exponential part is present are not).
// eSigned: Boolean?|Array?
// The leading plus-or-minus sign on the exponent. Can be true,
// false, or [true, false]. Default is [true, false], (i.e. will
// match if it is signed or unsigned). flags in regexp.integer can be
// applied.
this.places = places;
this.decimal = decimal;
this.fractional = fractional;
this.exponent = exponent;
this.eSigned = eSigned;
}
=====*/
dojo.number._realNumberRegexp = function(/*dojo.number.__RealNumberRegexpFlags?*/flags){
// summary:
// Builds a regular expression to match a real number in exponential
// notation
// assign default values to missing parameters
flags = flags || {};
//TODO: use mixin instead?
if(!("places" in flags)){ flags.places = Infinity; }
if(typeof flags.decimal != "string"){ flags.decimal = "."; }
if(!("fractional" in flags) || /^0/.test(flags.places)){ flags.fractional = [true, false]; }
if(!("exponent" in flags)){ flags.exponent = [true, false]; }
if(!("eSigned" in flags)){ flags.eSigned = [true, false]; }
var integerRE = dojo.number._integerRegexp(flags),
decimalRE = dregexp.buildGroupRE(flags.fractional,
function(q){
var re = "";
if(q && (flags.places!==0)){
re = "\\" + flags.decimal;
if(flags.places == Infinity){
re = "(?:" + re + "\\d+)?";
}else{
re += "\\d{" + flags.places + "}";
}
}
return re;
},
true
);
var exponentRE = dregexp.buildGroupRE(flags.exponent,
function(q){
if(q){ return "([eE]" + dojo.number._integerRegexp({ signed: flags.eSigned}) + ")"; }
return "";
}
);
var realRE = integerRE + decimalRE;
// allow for decimals without integers, e.g. .25
if(decimalRE){realRE = "(?:(?:"+ realRE + ")|(?:" + decimalRE + "))";}
return realRE + exponentRE; // String
};
/*=====
dojo.number.__IntegerRegexpFlags = function(){
// signed: Boolean?
// The leading plus-or-minus sign. Can be true, false, or `[true,false]`.
// Default is `[true, false]`, (i.e. will match if it is signed
// or unsigned).
// separator: String?
// The character used as the thousands separator. Default is no
// separator. For more than one symbol use an array, e.g. `[",", ""]`,
// makes ',' optional.
// groupSize: Number?
// group size between separators
// groupSize2: Number?
// second grouping, where separators 2..n have a different interval than the first separator (for India)
this.signed = signed;
this.separator = separator;
this.groupSize = groupSize;
this.groupSize2 = groupSize2;
}
=====*/
dojo.number._integerRegexp = function(/*dojo.number.__IntegerRegexpFlags?*/flags){
// summary:
// Builds a regular expression that matches an integer
// assign default values to missing parameters
flags = flags || {};
if(!("signed" in flags)){ flags.signed = [true, false]; }
if(!("separator" in flags)){
flags.separator = "";
}else if(!("groupSize" in flags)){
flags.groupSize = 3;
}
var signRE = dregexp.buildGroupRE(flags.signed,
function(q){ return q ? "[-+]" : ""; },
true
);
var numberRE = dregexp.buildGroupRE(flags.separator,
function(sep){
if(!sep){
return "(?:\\d+)";
}
sep = dregexp.escapeString(sep);
if(sep == " "){ sep = "\\s"; }
else if(sep == "\xa0"){ sep = "\\s\\xa0"; }
var grp = flags.groupSize, grp2 = flags.groupSize2;
//TODO: should we continue to enforce that numbers with separators begin with 1-9? See #6933
if(grp2){
var grp2RE = "(?:0|[1-9]\\d{0," + (grp2-1) + "}(?:[" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})";
return ((grp-grp2) > 0) ? "(?:" + grp2RE + "|(?:0|[1-9]\\d{0," + (grp-1) + "}))" : grp2RE;
}
return "(?:0|[1-9]\\d{0," + (grp-1) + "}(?:[" + sep + "]\\d{" + grp + "})*)";
},
true
);
return signRE + numberRE; // String
};
return dojo.number;
});
},
'dojo/data/util/filter':function(){
define(["dojo/_base/lang"], function(lang) {
// module:
// dojo/data/util/filter
// summary:
// TODOC
var filter = lang.getObject("dojo.data.util.filter", true);
filter.patternToRegExp = function(/*String*/pattern, /*boolean?*/ ignoreCase){
// summary:
// Helper function to convert a simple pattern to a regular expression for matching.
// description:
// Returns a regular expression object that conforms to the defined conversion rules.
// For example:
// ca* -> /^ca.*$/
// *ca* -> /^.*ca.*$/
// *c\*a* -> /^.*c\*a.*$/
// *c\*a?* -> /^.*c\*a..*$/
// and so on.
//
// pattern: string
// A simple matching pattern to convert that follows basic rules:
// * Means match anything, so ca* means match anything starting with ca
// ? Means match single character. So, b?b will match to bob and bab, and so on.
// \ is an escape character. So for example, \* means do not treat * as a match, but literal character *.
// To use a \ as a character in the string, it must be escaped. So in the pattern it should be
// represented by \\ to be treated as an ordinary \ character instead of an escape.
//
// ignoreCase:
// An optional flag to indicate if the pattern matching should be treated as case-sensitive or not when comparing
// By default, it is assumed case sensitive.
var rxp = "^";
var c = null;
for(var i = 0; i < pattern.length; i++){
c = pattern.charAt(i);
switch(c){
case '\\':
rxp += c;
i++;
rxp += pattern.charAt(i);
break;
case '*':
rxp += ".*"; break;
case '?':
rxp += "."; break;
case '$':
case '^':
case '/':
case '+':
case '.':
case '|':
case '(':
case ')':
case '{':
case '}':
case '[':
case ']':
rxp += "\\"; //fallthrough
default:
rxp += c;
}
}
rxp += "$";
if(ignoreCase){
return new RegExp(rxp,"mi"); //RegExp
}else{
return new RegExp(rxp,"m"); //RegExp
}
};
return filter;
});
},
'dijit/_WidgetsInTemplateMixin':function(){
define([
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/parser", // parser.parse
"dijit/registry" // registry.findWidgets
], function(array, declare, parser, registry){
// module:
// dijit/_WidgetsInTemplateMixin
// summary:
// Mixin to supplement _TemplatedMixin when template contains widgets
return declare("dijit._WidgetsInTemplateMixin", null, {
// summary:
// Mixin to supplement _TemplatedMixin when template contains widgets
// _earlyTemplatedStartup: Boolean
// A fallback to preserve the 1.0 - 1.3 behavior of children in
// templates having their startup called before the parent widget
// fires postCreate. Defaults to 'false', causing child widgets to
// have their .startup() called immediately before a parent widget
// .startup(), but always after the parent .postCreate(). Set to
// 'true' to re-enable to previous, arguably broken, behavior.
_earlyTemplatedStartup: false,
// widgetsInTemplate: [protected] Boolean
// Should we parse the template to find widgets that might be
// declared in markup inside it? (Remove for 2.0 and assume true)
widgetsInTemplate: true,
_beforeFillContent: function(){
if(this.widgetsInTemplate){
// Before copying over content, instantiate widgets in template
var node = this.domNode;
var cw = (this._startupWidgets = parser.parse(node, {
noStart: !this._earlyTemplatedStartup,
template: true,
inherited: {dir: this.dir, lang: this.lang, textDir: this.textDir},
propsThis: this, // so data-dojo-props of widgets in the template can reference "this" to refer to me
scope: "dojo" // even in multi-version mode templates use dojoType/data-dojo-type
}));
this._supportingWidgets = registry.findWidgets(node);
this._attachTemplateNodes(cw, function(n,p){
return n[p];
});
}
},
startup: function(){
array.forEach(this._startupWidgets, function(w){
if(w && !w._started && w.startup){
w.startup();
}
});
this.inherited(arguments);
}
});
});
},
'dijit/form/HorizontalRuleLabels':function(){
define([
"dojo/_base/declare", // declare
"dojo/number", // number.format
"dojo/query", // query
"./HorizontalRule"
], function(declare, number, query, HorizontalRule){
/*=====
var HorizontalRule = dijit.form.HorizontalRule;
=====*/
// module:
// dijit/form/HorizontalRuleLabels
// summary:
// Labels for `dijit.form.HorizontalSlider`
return declare("dijit.form.HorizontalRuleLabels", HorizontalRule, {
// summary:
// Labels for `dijit.form.HorizontalSlider`
templateString: '<div class="dijitRuleContainer dijitRuleContainerH dijitRuleLabelsContainer dijitRuleLabelsContainerH"></div>',
// labelStyle: String
// CSS style to apply to individual text labels
labelStyle: "",
// labels: String[]?
// Array of text labels to render - evenly spaced from left-to-right or bottom-to-top.
// Alternately, minimum and maximum can be specified, to get numeric labels.
labels: [],
// numericMargin: Integer
// Number of generated numeric labels that should be rendered as '' on the ends when labels[] are not specified
numericMargin: 0,
// numericMinimum: Integer
// Leftmost label value for generated numeric labels when labels[] are not specified
minimum: 0,
// numericMaximum: Integer
// Rightmost label value for generated numeric labels when labels[] are not specified
maximum: 1,
// constraints: Object
// pattern, places, lang, et al (see dojo.number) for generated numeric labels when labels[] are not specified
constraints: {pattern:"#%"},
_positionPrefix: '<div class="dijitRuleLabelContainer dijitRuleLabelContainerH" style="left:',
_labelPrefix: '"><div class="dijitRuleLabel dijitRuleLabelH">',
_suffix: '</div></div>',
_calcPosition: function(pos){
// summary:
// Returns the value to be used in HTML for the label as part of the left: attribute
// tags:
// protected extension
return pos;
},
_genHTML: function(pos, ndx){
return this._positionPrefix + this._calcPosition(pos) + this._positionSuffix + this.labelStyle + this._labelPrefix + this.labels[ndx] + this._suffix;
},
getLabels: function(){
// summary:
// Overridable function to return array of labels to use for this slider.
// Can specify a getLabels() method instead of a labels[] array, or min/max attributes.
// tags:
// protected extension
// if the labels array was not specified directly, then see if <li> children were
var labels = this.labels;
if(!labels.length){
// for markup creation, labels are specified as child elements
labels = query("> li", this.srcNodeRef).map(function(node){
return String(node.innerHTML);
});
}
this.srcNodeRef.innerHTML = '';
// if the labels were not specified directly and not as <li> children, then calculate numeric labels
if(!labels.length && this.count > 1){
var start = this.minimum;
var inc = (this.maximum - start) / (this.count-1);
for(var i=0; i < this.count; i++){
labels.push((i < this.numericMargin || i >= (this.count-this.numericMargin)) ? '' : number.format(start, this.constraints));
start += inc;
}
}
return labels;
},
postMixInProperties: function(){
this.inherited(arguments);
this.labels = this.getLabels();
this.count = this.labels.length;
}
});
});
},
'url:dijit/templates/MenuBarItem.html':"<div class=\"dijitReset dijitInline dijitMenuItem dijitMenuItemLabel\" data-dojo-attach-point=\"focusNode\" role=\"menuitem\" tabIndex=\"-1\"\n\t\tdata-dojo-attach-event=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\n\t<span data-dojo-attach-point=\"containerNode\"></span>\n</div>\n",
'dijit/form/FilteringSelect':function(){
define([
"dojo/data/util/filter", // filter.patternToRegExp
"dojo/_base/declare", // declare
"dojo/_base/Deferred", // Deferred.when
"dojo/_base/lang", // lang.mixin
"./MappedTextBox",
"./ComboBoxMixin"
], function(filter, declare, Deferred, lang, MappedTextBox, ComboBoxMixin){
/*=====
var MappedTextBox = dijit.form.MappedTextBox;
var ComboBoxMixin = dijit.form.ComboBoxMixin;
=====*/
// module:
// dijit/form/FilteringSelect
// summary:
// An enhanced version of the HTML SELECT tag, populated dynamically
return declare("dijit.form.FilteringSelect", [MappedTextBox, ComboBoxMixin], {
// summary:
// An enhanced version of the HTML SELECT tag, populated dynamically
//
// description:
// An enhanced version of the HTML SELECT tag, populated dynamically. It works
// very nicely with very large data sets because it can load and page data as needed.
// It also resembles ComboBox, but does not allow values outside of the provided ones.
// If OPTION tags are used as the data provider via markup, then the
// OPTION tag's child text node is used as the displayed value when selected
// while the OPTION tag's value attribute is used as the widget value on form submit.
// To set the default value when using OPTION tags, specify the selected
// attribute on 1 of the child OPTION tags.
//
// Similar features:
// - There is a drop down list of possible values.
// - You can only enter a value from the drop down list. (You can't
// enter an arbitrary value.)
// - The value submitted with the form is the hidden value (ex: CA),
// not the displayed value a.k.a. label (ex: California)
//
// Enhancements over plain HTML version:
// - If you type in some text then it will filter down the list of
// possible values in the drop down list.
// - List can be specified either as a static list or via a javascript
// function (that can get the list from a server)
// required: Boolean
// True (default) if user is required to enter a value into this field.
required: true,
_lastDisplayedValue: "",
_isValidSubset: function(){
return this._opened;
},
isValid: function(){
// Overrides ValidationTextBox.isValid()
return this.item || (!this.required && this.get('displayedValue') == ""); // #5974
},
_refreshState: function(){
if(!this.searchTimer){ // state will be refreshed after results are returned
this.inherited(arguments);
}
},
_callbackSetLabel: function(
/*Array*/ result,
/*Object*/ query,
/*Object*/ options,
/*Boolean?*/ priorityChange){
// summary:
// Callback from dojo.store after lookup of user entered value finishes
// setValue does a synchronous lookup,
// so it calls _callbackSetLabel directly,
// and so does not pass dataObject
// still need to test against _lastQuery in case it came too late
if((query && query[this.searchAttr] !== this._lastQuery) || (!query && result.length && this.store.getIdentity(result[0]) != this._lastQuery)){
return;
}
if(!result.length){
//#3268: don't modify display value on bad input
//#3285: change CSS to indicate error
this.set("value", '', priorityChange || (priorityChange === undefined && !this.focused), this.textbox.value, null);
}else{
this.set('item', result[0], priorityChange);
}
},
_openResultList: function(/*Object*/ results, /*Object*/ query, /*Object*/ options){
// Callback when a data store query completes.
// Overrides ComboBox._openResultList()
// #3285: tap into search callback to see if user's query resembles a match
if(query[this.searchAttr] !== this._lastQuery){
return;
}
this.inherited(arguments);
if(this.item === undefined){ // item == undefined for keyboard search
// If the search returned no items that means that the user typed
// in something invalid (and they can't make it valid by typing more characters),
// so flag the FilteringSelect as being in an invalid state
this.validate(true);
}
},
_getValueAttr: function(){
// summary:
// Hook for get('value') to work.
// don't get the textbox value but rather the previously set hidden value.
// Use this.valueNode.value which isn't always set for other MappedTextBox widgets until blur
return this.valueNode.value;
},
_getValueField: function(){
// Overrides ComboBox._getValueField()
return "value";
},
_setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange, /*String?*/ displayedValue, /*item?*/ item){
// summary:
// Hook so set('value', value) works.
// description:
// Sets the value of the select.
// Also sets the label to the corresponding value by reverse lookup.
if(!this._onChangeActive){ priorityChange = null; }
if(item === undefined){
if(value === null || value === ''){
value = '';
if(!lang.isString(displayedValue)){
this._setDisplayedValueAttr(displayedValue||'', priorityChange);
return;
}
}
var self = this;
this._lastQuery = value;
Deferred.when(this.store.get(value), function(item){
self._callbackSetLabel(item? [item] : [], undefined, undefined, priorityChange);
});
}else{
this.valueNode.value = value;
this.inherited(arguments);
}
},
_setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
// summary:
// Set the displayed valued in the input box, and the hidden value
// that gets submitted, based on a dojo.data store item.
// description:
// Users shouldn't call this function; they should be calling
// set('item', value)
// tags:
// private
this.inherited(arguments);
this._lastDisplayedValue = this.textbox.value;
},
_getDisplayQueryString: function(/*String*/ text){
return text.replace(/([\\\*\?])/g, "\\$1");
},
_setDisplayedValueAttr: function(/*String*/ label, /*Boolean?*/ priorityChange){
// summary:
// Hook so set('displayedValue', label) works.
// description:
// Sets textbox to display label. Also performs reverse lookup
// to set the hidden value. label should corresponding to item.searchAttr.
if(label == null){ label = ''; }
// This is called at initialization along with every custom setter.
// Usually (or always?) the call can be ignored. If it needs to be
// processed then at least make sure that the XHR request doesn't trigger an onChange()
// event, even if it returns after creation has finished
if(!this._created){
if(!("displayedValue" in this.params)){
return;
}
priorityChange = false;
}
// Do a reverse lookup to map the specified displayedValue to the hidden value.
// Note that if there's a custom labelFunc() this code
if(this.store){
this.closeDropDown();
var query = lang.clone(this.query); // #6196: populate query with user-specifics
// Generate query
var qs = this._getDisplayQueryString(label), q;
if(this.store._oldAPI){
// remove this branch for 2.0
q = qs;
}else{
// Query on searchAttr is a regex for benefit of dojo.store.Memory,
// but with a toString() method to help dojo.store.JsonRest.
// Search string like "Co*" converted to regex like /^Co.*$/i.
q = filter.patternToRegExp(qs, this.ignoreCase);
q.toString = function(){ return qs; };
}
this._lastQuery = query[this.searchAttr] = q;
// If the label is not valid, the callback will never set it,
// so the last valid value will get the warning textbox. Set the
// textbox value now so that the impending warning will make
// sense to the user
this.textbox.value = label;
this._lastDisplayedValue = label;
this._set("displayedValue", label); // for watch("displayedValue") notification
var _this = this;
var options = {
ignoreCase: this.ignoreCase,
deep: true
};
lang.mixin(options, this.fetchProperties);
this._fetchHandle = this.store.query(query, options);
Deferred.when(this._fetchHandle, function(result){
_this._fetchHandle = null;
_this._callbackSetLabel(result || [], query, options, priorityChange);
}, function(err){
_this._fetchHandle = null;
if(!_this._cancelingQuery){ // don't treat canceled query as an error
console.error('dijit.form.FilteringSelect: ' + err.toString());
}
});
}
},
undo: function(){
this.set('displayedValue', this._lastDisplayedValue);
}
});
});
},
'dojo/data/util/sorter':function(){
define(["dojo/_base/lang"], function(lang) {
// module:
// dojo/data/util/sorter
// summary:
// TODOC
var sorter = lang.getObject("dojo.data.util.sorter", true);
sorter.basicComparator = function( /*anything*/ a,
/*anything*/ b){
// summary:
// Basic comparision function that compares if an item is greater or less than another item
// description:
// returns 1 if a > b, -1 if a < b, 0 if equal.
// 'null' values (null, undefined) are treated as larger values so that they're pushed to the end of the list.
// And compared to each other, null is equivalent to undefined.
//null is a problematic compare, so if null, we set to undefined.
//Makes the check logic simple, compact, and consistent
//And (null == undefined) === true, so the check later against null
//works for undefined and is less bytes.
var r = -1;
if(a === null){
a = undefined;
}
if(b === null){
b = undefined;
}
if(a == b){
r = 0;
}else if(a > b || a == null){
r = 1;
}
return r; //int {-1,0,1}
};
sorter.createSortFunction = function( /* attributes array */sortSpec, /*dojo.data.core.Read*/ store){
// summary:
// Helper function to generate the sorting function based off the list of sort attributes.
// description:
// The sort function creation will look for a property on the store called 'comparatorMap'. If it exists
// it will look in the mapping for comparisons function for the attributes. If one is found, it will
// use it instead of the basic comparator, which is typically used for strings, ints, booleans, and dates.
// Returns the sorting function for this particular list of attributes and sorting directions.
//
// sortSpec: array
// A JS object that array that defines out what attribute names to sort on and whether it should be descenting or asending.
// The objects should be formatted as follows:
// {
// attribute: "attributeName-string" || attribute,
// descending: true|false; // Default is false.
// }
// store: object
// The datastore object to look up item values from.
//
var sortFunctions=[];
function createSortFunction(attr, dir, comp, s){
//Passing in comp and s (comparator and store), makes this
//function much faster.
return function(itemA, itemB){
var a = s.getValue(itemA, attr);
var b = s.getValue(itemB, attr);
return dir * comp(a,b); //int
};
}
var sortAttribute;
var map = store.comparatorMap;
var bc = sorter.basicComparator;
for(var i = 0; i < sortSpec.length; i++){
sortAttribute = sortSpec[i];
var attr = sortAttribute.attribute;
if(attr){
var dir = (sortAttribute.descending) ? -1 : 1;
var comp = bc;
if(map){
if(typeof attr !== "string" && ("toString" in attr)){
attr = attr.toString();
}
comp = map[attr] || bc;
}
sortFunctions.push(createSortFunction(attr,
dir, comp, store));
}
}
return function(rowA, rowB){
var i=0;
while(i < sortFunctions.length){
var ret = sortFunctions[i++](rowA, rowB);
if(ret !== 0){
return ret;//int
}
}
return 0; //int
}; // Function
};
return sorter;
});
},
'dijit/form/_ButtonMixin':function(){
define([
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"dojo/_base/event", // event.stop
"../registry" // registry.byNode
], function(declare, dom, event, registry){
// module:
// dijit/form/_ButtonMixin
// summary:
// A mixin to add a thin standard API wrapper to a normal HTML button
return declare("dijit.form._ButtonMixin", null, {
// summary:
// A mixin to add a thin standard API wrapper to a normal HTML button
// description:
// A label should always be specified (through innerHTML) or the label attribute.
// Attach points:
// focusNode (required): this node receives focus
// valueNode (optional): this node's value gets submitted with FORM elements
// containerNode (optional): this node gets the innerHTML assignment for label
// example:
// | <button data-dojo-type="dijit.form.Button" onClick="...">Hello world</button>
//
// example:
// | var button1 = new dijit.form.Button({label: "hello world", onClick: foo});
// | dojo.body().appendChild(button1.domNode);
// label: HTML String
// Content to display in button.
label: "",
// type: [const] String
// Type of button (submit, reset, button, checkbox, radio)
type: "button",
_onClick: function(/*Event*/ e){
// summary:
// Internal function to handle click actions
if(this.disabled){
event.stop(e);
return false;
}
var preventDefault = this.onClick(e) === false; // user click actions
if(!preventDefault && this.type == "submit" && !(this.valueNode||this.focusNode).form){ // see if a non-form widget needs to be signalled
for(var node=this.domNode; node.parentNode; node=node.parentNode){
var widget=registry.byNode(node);
if(widget && typeof widget._onSubmit == "function"){
widget._onSubmit(e);
preventDefault = true;
break;
}
}
}
if(preventDefault){
e.preventDefault();
}
return !preventDefault;
},
postCreate: function(){
this.inherited(arguments);
dom.setSelectable(this.focusNode, false);
},
onClick: function(/*Event*/ /*===== e =====*/){
// summary:
// Callback for when button is clicked.
// If type="submit", return true to perform submit, or false to cancel it.
// type:
// callback
return true; // Boolean
},
_setLabelAttr: function(/*String*/ content){
// summary:
// Hook for set('label', ...) to work.
// description:
// Set the label (text) of the button; takes an HTML string.
this._set("label", content);
(this.containerNode||this.focusNode).innerHTML = content;
}
});
});
},
'dojo/colors':function(){
define(["./_base/kernel", "./_base/lang", "./_base/Color", "./_base/array"], function(dojo, lang, Color, ArrayUtil) {
// module:
// dojo/colors
// summary:
// TODOC
var ColorExt = lang.getObject("dojo.colors", true);
//TODO: this module appears to break naming conventions
/*=====
lang.mixin(dojo, {
colors: {
// summary: Color utilities, extending Base dojo.Color
}
});
=====*/
// this is a standard conversion prescribed by the CSS3 Color Module
var hue2rgb = function(m1, m2, h){
if(h < 0){ ++h; }
if(h > 1){ --h; }
var h6 = 6 * h;
if(h6 < 1){ return m1 + (m2 - m1) * h6; }
if(2 * h < 1){ return m2; }
if(3 * h < 2){ return m1 + (m2 - m1) * (2 / 3 - h) * 6; }
return m1;
};
// Override base Color.fromRgb with the impl in this module
dojo.colorFromRgb = Color.fromRgb = function(/*String*/ color, /*dojo.Color?*/ obj){
// summary:
// get rgb(a) array from css-style color declarations
// description:
// this function can handle all 4 CSS3 Color Module formats: rgb,
// rgba, hsl, hsla, including rgb(a) with percentage values.
var m = color.toLowerCase().match(/^(rgba?|hsla?)\(([\s\.\-,%0-9]+)\)/);
if(m){
var c = m[2].split(/\s*,\s*/), l = c.length, t = m[1], a;
if((t == "rgb" && l == 3) || (t == "rgba" && l == 4)){
var r = c[0];
if(r.charAt(r.length - 1) == "%"){
// 3 rgb percentage values
a = ArrayUtil.map(c, function(x){
return parseFloat(x) * 2.56;
});
if(l == 4){ a[3] = c[3]; }
return Color.fromArray(a, obj); // dojo.Color
}
return Color.fromArray(c, obj); // dojo.Color
}
if((t == "hsl" && l == 3) || (t == "hsla" && l == 4)){
// normalize hsl values
var H = ((parseFloat(c[0]) % 360) + 360) % 360 / 360,
S = parseFloat(c[1]) / 100,
L = parseFloat(c[2]) / 100,
// calculate rgb according to the algorithm
// recommended by the CSS3 Color Module
m2 = L <= 0.5 ? L * (S + 1) : L + S - L * S,
m1 = 2 * L - m2;
a = [
hue2rgb(m1, m2, H + 1 / 3) * 256,
hue2rgb(m1, m2, H) * 256,
hue2rgb(m1, m2, H - 1 / 3) * 256,
1
];
if(l == 4){ a[3] = c[3]; }
return Color.fromArray(a, obj); // dojo.Color
}
}
return null; // dojo.Color
};
var confine = function(c, low, high){
// summary:
// sanitize a color component by making sure it is a number,
// and clamping it to valid values
c = Number(c);
return isNaN(c) ? high : c < low ? low : c > high ? high : c; // Number
};
Color.prototype.sanitize = function(){
// summary: makes sure that the object has correct attributes
var t = this;
t.r = Math.round(confine(t.r, 0, 255));
t.g = Math.round(confine(t.g, 0, 255));
t.b = Math.round(confine(t.b, 0, 255));
t.a = confine(t.a, 0, 1);
return this; // dojo.Color
};
ColorExt.makeGrey = Color.makeGrey = function(/*Number*/ g, /*Number?*/ a){
// summary: creates a greyscale color with an optional alpha
return Color.fromArray([g, g, g, a]); // dojo.Color
};
// mixin all CSS3 named colors not already in _base, along with SVG 1.0 variant spellings
lang.mixin(Color.named, {
"aliceblue": [240,248,255],
"antiquewhite": [250,235,215],
"aquamarine": [127,255,212],
"azure": [240,255,255],
"beige": [245,245,220],
"bisque": [255,228,196],
"blanchedalmond": [255,235,205],
"blueviolet": [138,43,226],
"brown": [165,42,42],
"burlywood": [222,184,135],
"cadetblue": [95,158,160],
"chartreuse": [127,255,0],
"chocolate": [210,105,30],
"coral": [255,127,80],
"cornflowerblue": [100,149,237],
"cornsilk": [255,248,220],
"crimson": [220,20,60],
"cyan": [0,255,255],
"darkblue": [0,0,139],
"darkcyan": [0,139,139],
"darkgoldenrod": [184,134,11],
"darkgray": [169,169,169],
"darkgreen": [0,100,0],
"darkgrey": [169,169,169],
"darkkhaki": [189,183,107],
"darkmagenta": [139,0,139],
"darkolivegreen": [85,107,47],
"darkorange": [255,140,0],
"darkorchid": [153,50,204],
"darkred": [139,0,0],
"darksalmon": [233,150,122],
"darkseagreen": [143,188,143],
"darkslateblue": [72,61,139],
"darkslategray": [47,79,79],
"darkslategrey": [47,79,79],
"darkturquoise": [0,206,209],
"darkviolet": [148,0,211],
"deeppink": [255,20,147],
"deepskyblue": [0,191,255],
"dimgray": [105,105,105],
"dimgrey": [105,105,105],
"dodgerblue": [30,144,255],
"firebrick": [178,34,34],
"floralwhite": [255,250,240],
"forestgreen": [34,139,34],
"gainsboro": [220,220,220],
"ghostwhite": [248,248,255],
"gold": [255,215,0],
"goldenrod": [218,165,32],
"greenyellow": [173,255,47],
"grey": [128,128,128],
"honeydew": [240,255,240],
"hotpink": [255,105,180],
"indianred": [205,92,92],
"indigo": [75,0,130],
"ivory": [255,255,240],
"khaki": [240,230,140],
"lavender": [230,230,250],
"lavenderblush": [255,240,245],
"lawngreen": [124,252,0],
"lemonchiffon": [255,250,205],
"lightblue": [173,216,230],
"lightcoral": [240,128,128],
"lightcyan": [224,255,255],
"lightgoldenrodyellow": [250,250,210],
"lightgray": [211,211,211],
"lightgreen": [144,238,144],
"lightgrey": [211,211,211],
"lightpink": [255,182,193],
"lightsalmon": [255,160,122],
"lightseagreen": [32,178,170],
"lightskyblue": [135,206,250],
"lightslategray": [119,136,153],
"lightslategrey": [119,136,153],
"lightsteelblue": [176,196,222],
"lightyellow": [255,255,224],
"limegreen": [50,205,50],
"linen": [250,240,230],
"magenta": [255,0,255],
"mediumaquamarine": [102,205,170],
"mediumblue": [0,0,205],
"mediumorchid": [186,85,211],
"mediumpurple": [147,112,219],
"mediumseagreen": [60,179,113],
"mediumslateblue": [123,104,238],
"mediumspringgreen": [0,250,154],
"mediumturquoise": [72,209,204],
"mediumvioletred": [199,21,133],
"midnightblue": [25,25,112],
"mintcream": [245,255,250],
"mistyrose": [255,228,225],
"moccasin": [255,228,181],
"navajowhite": [255,222,173],
"oldlace": [253,245,230],
"olivedrab": [107,142,35],
"orange": [255,165,0],
"orangered": [255,69,0],
"orchid": [218,112,214],
"palegoldenrod": [238,232,170],
"palegreen": [152,251,152],
"paleturquoise": [175,238,238],
"palevioletred": [219,112,147],
"papayawhip": [255,239,213],
"peachpuff": [255,218,185],
"peru": [205,133,63],
"pink": [255,192,203],
"plum": [221,160,221],
"powderblue": [176,224,230],
"rosybrown": [188,143,143],
"royalblue": [65,105,225],
"saddlebrown": [139,69,19],
"salmon": [250,128,114],
"sandybrown": [244,164,96],
"seagreen": [46,139,87],
"seashell": [255,245,238],
"sienna": [160,82,45],
"skyblue": [135,206,235],
"slateblue": [106,90,205],
"slategray": [112,128,144],
"slategrey": [112,128,144],
"snow": [255,250,250],
"springgreen": [0,255,127],
"steelblue": [70,130,180],
"tan": [210,180,140],
"thistle": [216,191,216],
"tomato": [255,99,71],
"turquoise": [64,224,208],
"violet": [238,130,238],
"wheat": [245,222,179],
"whitesmoke": [245,245,245],
"yellowgreen": [154,205,50]
});
return Color;
});
},
'url:dijit/form/templates/Spinner.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\"\n\tid=\"widget_${id}\" role=\"presentation\"\n\t><div class=\"dijitReset dijitButtonNode dijitSpinnerButtonContainer\"\n\t\t><input class=\"dijitReset dijitInputField dijitSpinnerButtonInner\" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t/><div class=\"dijitReset dijitLeft dijitButtonNode dijitArrowButton dijitUpArrowButton\"\n\t\t\tdata-dojo-attach-point=\"upArrowNode\"\n\t\t\t><div class=\"dijitArrowButtonInner\"\n\t\t\t\t><input class=\"dijitReset dijitInputField\" value=\"&#9650;\" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t\t\t${_buttonInputDisabled}\n\t\t\t/></div\n\t\t></div\n\t\t><div class=\"dijitReset dijitLeft dijitButtonNode dijitArrowButton dijitDownArrowButton\"\n\t\t\tdata-dojo-attach-point=\"downArrowNode\"\n\t\t\t><div class=\"dijitArrowButtonInner\"\n\t\t\t\t><input class=\"dijitReset dijitInputField\" value=\"&#9660;\" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t\t\t${_buttonInputDisabled}\n\t\t\t/></div\n\t\t></div\n\t></div\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935;\" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class='dijitReset dijitInputInner' data-dojo-attach-point=\"textbox,focusNode\" type=\"${type}\" data-dojo-attach-event=\"onkeypress:_onKeyPress\"\n\t\t\trole=\"spinbutton\" autocomplete=\"off\" ${!nameAttrSetting}\n\t/></div\n></div>\n",
'dijit/tree/_dndContainer':function(){
define([
"dojo/aspect", // aspect.after
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.add domClass.remove domClass.replace
"dojo/_base/event", // event.stop
"dojo/_base/lang", // lang.getObject lang.mixin lang.hitch
"dojo/mouse", // mouse.enter, mouse.leave
"dojo/on"
], function(aspect, declare, domClass, event, lang, mouse, on){
// module:
// dijit/tree/_dndContainer
// summary:
// This is a base class for `dijit.tree._dndSelector`, and isn't meant to be used directly.
// It's modeled after `dojo.dnd.Container`.
return declare("dijit.tree._dndContainer", null, {
// summary:
// This is a base class for `dijit.tree._dndSelector`, and isn't meant to be used directly.
// It's modeled after `dojo.dnd.Container`.
// tags:
// protected
/*=====
// current: DomNode
// The currently hovered TreeNode.rowNode (which is the DOM node
// associated w/a given node in the tree, excluding it's descendants)
current: null,
=====*/
constructor: function(tree, params){
// summary:
// A constructor of the Container
// tree: Node
// Node or node's id to build the container on
// params: dijit.tree.__SourceArgs
// A dict of parameters, which gets mixed into the object
// tags:
// private
this.tree = tree;
this.node = tree.domNode; // TODO: rename; it's not a TreeNode but the whole Tree
lang.mixin(this, params);
// class-specific variables
this.current = null; // current TreeNode's DOM node
// states
this.containerState = "";
domClass.add(this.node, "dojoDndContainer");
// set up events
this.events = [
// container level events
on(this.node, mouse.enter, lang.hitch(this, "onOverEvent")),
on(this.node, mouse.leave, lang.hitch(this, "onOutEvent")),
// switching between TreeNodes
aspect.after(this.tree, "_onNodeMouseEnter", lang.hitch(this, "onMouseOver"), true),
aspect.after(this.tree, "_onNodeMouseLeave", lang.hitch(this, "onMouseOut"), true),
// cancel text selection and text dragging
on(this.node, "dragstart", lang.hitch(event, "stop")),
on(this.node, "selectstart", lang.hitch(event, "stop"))
];
},
destroy: function(){
// summary:
// Prepares this object to be garbage-collected
var h;
while(h = this.events.pop()){ h.remove(); }
// this.clearItems();
this.node = this.parent = null;
},
// mouse events
onMouseOver: function(widget /*===== , evt =====*/){
// summary:
// Called when mouse is moved over a TreeNode
// widget: TreeNode
// evt: Event
// tags:
// protected
this.current = widget;
},
onMouseOut: function(/*===== widget, evt =====*/){
// summary:
// Called when mouse is moved away from a TreeNode
// widget: TreeNode
// evt: Event
// tags:
// protected
this.current = null;
},
_changeState: function(type, newState){
// summary:
// Changes a named state to new state value
// type: String
// A name of the state to change
// newState: String
// new state
var prefix = "dojoDnd" + type;
var state = type.toLowerCase() + "State";
//domClass.replace(this.node, prefix + newState, prefix + this[state]);
domClass.replace(this.node, prefix + newState, prefix + this[state]);
this[state] = newState;
},
_addItemClass: function(node, type){
// summary:
// Adds a class with prefix "dojoDndItem"
// node: Node
// A node
// type: String
// A variable suffix for a class name
domClass.add(node, "dojoDndItem" + type);
},
_removeItemClass: function(node, type){
// summary:
// Removes a class with prefix "dojoDndItem"
// node: Node
// A node
// type: String
// A variable suffix for a class name
domClass.remove(node, "dojoDndItem" + type);
},
onOverEvent: function(){
// summary:
// This function is called once, when mouse is over our container
// tags:
// protected
this._changeState("Container", "Over");
},
onOutEvent: function(){
// summary:
// This function is called once, when mouse is out of our container
// tags:
// protected
this._changeState("Container", "");
}
});
});
},
'dojo/date/locale':function(){
define([
"../_base/kernel",
"../_base/lang",
"../_base/array",
"../date",
"../cldr/supplemental",
"../regexp",
"../string",
"../i18n!../cldr/nls/gregorian"
], function(dojo, lang, array, date, cldr, regexp, string, gregorian) {
// module:
// dojo/date/locale
// summary:
// This modules defines dojo.date.locale, localization methods for Date.
lang.getObject("date.locale", true, dojo);
// Localization methods for Date. Honor local customs using locale-dependent dojo.cldr data.
// Load the bundles containing localization information for
// names and formats
//NOTE: Everything in this module assumes Gregorian calendars.
// Other calendars will be implemented in separate modules.
// Format a pattern without literals
function formatPattern(dateObject, bundle, options, pattern){
return pattern.replace(/([a-z])\1*/ig, function(match){
var s, pad,
c = match.charAt(0),
l = match.length,
widthList = ["abbr", "wide", "narrow"];
switch(c){
case 'G':
s = bundle[(l < 4) ? "eraAbbr" : "eraNames"][dateObject.getFullYear() < 0 ? 0 : 1];
break;
case 'y':
s = dateObject.getFullYear();
switch(l){
case 1:
break;
case 2:
if(!options.fullYear){
s = String(s); s = s.substr(s.length - 2);
break;
}
// fallthrough
default:
pad = true;
}
break;
case 'Q':
case 'q':
s = Math.ceil((dateObject.getMonth()+1)/3);
// switch(l){
// case 1: case 2:
pad = true;
// break;
// case 3: case 4: // unimplemented
// }
break;
case 'M':
var m = dateObject.getMonth();
if(l<3){
s = m+1; pad = true;
}else{
var propM = ["months", "format", widthList[l-3]].join("-");
s = bundle[propM][m];
}
break;
case 'w':
var firstDay = 0;
s = dojo.date.locale._getWeekOfYear(dateObject, firstDay); pad = true;
break;
case 'd':
s = dateObject.getDate(); pad = true;
break;
case 'D':
s = dojo.date.locale._getDayOfYear(dateObject); pad = true;
break;
case 'E':
var d = dateObject.getDay();
if(l<3){
s = d+1; pad = true;
}else{
var propD = ["days", "format", widthList[l-3]].join("-");
s = bundle[propD][d];
}
break;
case 'a':
var timePeriod = (dateObject.getHours() < 12) ? 'am' : 'pm';
s = options[timePeriod] || bundle['dayPeriods-format-wide-' + timePeriod];
break;
case 'h':
case 'H':
case 'K':
case 'k':
var h = dateObject.getHours();
// strange choices in the date format make it impossible to write this succinctly
switch (c){
case 'h': // 1-12
s = (h % 12) || 12;
break;
case 'H': // 0-23
s = h;
break;
case 'K': // 0-11
s = (h % 12);
break;
case 'k': // 1-24
s = h || 24;
break;
}
pad = true;
break;
case 'm':
s = dateObject.getMinutes(); pad = true;
break;
case 's':
s = dateObject.getSeconds(); pad = true;
break;
case 'S':
s = Math.round(dateObject.getMilliseconds() * Math.pow(10, l-3)); pad = true;
break;
case 'v': // FIXME: don't know what this is. seems to be same as z?
case 'z':
// We only have one timezone to offer; the one from the browser
s = dojo.date.locale._getZone(dateObject, true, options);
if(s){break;}
l=4;
// fallthrough... use GMT if tz not available
case 'Z':
var offset = dojo.date.locale._getZone(dateObject, false, options);
var tz = [
(offset<=0 ? "+" : "-"),
string.pad(Math.floor(Math.abs(offset)/60), 2),
string.pad(Math.abs(offset)% 60, 2)
];
if(l==4){
tz.splice(0, 0, "GMT");
tz.splice(3, 0, ":");
}
s = tz.join("");
break;
// case 'Y': case 'u': case 'W': case 'F': case 'g': case 'A': case 'e':
// console.log(match+" modifier unimplemented");
default:
throw new Error("dojo.date.locale.format: invalid pattern char: "+pattern);
}
if(pad){ s = string.pad(s, l); }
return s;
});
}
/*=====
dojo.date.locale.__FormatOptions = function(){
// selector: String
// choice of 'time','date' (default: date and time)
// formatLength: String
// choice of long, short, medium or full (plus any custom additions). Defaults to 'short'
// datePattern:String
// override pattern with this string
// timePattern:String
// override pattern with this string
// am: String
// override strings for am in times
// pm: String
// override strings for pm in times
// locale: String
// override the locale used to determine formatting rules
// fullYear: Boolean
// (format only) use 4 digit years whenever 2 digit years are called for
// strict: Boolean
// (parse only) strict parsing, off by default
this.selector = selector;
this.formatLength = formatLength;
this.datePattern = datePattern;
this.timePattern = timePattern;
this.am = am;
this.pm = pm;
this.locale = locale;
this.fullYear = fullYear;
this.strict = strict;
}
=====*/
dojo.date.locale._getZone = function(/*Date*/dateObject, /*boolean*/getName, /*dojo.date.locale.__FormatOptions?*/options){
// summary:
// Returns the zone (or offset) for the given date and options. This
// is broken out into a separate function so that it can be overridden
// by timezone-aware code.
//
// dateObject:
// the date and/or time being formatted.
//
// getName:
// Whether to return the timezone string (if true), or the offset (if false)
//
// options:
// The options being used for formatting
if(getName){
return date.getTimezoneName(dateObject);
}else{
return dateObject.getTimezoneOffset();
}
};
dojo.date.locale.format = function(/*Date*/dateObject, /*dojo.date.locale.__FormatOptions?*/options){
// summary:
// Format a Date object as a String, using locale-specific settings.
//
// description:
// Create a string from a Date object using a known localized pattern.
// By default, this method formats both date and time from dateObject.
// Formatting patterns are chosen appropriate to the locale. Different
// formatting lengths may be chosen, with "full" used by default.
// Custom patterns may be used or registered with translations using
// the dojo.date.locale.addCustomFormats method.
// Formatting patterns are implemented using [the syntax described at
// unicode.org](http://www.unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns)
//
// dateObject:
// the date and/or time to be formatted. If a time only is formatted,
// the values in the year, month, and day fields are irrelevant. The
// opposite is true when formatting only dates.
options = options || {};
var locale = dojo.i18n.normalizeLocale(options.locale),
formatLength = options.formatLength || 'short',
bundle = dojo.date.locale._getGregorianBundle(locale),
str = [],
sauce = lang.hitch(this, formatPattern, dateObject, bundle, options);
if(options.selector == "year"){
return _processPattern(bundle["dateFormatItem-yyyy"] || "yyyy", sauce);
}
var pattern;
if(options.selector != "date"){
pattern = options.timePattern || bundle["timeFormat-"+formatLength];
if(pattern){str.push(_processPattern(pattern, sauce));}
}
if(options.selector != "time"){
pattern = options.datePattern || bundle["dateFormat-"+formatLength];
if(pattern){str.push(_processPattern(pattern, sauce));}
}
return str.length == 1 ? str[0] : bundle["dateTimeFormat-"+formatLength].replace(/\{(\d+)\}/g,
function(match, key){ return str[key]; }); // String
};
dojo.date.locale.regexp = function(/*dojo.date.locale.__FormatOptions?*/options){
// summary:
// Builds the regular needed to parse a localized date
return dojo.date.locale._parseInfo(options).regexp; // String
};
dojo.date.locale._parseInfo = function(/*dojo.date.locale.__FormatOptions?*/options){
options = options || {};
var locale = dojo.i18n.normalizeLocale(options.locale),
bundle = dojo.date.locale._getGregorianBundle(locale),
formatLength = options.formatLength || 'short',
datePattern = options.datePattern || bundle["dateFormat-" + formatLength],
timePattern = options.timePattern || bundle["timeFormat-" + formatLength],
pattern;
if(options.selector == 'date'){
pattern = datePattern;
}else if(options.selector == 'time'){
pattern = timePattern;
}else{
pattern = bundle["dateTimeFormat-"+formatLength].replace(/\{(\d+)\}/g,
function(match, key){ return [timePattern, datePattern][key]; });
}
var tokens = [],
re = _processPattern(pattern, lang.hitch(this, _buildDateTimeRE, tokens, bundle, options));
return {regexp: re, tokens: tokens, bundle: bundle};
};
dojo.date.locale.parse = function(/*String*/value, /*dojo.date.locale.__FormatOptions?*/options){
// summary:
// Convert a properly formatted string to a primitive Date object,
// using locale-specific settings.
//
// description:
// Create a Date object from a string using a known localized pattern.
// By default, this method parses looking for both date and time in the string.
// Formatting patterns are chosen appropriate to the locale. Different
// formatting lengths may be chosen, with "full" used by default.
// Custom patterns may be used or registered with translations using
// the dojo.date.locale.addCustomFormats method.
//
// Formatting patterns are implemented using [the syntax described at
// unicode.org](http://www.unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns)
// When two digit years are used, a century is chosen according to a sliding
// window of 80 years before and 20 years after present year, for both `yy` and `yyyy` patterns.
// year < 100CE requires strict mode.
//
// value:
// A string representation of a date
// remove non-printing bidi control chars from input and pattern
var controlChars = /[\u200E\u200F\u202A\u202E]/g,
info = dojo.date.locale._parseInfo(options),
tokens = info.tokens, bundle = info.bundle,
re = new RegExp("^" + info.regexp.replace(controlChars, "") + "$",
info.strict ? "" : "i"),
match = re.exec(value && value.replace(controlChars, ""));
if(!match){ return null; } // null
var widthList = ['abbr', 'wide', 'narrow'],
result = [1970,0,1,0,0,0,0], // will get converted to a Date at the end
amPm = "",
valid = dojo.every(match, function(v, i){
if(!i){return true;}
var token=tokens[i-1];
var l=token.length;
switch(token.charAt(0)){
case 'y':
if(l != 2 && options.strict){
//interpret year literally, so '5' would be 5 A.D.
result[0] = v;
}else{
if(v<100){
v = Number(v);
//choose century to apply, according to a sliding window
//of 80 years before and 20 years after present year
var year = '' + new Date().getFullYear(),
century = year.substring(0, 2) * 100,
cutoff = Math.min(Number(year.substring(2, 4)) + 20, 99);
result[0] = (v < cutoff) ? century + v : century - 100 + v;
}else{
//we expected 2 digits and got more...
if(options.strict){
return false;
}
//interpret literally, so '150' would be 150 A.D.
//also tolerate '1950', if 'yyyy' input passed to 'yy' format
result[0] = v;
}
}
break;
case 'M':
if(l>2){
var months = bundle['months-format-' + widthList[l-3]].concat();
if(!options.strict){
//Tolerate abbreviating period in month part
//Case-insensitive comparison
v = v.replace(".","").toLowerCase();
months = dojo.map(months, function(s){ return s.replace(".","").toLowerCase(); } );
}
v = dojo.indexOf(months, v);
if(v == -1){
// console.log("dojo.date.locale.parse: Could not parse month name: '" + v + "'.");
return false;
}
}else{
v--;
}
result[1] = v;
break;
case 'E':
case 'e':
var days = bundle['days-format-' + widthList[l-3]].concat();
if(!options.strict){
//Case-insensitive comparison
v = v.toLowerCase();
days = dojo.map(days, function(d){return d.toLowerCase();});
}
v = dojo.indexOf(days, v);
if(v == -1){
// console.log("dojo.date.locale.parse: Could not parse weekday name: '" + v + "'.");
return false;
}
//TODO: not sure what to actually do with this input,
//in terms of setting something on the Date obj...?
//without more context, can't affect the actual date
//TODO: just validate?
break;
case 'D':
result[1] = 0;
// fallthrough...
case 'd':
result[2] = v;
break;
case 'a': //am/pm
var am = options.am || bundle['dayPeriods-format-wide-am'],
pm = options.pm || bundle['dayPeriods-format-wide-pm'];
if(!options.strict){
var period = /\./g;
v = v.replace(period,'').toLowerCase();
am = am.replace(period,'').toLowerCase();
pm = pm.replace(period,'').toLowerCase();
}
if(options.strict && v != am && v != pm){
// console.log("dojo.date.locale.parse: Could not parse am/pm part.");
return false;
}
// we might not have seen the hours field yet, so store the state and apply hour change later
amPm = (v == pm) ? 'p' : (v == am) ? 'a' : '';
break;
case 'K': //hour (1-24)
if(v == 24){ v = 0; }
// fallthrough...
case 'h': //hour (1-12)
case 'H': //hour (0-23)
case 'k': //hour (0-11)
//TODO: strict bounds checking, padding
if(v > 23){
// console.log("dojo.date.locale.parse: Illegal hours value");
return false;
}
//in the 12-hour case, adjusting for am/pm requires the 'a' part
//which could come before or after the hour, so we will adjust later
result[3] = v;
break;
case 'm': //minutes
result[4] = v;
break;
case 's': //seconds
result[5] = v;
break;
case 'S': //milliseconds
result[6] = v;
// break;
// case 'w':
//TODO var firstDay = 0;
// default:
//TODO: throw?
// console.log("dojo.date.locale.parse: unsupported pattern char=" + token.charAt(0));
}
return true;
});
var hours = +result[3];
if(amPm === 'p' && hours < 12){
result[3] = hours + 12; //e.g., 3pm -> 15
}else if(amPm === 'a' && hours == 12){
result[3] = 0; //12am -> 0
}
//TODO: implement a getWeekday() method in order to test
//validity of input strings containing 'EEE' or 'EEEE'...
var dateObject = new Date(result[0], result[1], result[2], result[3], result[4], result[5], result[6]); // Date
if(options.strict){
dateObject.setFullYear(result[0]);
}
// Check for overflow. The Date() constructor normalizes things like April 32nd...
//TODO: why isn't this done for times as well?
var allTokens = tokens.join(""),
dateToken = allTokens.indexOf('d') != -1,
monthToken = allTokens.indexOf('M') != -1;
if(!valid ||
(monthToken && dateObject.getMonth() > result[1]) ||
(dateToken && dateObject.getDate() > result[2])){
return null;
}
// Check for underflow, due to DST shifts. See #9366
// This assumes a 1 hour dst shift correction at midnight
// We could compare the timezone offset after the shift and add the difference instead.
if((monthToken && dateObject.getMonth() < result[1]) ||
(dateToken && dateObject.getDate() < result[2])){
dateObject = date.add(dateObject, "hour", 1);
}
return dateObject; // Date
};
function _processPattern(pattern, applyPattern, applyLiteral, applyAll){
//summary: Process a pattern with literals in it
// Break up on single quotes, treat every other one as a literal, except '' which becomes '
var identity = function(x){return x;};
applyPattern = applyPattern || identity;
applyLiteral = applyLiteral || identity;
applyAll = applyAll || identity;
//split on single quotes (which escape literals in date format strings)
//but preserve escaped single quotes (e.g., o''clock)
var chunks = pattern.match(/(''|[^'])+/g),
literal = pattern.charAt(0) == "'";
dojo.forEach(chunks, function(chunk, i){
if(!chunk){
chunks[i]='';
}else{
chunks[i]=(literal ? applyLiteral : applyPattern)(chunk.replace(/''/g, "'"));
literal = !literal;
}
});
return applyAll(chunks.join(''));
}
function _buildDateTimeRE(tokens, bundle, options, pattern){
pattern = regexp.escapeString(pattern);
if(!options.strict){ pattern = pattern.replace(" a", " ?a"); } // kludge to tolerate no space before am/pm
return pattern.replace(/([a-z])\1*/ig, function(match){
// Build a simple regexp. Avoid captures, which would ruin the tokens list
var s,
c = match.charAt(0),
l = match.length,
p2 = '', p3 = '';
if(options.strict){
if(l > 1){ p2 = '0' + '{'+(l-1)+'}'; }
if(l > 2){ p3 = '0' + '{'+(l-2)+'}'; }
}else{
p2 = '0?'; p3 = '0{0,2}';
}
switch(c){
case 'y':
s = '\\d{2,4}';
break;
case 'M':
s = (l>2) ? '\\S+?' : '1[0-2]|'+p2+'[1-9]';
break;
case 'D':
s = '[12][0-9][0-9]|3[0-5][0-9]|36[0-6]|'+p2+'[1-9][0-9]|'+p3+'[1-9]';
break;
case 'd':
s = '3[01]|[12]\\d|'+p2+'[1-9]';
break;
case 'w':
s = '[1-4][0-9]|5[0-3]|'+p2+'[1-9]';
break;
case 'E':
s = '\\S+';
break;
case 'h': //hour (1-12)
s = '1[0-2]|'+p2+'[1-9]';
break;
case 'k': //hour (0-11)
s = '1[01]|'+p2+'\\d';
break;
case 'H': //hour (0-23)
s = '1\\d|2[0-3]|'+p2+'\\d';
break;
case 'K': //hour (1-24)
s = '1\\d|2[0-4]|'+p2+'[1-9]';
break;
case 'm':
case 's':
s = '[0-5]\\d';
break;
case 'S':
s = '\\d{'+l+'}';
break;
case 'a':
var am = options.am || bundle['dayPeriods-format-wide-am'],
pm = options.pm || bundle['dayPeriods-format-wide-pm'];
s = am + '|' + pm;
if(!options.strict){
if(am != am.toLowerCase()){ s += '|' + am.toLowerCase(); }
if(pm != pm.toLowerCase()){ s += '|' + pm.toLowerCase(); }
if(s.indexOf('.') != -1){ s += '|' + s.replace(/\./g, ""); }
}
s = s.replace(/\./g, "\\.");
break;
default:
// case 'v':
// case 'z':
// case 'Z':
s = ".*";
// console.log("parse of date format, pattern=" + pattern);
}
if(tokens){ tokens.push(match); }
return "(" + s + ")"; // add capture
}).replace(/[\xa0 ]/g, "[\\s\\xa0]"); // normalize whitespace. Need explicit handling of \xa0 for IE.
}
var _customFormats = [];
dojo.date.locale.addCustomFormats = function(/*String*/packageName, /*String*/bundleName){
// summary:
// Add a reference to a bundle containing localized custom formats to be
// used by date/time formatting and parsing routines.
//
// description:
// The user may add custom localized formats where the bundle has properties following the
// same naming convention used by dojo.cldr: `dateFormat-xxxx` / `timeFormat-xxxx`
// The pattern string should match the format used by the CLDR.
// See dojo.date.locale.format() for details.
// The resources must be loaded by dojo.requireLocalization() prior to use
_customFormats.push({pkg:packageName,name:bundleName});
};
dojo.date.locale._getGregorianBundle = function(/*String*/locale){
var gregorian = {};
dojo.forEach(_customFormats, function(desc){
var bundle = dojo.i18n.getLocalization(desc.pkg, desc.name, locale);
gregorian = lang.mixin(gregorian, bundle);
}, this);
return gregorian; /*Object*/
};
dojo.date.locale.addCustomFormats("dojo.cldr","gregorian");
dojo.date.locale.getNames = function(/*String*/item, /*String*/type, /*String?*/context, /*String?*/locale){
// summary:
// Used to get localized strings from dojo.cldr for day or month names.
//
// item:
// 'months' || 'days'
// type:
// 'wide' || 'abbr' || 'narrow' (e.g. "Monday", "Mon", or "M" respectively, in English)
// context:
// 'standAlone' || 'format' (default)
// locale:
// override locale used to find the names
var label,
lookup = dojo.date.locale._getGregorianBundle(locale),
props = [item, context, type];
if(context == 'standAlone'){
var key = props.join('-');
label = lookup[key];
// Fall back to 'format' flavor of name
if(label[0] == 1){ label = undefined; } // kludge, in the absence of real aliasing support in dojo.cldr
}
props[1] = 'format';
// return by copy so changes won't be made accidentally to the in-memory model
return (label || lookup[props.join('-')]).concat(); /*Array*/
};
dojo.date.locale.isWeekend = function(/*Date?*/dateObject, /*String?*/locale){
// summary:
// Determines if the date falls on a weekend, according to local custom.
var weekend = cldr.getWeekend(locale),
day = (dateObject || new Date()).getDay();
if(weekend.end < weekend.start){
weekend.end += 7;
if(day < weekend.start){ day += 7; }
}
return day >= weekend.start && day <= weekend.end; // Boolean
};
// These are used only by format and strftime. Do they need to be public? Which module should they go in?
dojo.date.locale._getDayOfYear = function(/*Date*/dateObject){
// summary: gets the day of the year as represented by dateObject
return date.difference(new Date(dateObject.getFullYear(), 0, 1, dateObject.getHours()), dateObject) + 1; // Number
};
dojo.date.locale._getWeekOfYear = function(/*Date*/dateObject, /*Number*/firstDayOfWeek){
if(arguments.length == 1){ firstDayOfWeek = 0; } // Sunday
var firstDayOfYear = new Date(dateObject.getFullYear(), 0, 1).getDay(),
adj = (firstDayOfYear - firstDayOfWeek + 7) % 7,
week = Math.floor((dojo.date.locale._getDayOfYear(dateObject) + adj - 1) / 7);
// if year starts on the specified day, start counting weeks at 1
if(firstDayOfYear == firstDayOfWeek){ week++; }
return week; // Number
};
return dojo.date.locale;
});
},
'url:dijit/templates/InlineEditBox.html':"<span data-dojo-attach-point=\"editNode\" role=\"presentation\" style=\"position: absolute; visibility:hidden\" class=\"dijitReset dijitInline\"\n\tdata-dojo-attach-event=\"onkeypress: _onKeyPress\"\n\t><span data-dojo-attach-point=\"editorPlaceholder\"></span\n\t><span data-dojo-attach-point=\"buttonContainer\"\n\t\t><button data-dojo-type=\"dijit.form.Button\" data-dojo-props=\"label: '${buttonSave}', 'class': 'saveButton'\"\n\t\t\tdata-dojo-attach-point=\"saveButton\" data-dojo-attach-event=\"onClick:save\"></button\n\t\t><button data-dojo-type=\"dijit.form.Button\" data-dojo-props=\"label: '${buttonCancel}', 'class': 'cancelButton'\"\n\t\t\tdata-dojo-attach-point=\"cancelButton\" data-dojo-attach-event=\"onClick:cancel\"></button\n\t></span\n></span>\n",
'dijit/form/VerticalRule':function(){
define([
"dojo/_base/declare", // declare
"./HorizontalRule"
], function(declare, HorizontalRule){
/*=====
var HorizontalRule = dijit.form.HorizontalRule;
=====*/
// module:
// dijit/form/VerticalRule
// summary:
// Hash marks for the `dijit.form.VerticalSlider`
return declare("dijit.form.VerticalRule", HorizontalRule, {
// summary:
// Hash marks for the `dijit.form.VerticalSlider`
templateString: '<div class="dijitRuleContainer dijitRuleContainerV"></div>',
_positionPrefix: '<div class="dijitRuleMark dijitRuleMarkV" style="top:',
/*=====
// container: String
// This is either "leftDecoration" or "rightDecoration",
// to indicate whether this rule goes to the left or to the right of the slider.
// Note that on RTL system, "leftDecoration" would actually go to the right, and vice-versa.
container: "",
=====*/
// Overrides HorizontalRule._isHorizontal
_isHorizontal: false
});
});
},
'dijit/form/_FormSelectWidget':function(){
define([
"dojo/_base/array", // array.filter array.forEach array.map array.some
"dojo/aspect", // aspect.after
"dojo/data/util/sorter", // util.sorter.createSortFunction
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"dojo/dom-class", // domClass.toggle
"dojo/_base/kernel", // _scopeName
"dojo/_base/lang", // lang.delegate lang.isArray lang.isObject lang.hitch
"dojo/query", // query
"./_FormValueWidget"
], function(array, aspect, sorter, declare, dom, domClass, kernel, lang, query, _FormValueWidget){
/*=====
var _FormValueWidget = dijit.form._FormValueWidget;
=====*/
// module:
// dijit/form/_FormSelectWidget
// summary:
// Extends _FormValueWidget in order to provide "select-specific"
// values - i.e., those values that are unique to <select> elements.
/*=====
dijit.form.__SelectOption = function(){
// value: String
// The value of the option. Setting to empty (or missing) will
// place a separator at that location
// label: String
// The label for our option. It can contain html tags.
// selected: Boolean
// Whether or not we are a selected option
// disabled: Boolean
// Whether or not this specific option is disabled
this.value = value;
this.label = label;
this.selected = selected;
this.disabled = disabled;
}
=====*/
return declare("dijit.form._FormSelectWidget", _FormValueWidget, {
// summary:
// Extends _FormValueWidget in order to provide "select-specific"
// values - i.e., those values that are unique to <select> elements.
// This also provides the mechanism for reading the elements from
// a store, if desired.
// multiple: [const] Boolean
// Whether or not we are multi-valued
multiple: false,
// options: dijit.form.__SelectOption[]
// The set of options for our select item. Roughly corresponds to
// the html <option> tag.
options: null,
// store: dojo.data.api.Identity
// A store which, at the very least implements dojo.data.api.Identity
// to use for getting our list of options - rather than reading them
// from the <option> html tags.
store: null,
// query: object
// A query to use when fetching items from our store
query: null,
// queryOptions: object
// Query options to use when fetching from the store
queryOptions: null,
// onFetch: Function
// A callback to do with an onFetch - but before any items are actually
// iterated over (i.e. to filter even further what you want to add)
onFetch: null,
// sortByLabel: Boolean
// Flag to sort the options returned from a store by the label of
// the store.
sortByLabel: true,
// loadChildrenOnOpen: Boolean
// By default loadChildren is called when the items are fetched from the
// store. This property allows delaying loadChildren (and the creation
// of the options/menuitems) until the user clicks the button to open the
// dropdown.
loadChildrenOnOpen: false,
getOptions: function(/*anything*/ valueOrIdx){
// summary:
// Returns a given option (or options).
// valueOrIdx:
// If passed in as a string, that string is used to look up the option
// in the array of options - based on the value property.
// (See dijit.form.__SelectOption).
//
// If passed in a number, then the option with the given index (0-based)
// within this select will be returned.
//
// If passed in a dijit.form.__SelectOption, the same option will be
// returned if and only if it exists within this select.
//
// If passed an array, then an array will be returned with each element
// in the array being looked up.
//
// If not passed a value, then all options will be returned
//
// returns:
// The option corresponding with the given value or index. null
// is returned if any of the following are true:
// - A string value is passed in which doesn't exist
// - An index is passed in which is outside the bounds of the array of options
// - A dijit.form.__SelectOption is passed in which is not a part of the select
// NOTE: the compare for passing in a dijit.form.__SelectOption checks
// if the value property matches - NOT if the exact option exists
// NOTE: if passing in an array, null elements will be placed in the returned
// array when a value is not found.
var lookupValue = valueOrIdx, opts = this.options || [], l = opts.length;
if(lookupValue === undefined){
return opts; // dijit.form.__SelectOption[]
}
if(lang.isArray(lookupValue)){
return array.map(lookupValue, "return this.getOptions(item);", this); // dijit.form.__SelectOption[]
}
if(lang.isObject(valueOrIdx)){
// We were passed an option - so see if it's in our array (directly),
// and if it's not, try and find it by value.
if(!array.some(this.options, function(o, idx){
if(o === lookupValue ||
(o.value && o.value === lookupValue.value)){
lookupValue = idx;
return true;
}
return false;
})){
lookupValue = -1;
}
}
if(typeof lookupValue == "string"){
for(var i=0; i<l; i++){
if(opts[i].value === lookupValue){
lookupValue = i;
break;
}
}
}
if(typeof lookupValue == "number" && lookupValue >= 0 && lookupValue < l){
return this.options[lookupValue]; // dijit.form.__SelectOption
}
return null; // null
},
addOption: function(/*dijit.form.__SelectOption|dijit.form.__SelectOption[]*/ option){
// summary:
// Adds an option or options to the end of the select. If value
// of the option is empty or missing, a separator is created instead.
// Passing in an array of options will yield slightly better performance
// since the children are only loaded once.
if(!lang.isArray(option)){ option = [option]; }
array.forEach(option, function(i){
if(i && lang.isObject(i)){
this.options.push(i);
}
}, this);
this._loadChildren();
},
removeOption: function(/*String|dijit.form.__SelectOption|Number|Array*/ valueOrIdx){
// summary:
// Removes the given option or options. You can remove by string
// (in which case the value is removed), number (in which case the
// index in the options array is removed), or select option (in
// which case, the select option with a matching value is removed).
// You can also pass in an array of those values for a slightly
// better performance since the children are only loaded once.
if(!lang.isArray(valueOrIdx)){ valueOrIdx = [valueOrIdx]; }
var oldOpts = this.getOptions(valueOrIdx);
array.forEach(oldOpts, function(i){
// We can get null back in our array - if our option was not found. In
// that case, we don't want to blow up...
if(i){
this.options = array.filter(this.options, function(node){
return (node.value !== i.value || node.label !== i.label);
});
this._removeOptionItem(i);
}
}, this);
this._loadChildren();
},
updateOption: function(/*dijit.form.__SelectOption|dijit.form.__SelectOption[]*/ newOption){
// summary:
// Updates the values of the given option. The option to update
// is matched based on the value of the entered option. Passing
// in an array of new options will yield better performance since
// the children will only be loaded once.
if(!lang.isArray(newOption)){ newOption = [newOption]; }
array.forEach(newOption, function(i){
var oldOpt = this.getOptions(i), k;
if(oldOpt){
for(k in i){ oldOpt[k] = i[k]; }
}
}, this);
this._loadChildren();
},
setStore: function(/*dojo.data.api.Identity*/ store,
/*anything?*/ selectedValue,
/*Object?*/ fetchArgs){
// summary:
// Sets the store you would like to use with this select widget.
// The selected value is the value of the new store to set. This
// function returns the original store, in case you want to reuse
// it or something.
// store: dojo.data.api.Identity
// The store you would like to use - it MUST implement dojo.data.api.Identity,
// and MAY implement dojo.data.api.Notification.
// selectedValue: anything?
// The value that this widget should set itself to *after* the store
// has been loaded
// fetchArgs: Object?
// The arguments that will be passed to the store's fetch() function
var oStore = this.store;
fetchArgs = fetchArgs || {};
if(oStore !== store){
// Our store has changed, so update our notifications
var h;
while(h = this._notifyConnections.pop()){ h.remove(); }
if(store && store.getFeatures()["dojo.data.api.Notification"]){
this._notifyConnections = [
aspect.after(store, "onNew", lang.hitch(this, "_onNewItem"), true),
aspect.after(store, "onDelete", lang.hitch(this, "_onDeleteItem"), true),
aspect.after(store, "onSet", lang.hitch(this, "_onSetItem"), true)
];
}
this._set("store", store);
}
// Turn off change notifications while we make all these changes
this._onChangeActive = false;
// Remove existing options (if there are any)
if(this.options && this.options.length){
this.removeOption(this.options);
}
// Add our new options
if(store){
this._loadingStore = true;
store.fetch(lang.delegate(fetchArgs, {
onComplete: function(items, opts){
if(this.sortByLabel && !fetchArgs.sort && items.length){
items.sort(sorter.createSortFunction([{
attribute: store.getLabelAttributes(items[0])[0]
}], store));
}
if(fetchArgs.onFetch){
items = fetchArgs.onFetch.call(this, items, opts);
}
// TODO: Add these guys as a batch, instead of separately
array.forEach(items, function(i){
this._addOptionForItem(i);
}, this);
// Set our value (which might be undefined), and then tweak
// it to send a change event with the real value
this._loadingStore = false;
this.set("value", "_pendingValue" in this ? this._pendingValue : selectedValue);
delete this._pendingValue;
if(!this.loadChildrenOnOpen){
this._loadChildren();
}else{
this._pseudoLoadChildren(items);
}
this._fetchedWith = opts;
this._lastValueReported = this.multiple ? [] : null;
this._onChangeActive = true;
this.onSetStore();
this._handleOnChange(this.value);
},
scope: this
}));
}else{
delete this._fetchedWith;
}
return oStore; // dojo.data.api.Identity
},
// TODO: implement set() and watch() for store and query, although not sure how to handle
// setting them individually rather than together (as in setStore() above)
_setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
// summary:
// set the value of the widget.
// If a string is passed, then we set our value from looking it up.
if(this._loadingStore){
// Our store is loading - so save our value, and we'll set it when
// we're done
this._pendingValue = newValue;
return;
}
var opts = this.getOptions() || [];
if(!lang.isArray(newValue)){
newValue = [newValue];
}
array.forEach(newValue, function(i, idx){
if(!lang.isObject(i)){
i = i + "";
}
if(typeof i === "string"){
newValue[idx] = array.filter(opts, function(node){
return node.value === i;
})[0] || {value: "", label: ""};
}
}, this);
// Make sure some sane default is set
newValue = array.filter(newValue, function(i){ return i && i.value; });
if(!this.multiple && (!newValue[0] || !newValue[0].value) && opts.length){
newValue[0] = opts[0];
}
array.forEach(opts, function(i){
i.selected = array.some(newValue, function(v){ return v.value === i.value; });
});
var val = array.map(newValue, function(i){ return i.value; }),
disp = array.map(newValue, function(i){ return i.label; });
this._set("value", this.multiple ? val : val[0]);
this._setDisplay(this.multiple ? disp : disp[0]);
this._updateSelection();
this._handleOnChange(this.value, priorityChange);
},
_getDisplayedValueAttr: function(){
// summary:
// returns the displayed value of the widget
var val = this.get("value");
if(!lang.isArray(val)){
val = [val];
}
var ret = array.map(this.getOptions(val), function(v){
if(v && "label" in v){
return v.label;
}else if(v){
return v.value;
}
return null;
}, this);
return this.multiple ? ret : ret[0];
},
_loadChildren: function(){
// summary:
// Loads the children represented by this widget's options.
// reset the menu to make it populatable on the next click
if(this._loadingStore){ return; }
array.forEach(this._getChildren(), function(child){
child.destroyRecursive();
});
// Add each menu item
array.forEach(this.options, this._addOptionItem, this);
// Update states
this._updateSelection();
},
_updateSelection: function(){
// summary:
// Sets the "selected" class on the item for styling purposes
this._set("value", this._getValueFromOpts());
var val = this.value;
if(!lang.isArray(val)){
val = [val];
}
if(val && val[0]){
array.forEach(this._getChildren(), function(child){
var isSelected = array.some(val, function(v){
return child.option && (v === child.option.value);
});
domClass.toggle(child.domNode, this.baseClass + "SelectedOption", isSelected);
child.domNode.setAttribute("aria-selected", isSelected);
}, this);
}
},
_getValueFromOpts: function(){
// summary:
// Returns the value of the widget by reading the options for
// the selected flag
var opts = this.getOptions() || [];
if(!this.multiple && opts.length){
// Mirror what a select does - choose the first one
var opt = array.filter(opts, function(i){
return i.selected;
})[0];
if(opt && opt.value){
return opt.value
}else{
opts[0].selected = true;
return opts[0].value;
}
}else if(this.multiple){
// Set value to be the sum of all selected
return array.map(array.filter(opts, function(i){
return i.selected;
}), function(i){
return i.value;
}) || [];
}
return "";
},
// Internal functions to call when we have store notifications come in
_onNewItem: function(/*item*/ item, /*Object?*/ parentInfo){
if(!parentInfo || !parentInfo.parent){
// Only add it if we are top-level
this._addOptionForItem(item);
}
},
_onDeleteItem: function(/*item*/ item){
var store = this.store;
this.removeOption(store.getIdentity(item));
},
_onSetItem: function(/*item*/ item){
this.updateOption(this._getOptionObjForItem(item));
},
_getOptionObjForItem: function(item){
// summary:
// Returns an option object based off the given item. The "value"
// of the option item will be the identity of the item, the "label"
// of the option will be the label of the item. If the item contains
// children, the children value of the item will be set
var store = this.store, label = store.getLabel(item),
value = (label ? store.getIdentity(item) : null);
return {value: value, label: label, item:item}; // dijit.form.__SelectOption
},
_addOptionForItem: function(/*item*/ item){
// summary:
// Creates (and adds) the option for the given item
var store = this.store;
if(!store.isItemLoaded(item)){
// We are not loaded - so let's load it and add later
store.loadItem({item: item, onItem: function(i){
this._addOptionForItem(i);
},
scope: this});
return;
}
var newOpt = this._getOptionObjForItem(item);
this.addOption(newOpt);
},
constructor: function(/*Object*/ keywordArgs){
// summary:
// Saves off our value, if we have an initial one set so we
// can use it if we have a store as well (see startup())
this._oValue = (keywordArgs || {}).value || null;
this._notifyConnections = [];
},
buildRendering: function(){
this.inherited(arguments);
dom.setSelectable(this.focusNode, false);
},
_fillContent: function(){
// summary:
// Loads our options and sets up our dropdown correctly. We
// don't want any content, so we don't call any inherit chain
// function.
var opts = this.options;
if(!opts){
opts = this.options = this.srcNodeRef ? query("> *",
this.srcNodeRef).map(function(node){
if(node.getAttribute("type") === "separator"){
return { value: "", label: "", selected: false, disabled: false };
}
return {
value: (node.getAttribute("data-" + kernel._scopeName + "-value") || node.getAttribute("value")),
label: String(node.innerHTML),
// FIXME: disabled and selected are not valid on complex markup children (which is why we're
// looking for data-dojo-value above. perhaps we should data-dojo-props="" this whole thing?)
// decide before 1.6
selected: node.getAttribute("selected") || false,
disabled: node.getAttribute("disabled") || false
};
}, this) : [];
}
if(!this.value){
this._set("value", this._getValueFromOpts());
}else if(this.multiple && typeof this.value == "string"){
this._set("value", this.value.split(","));
}
},
postCreate: function(){
// summary:
// sets up our event handling that we need for functioning
// as a select
this.inherited(arguments);
// Make our event connections for updating state
this.connect(this, "onChange", "_updateSelection");
this.connect(this, "startup", "_loadChildren");
this._setValueAttr(this.value, null);
},
startup: function(){
// summary:
// Connects in our store, if we have one defined
this.inherited(arguments);
var store = this.store, fetchArgs = {};
array.forEach(["query", "queryOptions", "onFetch"], function(i){
if(this[i]){
fetchArgs[i] = this[i];
}
delete this[i];
}, this);
if(store && store.getFeatures()["dojo.data.api.Identity"]){
// Temporarily set our store to null so that it will get set
// and connected appropriately
this.store = null;
this.setStore(store, this._oValue, fetchArgs);
}
},
destroy: function(){
// summary:
// Clean up our connections
var h;
while(h = this._notifyConnections.pop()){ h.remove(); }
this.inherited(arguments);
},
_addOptionItem: function(/*dijit.form.__SelectOption*/ /*===== option =====*/){
// summary:
// User-overridable function which, for the given option, adds an
// item to the select. If the option doesn't have a value, then a
// separator is added in that place. Make sure to store the option
// in the created option widget.
},
_removeOptionItem: function(/*dijit.form.__SelectOption*/ /*===== option =====*/){
// summary:
// User-overridable function which, for the given option, removes
// its item from the select.
},
_setDisplay: function(/*String or String[]*/ /*===== newDisplay =====*/){
// summary:
// Overridable function which will set the display for the
// widget. newDisplay is either a string (in the case of
// single selects) or array of strings (in the case of multi-selects)
},
_getChildren: function(){
// summary:
// Overridable function to return the children that this widget contains.
return [];
},
_getSelectedOptionsAttr: function(){
// summary:
// hooks into this.attr to provide a mechanism for getting the
// option items for the current value of the widget.
return this.getOptions(this.get("value"));
},
_pseudoLoadChildren: function(/*item[]*/ /*===== items =====*/){
// summary:
// a function that will "fake" loading children, if needed, and
// if we have set to not load children until the widget opens.
// items:
// An array of items that will be loaded, when needed
},
onSetStore: function(){
// summary:
// a function that can be connected to in order to receive a
// notification that the store has finished loading and all options
// from that store are available
}
});
});
},
'dijit/form/Select':function(){
define([
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/dom-class", // domClass.add domClass.remove domClass.toggle
"dojo/dom-construct", // domConstruct.create
"dojo/dom-geometry", // domGeometry.setMarginBox
"dojo/_base/event", // event.stop
"dojo/i18n", // i18n.getLocalization
"dojo/_base/lang", // lang.hitch
"./_FormSelectWidget",
"../_HasDropDown",
"../Menu",
"../MenuItem",
"../MenuSeparator",
"../Tooltip",
"dojo/text!./templates/Select.html",
"dojo/i18n!./nls/validate"
], function(array, declare, domAttr, domClass, domConstruct, domGeometry, event, i18n, lang,
_FormSelectWidget, _HasDropDown, Menu, MenuItem, MenuSeparator, Tooltip, template){
/*=====
var _FormSelectWidget = dijit.form._FormSelectWidget;
var _HasDropDown = dijit._HasDropDown;
var _FormSelectWidget = dijit._FormSelectWidget;
var Menu = dijit.Menu;
var MenuItem = dijit.MenuItem;
var MenuSeparator = dijit.MenuSeparator;
var Tooltip = dijit.Tooltip;
=====*/
// module:
// dijit/form/Select
// summary:
// This is a "styleable" select box - it is basically a DropDownButton which
// can take a <select> as its input.
var _SelectMenu = declare("dijit.form._SelectMenu", Menu, {
// summary:
// An internally-used menu for dropdown that allows us a vertical scrollbar
buildRendering: function(){
// summary:
// Stub in our own changes, so that our domNode is not a table
// otherwise, we won't respond correctly to heights/overflows
this.inherited(arguments);
var o = (this.menuTableNode = this.domNode);
var n = (this.domNode = domConstruct.create("div", {style: {overflowX: "hidden", overflowY: "scroll"}}));
if(o.parentNode){
o.parentNode.replaceChild(n, o);
}
domClass.remove(o, "dijitMenuTable");
n.className = o.className + " dijitSelectMenu";
o.className = "dijitReset dijitMenuTable";
o.setAttribute("role", "listbox");
n.setAttribute("role", "presentation");
n.appendChild(o);
},
postCreate: function(){
// summary:
// stop mousemove from selecting text on IE to be consistent with other browsers
this.inherited(arguments);
this.connect(this.domNode, "onmousemove", event.stop);
},
resize: function(/*Object*/ mb){
// summary:
// Overridden so that we are able to handle resizing our
// internal widget. Note that this is not a "full" resize
// implementation - it only works correctly if you pass it a
// marginBox.
//
// mb: Object
// The margin box to set this dropdown to.
if(mb){
domGeometry.setMarginBox(this.domNode, mb);
if("w" in mb){
// We've explicitly set the wrapper <div>'s width, so set <table> width to match.
// 100% is safer than a pixel value because there may be a scroll bar with
// browser/OS specific width.
this.menuTableNode.style.width = "100%";
}
}
}
});
var Select = declare("dijit.form.Select", [_FormSelectWidget, _HasDropDown], {
// summary:
// This is a "styleable" select box - it is basically a DropDownButton which
// can take a <select> as its input.
baseClass: "dijitSelect",
templateString: template,
// required: Boolean
// Can be true or false, default is false.
required: false,
// state: [readonly] String
// "Incomplete" if this select is required but unset (i.e. blank value), "" otherwise
state: "",
// message: String
// Currently displayed error/prompt message
message: "",
// tooltipPosition: String[]
// See description of dijit.Tooltip.defaultPosition for details on this parameter.
tooltipPosition: [],
// emptyLabel: string
// What to display in an "empty" dropdown
emptyLabel: "&#160;", // &nbsp;
// _isLoaded: Boolean
// Whether or not we have been loaded
_isLoaded: false,
// _childrenLoaded: Boolean
// Whether or not our children have been loaded
_childrenLoaded: false,
_fillContent: function(){
// summary:
// Set the value to be the first, or the selected index
this.inherited(arguments);
// set value from selected option
if(this.options.length && !this.value && this.srcNodeRef){
var si = this.srcNodeRef.selectedIndex || 0; // || 0 needed for when srcNodeRef is not a SELECT
this.value = this.options[si >= 0 ? si : 0].value;
}
// Create the dropDown widget
this.dropDown = new _SelectMenu({id: this.id + "_menu"});
domClass.add(this.dropDown.domNode, this.baseClass + "Menu");
},
_getMenuItemForOption: function(/*dijit.form.__SelectOption*/ option){
// summary:
// For the given option, return the menu item that should be
// used to display it. This can be overridden as needed
if(!option.value && !option.label){
// We are a separator (no label set for it)
return new MenuSeparator();
}else{
// Just a regular menu option
var click = lang.hitch(this, "_setValueAttr", option);
var item = new MenuItem({
option: option,
label: option.label || this.emptyLabel,
onClick: click,
disabled: option.disabled || false
});
item.focusNode.setAttribute("role", "listitem");
return item;
}
},
_addOptionItem: function(/*dijit.form.__SelectOption*/ option){
// summary:
// For the given option, add an option to our dropdown.
// If the option doesn't have a value, then a separator is added
// in that place.
if(this.dropDown){
this.dropDown.addChild(this._getMenuItemForOption(option));
}
},
_getChildren: function(){
if(!this.dropDown){
return [];
}
return this.dropDown.getChildren();
},
_loadChildren: function(/*Boolean*/ loadMenuItems){
// summary:
// Resets the menu and the length attribute of the button - and
// ensures that the label is appropriately set.
// loadMenuItems: Boolean
// actually loads the child menu items - we only do this when we are
// populating for showing the dropdown.
if(loadMenuItems === true){
// this.inherited destroys this.dropDown's child widgets (MenuItems).
// Avoid this.dropDown (Menu widget) having a pointer to a destroyed widget (which will cause
// issues later in _setSelected). (see #10296)
if(this.dropDown){
delete this.dropDown.focusedChild;
}
if(this.options.length){
this.inherited(arguments);
}else{
// Drop down menu is blank but add one blank entry just so something appears on the screen
// to let users know that they are no choices (mimicing native select behavior)
array.forEach(this._getChildren(), function(child){ child.destroyRecursive(); });
var item = new MenuItem({label: "&#160;"});
this.dropDown.addChild(item);
}
}else{
this._updateSelection();
}
this._isLoaded = false;
this._childrenLoaded = true;
if(!this._loadingStore){
// Don't call this if we are loading - since we will handle it later
this._setValueAttr(this.value);
}
},
_setValueAttr: function(value){
this.inherited(arguments);
domAttr.set(this.valueNode, "value", this.get("value"));
this.validate(this.focused); // to update this.state
},
_setDisabledAttr: function(/*Boolean*/ value){
this.inherited(arguments);
this.validate(this.focused); // to update this.state
},
_setRequiredAttr: function(/*Boolean*/ value){
this._set("required", value);
this.focusNode.setAttribute("aria-required", value);
this.validate(this.focused); // to update this.state
},
_setDisplay: function(/*String*/ newDisplay){
// summary:
// sets the display for the given value (or values)
var lbl = newDisplay || this.emptyLabel;
this.containerNode.innerHTML = '<span class="dijitReset dijitInline ' + this.baseClass + 'Label">' + lbl + '</span>';
this.focusNode.setAttribute("aria-valuetext", lbl);
},
validate: function(/*Boolean*/ isFocused){
// summary:
// Called by oninit, onblur, and onkeypress, and whenever required/disabled state changes
// description:
// Show missing or invalid messages if appropriate, and highlight textbox field.
// Used when a select is initially set to no value and the user is required to
// set the value.
var isValid = this.disabled || this.isValid(isFocused);
this._set("state", isValid ? "" : "Incomplete");
this.focusNode.setAttribute("aria-invalid", isValid ? "false" : "true");
var message = isValid ? "" : this._missingMsg;
if(message && this.focused && this._hasBeenBlurred){
Tooltip.show(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
}else{
Tooltip.hide(this.domNode);
}
this._set("message", message);
return isValid;
},
isValid: function(/*Boolean*/ /*===== isFocused =====*/){
// summary:
// Whether or not this is a valid value. The only way a Select
// can be invalid is when it's required but nothing is selected.
return (!this.required || this.value === 0 || !(/^\s*$/.test(this.value || ""))); // handle value is null or undefined
},
reset: function(){
// summary:
// Overridden so that the state will be cleared.
this.inherited(arguments);
Tooltip.hide(this.domNode);
this.validate(this.focused); // to update this.state
},
postMixInProperties: function(){
// summary:
// set the missing message
this.inherited(arguments);
this._missingMsg = i18n.getLocalization("dijit.form", "validate",
this.lang).missingMessage;
},
postCreate: function(){
// summary:
// stop mousemove from selecting text on IE to be consistent with other browsers
this.inherited(arguments);
this.connect(this.domNode, "onmousemove", event.stop);
},
_setStyleAttr: function(/*String||Object*/ value){
this.inherited(arguments);
domClass.toggle(this.domNode, this.baseClass + "FixedWidth", !!this.domNode.style.width);
},
isLoaded: function(){
return this._isLoaded;
},
loadDropDown: function(/*Function*/ loadCallback){
// summary:
// populates the menu
this._loadChildren(true);
this._isLoaded = true;
loadCallback();
},
closeDropDown: function(){
// overriding _HasDropDown.closeDropDown()
this.inherited(arguments);
if(this.dropDown && this.dropDown.menuTableNode){
// Erase possible width: 100% setting from _SelectMenu.resize().
// Leaving it would interfere with the next openDropDown() call, which
// queries the natural size of the drop down.
this.dropDown.menuTableNode.style.width = "";
}
},
uninitialize: function(preserveDom){
if(this.dropDown && !this.dropDown._destroyed){
this.dropDown.destroyRecursive(preserveDom);
delete this.dropDown;
}
this.inherited(arguments);
},
_onFocus: function(){
this.validate(true); // show tooltip if second focus of required tooltip, but no selection
this.inherited(arguments);
},
_onBlur: function(){
Tooltip.hide(this.domNode);
this.inherited(arguments);
}
});
Select._Menu = _SelectMenu; // for monkey patching
return Select;
});
},
'dijit/_editor/range':function(){
define("dijit/_editor/range", [
"dojo/_base/array", // array.every
"dojo/_base/declare", // declare
"dojo/_base/lang", // lang.isArray
"dojo/_base/window", // win.global
".." // for exporting symbols to dijit, TODO: remove in 2.0
], function(array, declare, lang, win, dijit){
// module:
// dijit/_editor/range
// summary:
// W3C range API
dijit.range={};
dijit.range.getIndex = function(/*DomNode*/node, /*DomNode*/parent){
// dojo.profile.start("dijit.range.getIndex");
var ret = [], retR = [];
var onode = node;
var pnode, n;
while(node != parent){
var i = 0;
pnode = node.parentNode;
while((n = pnode.childNodes[i++])){
if(n === node){
--i;
break;
}
}
//if(i>=pnode.childNodes.length){
//dojo.debug("Error finding index of a node in dijit.range.getIndex");
//}
ret.unshift(i);
retR.unshift(i - pnode.childNodes.length);
node = pnode;
}
//normalized() can not be called so often to prevent
//invalidating selection/range, so we have to detect
//here that any text nodes in a row
if(ret.length > 0 && onode.nodeType == 3){
n = onode.previousSibling;
while(n && n.nodeType == 3){
ret[ret.length - 1]--;
n = n.previousSibling;
}
n = onode.nextSibling;
while(n && n.nodeType == 3){
retR[retR.length - 1]++;
n = n.nextSibling;
}
}
// dojo.profile.end("dijit.range.getIndex");
return {o: ret, r:retR};
};
dijit.range.getNode = function(/*Array*/index, /*DomNode*/parent){
if(!lang.isArray(index) || index.length == 0){
return parent;
}
var node = parent;
// if(!node)debugger
array.every(index, function(i){
if(i >= 0 && i < node.childNodes.length){
node = node.childNodes[i];
}else{
node = null;
//console.debug('Error: can not find node with index',index,'under parent node',parent );
return false; //terminate array.every
}
return true; //carry on the every loop
});
return node;
};
dijit.range.getCommonAncestor = function(n1, n2, root){
root = root || n1.ownerDocument.body;
var getAncestors = function(n){
var as = [];
while(n){
as.unshift(n);
if(n !== root){
n = n.parentNode;
}else{
break;
}
}
return as;
};
var n1as = getAncestors(n1);
var n2as = getAncestors(n2);
var m = Math.min(n1as.length, n2as.length);
var com = n1as[0]; //at least, one element should be in the array: the root (BODY by default)
for(var i = 1; i < m; i++){
if(n1as[i] === n2as[i]){
com = n1as[i]
}else{
break;
}
}
return com;
};
dijit.range.getAncestor = function(/*DomNode*/node, /*RegEx?*/regex, /*DomNode?*/root){
root = root || node.ownerDocument.body;
while(node && node !== root){
var name = node.nodeName.toUpperCase();
if(regex.test(name)){
return node;
}
node = node.parentNode;
}
return null;
};
dijit.range.BlockTagNames = /^(?:P|DIV|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI|DT|DE)$/;
dijit.range.getBlockAncestor = function(/*DomNode*/node, /*RegEx?*/regex, /*DomNode?*/root){
root = root || node.ownerDocument.body;
regex = regex || dijit.range.BlockTagNames;
var block = null, blockContainer;
while(node && node !== root){
var name = node.nodeName.toUpperCase();
if(!block && regex.test(name)){
block = node;
}
if(!blockContainer && (/^(?:BODY|TD|TH|CAPTION)$/).test(name)){
blockContainer = node;
}
node = node.parentNode;
}
return {blockNode:block, blockContainer:blockContainer || node.ownerDocument.body};
};
dijit.range.atBeginningOfContainer = function(/*DomNode*/container, /*DomNode*/node, /*Int*/offset){
var atBeginning = false;
var offsetAtBeginning = (offset == 0);
if(!offsetAtBeginning && node.nodeType == 3){ //if this is a text node, check whether the left part is all space
if(/^[\s\xA0]+$/.test(node.nodeValue.substr(0, offset))){
offsetAtBeginning = true;
}
}
if(offsetAtBeginning){
var cnode = node;
atBeginning = true;
while(cnode && cnode !== container){
if(cnode.previousSibling){
atBeginning = false;
break;
}
cnode = cnode.parentNode;
}
}
return atBeginning;
};
dijit.range.atEndOfContainer = function(/*DomNode*/container, /*DomNode*/node, /*Int*/offset){
var atEnd = false;
var offsetAtEnd = (offset == (node.length || node.childNodes.length));
if(!offsetAtEnd && node.nodeType == 3){ //if this is a text node, check whether the right part is all space
if(/^[\s\xA0]+$/.test(node.nodeValue.substr(offset))){
offsetAtEnd = true;
}
}
if(offsetAtEnd){
var cnode = node;
atEnd = true;
while(cnode && cnode !== container){
if(cnode.nextSibling){
atEnd = false;
break;
}
cnode = cnode.parentNode;
}
}
return atEnd;
};
dijit.range.adjacentNoneTextNode = function(startnode, next){
var node = startnode;
var len = (0 - startnode.length) || 0;
var prop = next ? 'nextSibling' : 'previousSibling';
while(node){
if(node.nodeType != 3){
break;
}
len += node.length;
node = node[prop];
}
return [node,len];
};
dijit.range._w3c = Boolean(window['getSelection']);
dijit.range.create = function(/*Window?*/window){
if(dijit.range._w3c){
return (window || win.global).document.createRange();
}else{//IE
return new dijit.range.W3CRange;
}
};
dijit.range.getSelection = function(/*Window*/win, /*Boolean?*/ignoreUpdate){
if(dijit.range._w3c){
return win.getSelection();
}else{//IE
var s = new dijit.range.ie.selection(win);
if(!ignoreUpdate){
s._getCurrentSelection();
}
return s;
}
};
if(!dijit.range._w3c){
dijit.range.ie = {
cachedSelection: {},
selection: function(win){
this._ranges = [];
this.addRange = function(r, /*boolean*/internal){
this._ranges.push(r);
if(!internal){
r._select();
}
this.rangeCount = this._ranges.length;
};
this.removeAllRanges = function(){
//don't detach, the range may be used later
// for(var i=0;i<this._ranges.length;i++){
// this._ranges[i].detach();
// }
this._ranges = [];
this.rangeCount = 0;
};
var _initCurrentRange = function(){
var r = win.document.selection.createRange();
var type = win.document.selection.type.toUpperCase();
if(type == "CONTROL"){
//TODO: multiple range selection(?)
return new dijit.range.W3CRange(dijit.range.ie.decomposeControlRange(r));
}else{
return new dijit.range.W3CRange(dijit.range.ie.decomposeTextRange(r));
}
};
this.getRangeAt = function(i){
return this._ranges[i];
};
this._getCurrentSelection = function(){
this.removeAllRanges();
var r = _initCurrentRange();
if(r){
this.addRange(r, true);
this.isCollapsed = r.collapsed;
}else{
this.isCollapsed = true;
}
};
},
decomposeControlRange: function(range){
var firstnode = range.item(0), lastnode = range.item(range.length - 1);
var startContainer = firstnode.parentNode, endContainer = lastnode.parentNode;
var startOffset = dijit.range.getIndex(firstnode, startContainer).o[0];
var endOffset = dijit.range.getIndex(lastnode, endContainer).o[0] + 1;
return [startContainer, startOffset,endContainer, endOffset];
},
getEndPoint: function(range, end){
var atmrange = range.duplicate();
atmrange.collapse(!end);
var cmpstr = 'EndTo' + (end ? 'End' : 'Start');
var parentNode = atmrange.parentElement();
var startnode, startOffset, lastNode;
if(parentNode.childNodes.length > 0){
array.every(parentNode.childNodes, function(node, i){
var calOffset;
if(node.nodeType != 3){
atmrange.moveToElementText(node);
if(atmrange.compareEndPoints(cmpstr, range) > 0){
//startnode = node.previousSibling;
if(lastNode && lastNode.nodeType == 3){
//where shall we put the start? in the text node or after?
startnode = lastNode;
calOffset = true;
}else{
startnode = parentNode;
startOffset = i;
return false;
}
}else{
if(i == parentNode.childNodes.length - 1){
startnode = parentNode;
startOffset = parentNode.childNodes.length;
return false;
}
}
}else{
if(i == parentNode.childNodes.length - 1){//at the end of this node
startnode = node;
calOffset = true;
}
}
// try{
if(calOffset && startnode){
var prevnode = dijit.range.adjacentNoneTextNode(startnode)[0];
if(prevnode){
startnode = prevnode.nextSibling;
}else{
startnode = parentNode.firstChild; //firstChild must be a text node
}
var prevnodeobj = dijit.range.adjacentNoneTextNode(startnode);
prevnode = prevnodeobj[0];
var lenoffset = prevnodeobj[1];
if(prevnode){
atmrange.moveToElementText(prevnode);
atmrange.collapse(false);
}else{
atmrange.moveToElementText(parentNode);
}
atmrange.setEndPoint(cmpstr, range);
startOffset = atmrange.text.length - lenoffset;
return false;
}
// }catch(e){ debugger }
lastNode = node;
return true;
});
}else{
startnode = parentNode;
startOffset = 0;
}
//if at the end of startnode and we are dealing with start container, then
//move the startnode to nextSibling if it is a text node
//TODO: do this for end container?
if(!end && startnode.nodeType == 1 && startOffset == startnode.childNodes.length){
var nextnode = startnode.nextSibling;
if(nextnode && nextnode.nodeType == 3){
startnode = nextnode;
startOffset = 0;
}
}
return [startnode, startOffset];
},
setEndPoint: function(range, container, offset){
//text node
var atmrange = range.duplicate(), node, len;
if(container.nodeType != 3){ //normal node
if(offset > 0){
node = container.childNodes[offset - 1];
if(node){
if(node.nodeType == 3){
container = node;
offset = node.length;
//pass through
}else{
if(node.nextSibling && node.nextSibling.nodeType == 3){
container = node.nextSibling;
offset = 0;
//pass through
}else{
atmrange.moveToElementText(node.nextSibling ? node : container);
var parent = node.parentNode;
var tempNode = parent.insertBefore(node.ownerDocument.createTextNode(' '), node.nextSibling);
atmrange.collapse(false);
parent.removeChild(tempNode);
}
}
}
}else{
atmrange.moveToElementText(container);
atmrange.collapse(true);
}
}
if(container.nodeType == 3){
var prevnodeobj = dijit.range.adjacentNoneTextNode(container);
var prevnode = prevnodeobj[0];
len = prevnodeobj[1];
if(prevnode){
atmrange.moveToElementText(prevnode);
atmrange.collapse(false);
//if contentEditable is not inherit, the above collapse won't make the end point
//in the correctly position: it always has a -1 offset, so compensate it
if(prevnode.contentEditable != 'inherit'){
len++;
}
}else{
atmrange.moveToElementText(container.parentNode);
atmrange.collapse(true);
}
offset += len;
if(offset > 0){
if(atmrange.move('character', offset) != offset){
console.error('Error when moving!');
}
}
}
return atmrange;
},
decomposeTextRange: function(range){
var tmpary = dijit.range.ie.getEndPoint(range);
var startContainer = tmpary[0], startOffset = tmpary[1];
var endContainer = tmpary[0], endOffset = tmpary[1];
if(range.htmlText.length){
if(range.htmlText == range.text){ //in the same text node
endOffset = startOffset + range.text.length;
}else{
tmpary = dijit.range.ie.getEndPoint(range, true);
endContainer = tmpary[0],endOffset = tmpary[1];
// if(startContainer.tagName == "BODY"){
// startContainer = startContainer.firstChild;
// }
}
}
return [startContainer, startOffset, endContainer, endOffset];
},
setRange: function(range, startContainer, startOffset, endContainer, endOffset, collapsed){
var start = dijit.range.ie.setEndPoint(range, startContainer, startOffset);
range.setEndPoint('StartToStart', start);
if(!collapsed){
var end = dijit.range.ie.setEndPoint(range, endContainer, endOffset);
}
range.setEndPoint('EndToEnd', end || start);
return range;
}
};
declare("dijit.range.W3CRange",null, {
constructor: function(){
if(arguments.length>0){
this.setStart(arguments[0][0],arguments[0][1]);
this.setEnd(arguments[0][2],arguments[0][3]);
}else{
this.commonAncestorContainer = null;
this.startContainer = null;
this.startOffset = 0;
this.endContainer = null;
this.endOffset = 0;
this.collapsed = true;
}
},
_updateInternal: function(){
if(this.startContainer !== this.endContainer){
this.commonAncestorContainer = dijit.range.getCommonAncestor(this.startContainer, this.endContainer);
}else{
this.commonAncestorContainer = this.startContainer;
}
this.collapsed = (this.startContainer === this.endContainer) && (this.startOffset == this.endOffset);
},
setStart: function(node, offset){
offset=parseInt(offset);
if(this.startContainer === node && this.startOffset == offset){
return;
}
delete this._cachedBookmark;
this.startContainer = node;
this.startOffset = offset;
if(!this.endContainer){
this.setEnd(node, offset);
}else{
this._updateInternal();
}
},
setEnd: function(node, offset){
offset=parseInt(offset);
if(this.endContainer === node && this.endOffset == offset){
return;
}
delete this._cachedBookmark;
this.endContainer = node;
this.endOffset = offset;
if(!this.startContainer){
this.setStart(node, offset);
}else{
this._updateInternal();
}
},
setStartAfter: function(node, offset){
this._setPoint('setStart', node, offset, 1);
},
setStartBefore: function(node, offset){
this._setPoint('setStart', node, offset, 0);
},
setEndAfter: function(node, offset){
this._setPoint('setEnd', node, offset, 1);
},
setEndBefore: function(node, offset){
this._setPoint('setEnd', node, offset, 0);
},
_setPoint: function(what, node, offset, ext){
var index = dijit.range.getIndex(node, node.parentNode).o;
this[what](node.parentNode, index.pop()+ext);
},
_getIERange: function(){
var r = (this._body || this.endContainer.ownerDocument.body).createTextRange();
dijit.range.ie.setRange(r, this.startContainer, this.startOffset, this.endContainer, this.endOffset, this.collapsed);
return r;
},
getBookmark: function(){
this._getIERange();
return this._cachedBookmark;
},
_select: function(){
var r = this._getIERange();
r.select();
},
deleteContents: function(){
var s = this.startContainer, r = this._getIERange();
if(s.nodeType === 3 && !this.startOffset){
//if the range starts at the beginning of a
//text node, move it to before the textnode
//to make sure the range is still valid
//after deleteContents() finishes
this.setStartBefore(s);
}
r.pasteHTML('');
this.endContainer = this.startContainer;
this.endOffset = this.startOffset;
this.collapsed = true;
},
cloneRange: function(){
var r = new dijit.range.W3CRange([this.startContainer,this.startOffset,
this.endContainer,this.endOffset]);
r._body = this._body;
return r;
},
detach: function(){
this._body = null;
this.commonAncestorContainer = null;
this.startContainer = null;
this.startOffset = 0;
this.endContainer = null;
this.endOffset = 0;
this.collapsed = true;
}
});
} //if(!dijit.range._w3c)
return dijit.range;
});
},
'dojo/store/util/QueryResults':function(){
define(["../../_base/array", "../../_base/lang", "../../_base/Deferred"
], function(array, lang, Deferred) {
// module:
// dojo/store/util/QueryResults
// summary:
// The module defines a query results wrapper
var util = lang.getObject("dojo.store.util", true);
util.QueryResults = function(results){
// summary:
// A function that wraps the results of a store query with additional
// methods.
//
// description:
// QueryResults is a basic wrapper that allows for array-like iteration
// over any kind of returned data from a query. While the simplest store
// will return a plain array of data, other stores may return deferreds or
// promises; this wrapper makes sure that *all* results can be treated
// the same.
//
// Additional methods include `forEach`, `filter` and `map`.
//
// returns: Object
// An array-like object that can be used for iterating over.
//
// example:
// Query a store and iterate over the results.
//
// | store.query({ prime: true }).forEach(function(item){
// | // do something
// | });
if(!results){
return results;
}
// if it is a promise it may be frozen
if(results.then){
results = lang.delegate(results);
}
function addIterativeMethod(method){
if(!results[method]){
results[method] = function(){
var args = arguments;
return Deferred.when(results, function(results){
Array.prototype.unshift.call(args, results);
return util.QueryResults(array[method].apply(array, args));
});
};
}
}
addIterativeMethod("forEach");
addIterativeMethod("filter");
addIterativeMethod("map");
if(!results.total){
results.total = Deferred.when(results, function(results){
return results.length;
});
}
return results;
};
return util.QueryResults;
});
},
'dijit/form/_ListBase':function(){
define([
"dojo/_base/declare", // declare
"dojo/window" // winUtils.scrollIntoView
], function(declare, winUtils){
// module:
// dijit/form/_ListBase
// summary:
// Focus-less menu to handle UI events consistently
return declare( "dijit.form._ListBase", null, {
// summary:
// Focus-less menu to handle UI events consistently
// Abstract methods that must be defined externally:
// onSelect: item is active (mousedown but not yet mouseup, or keyboard arrow selected but no Enter)
// onDeselect: cancels onSelect
// tags:
// private
// selected: DOMnode
// currently selected node
selected: null,
_getTarget: function(/*Event*/ evt){
var tgt = evt.target;
var container = this.containerNode;
if(tgt == container || tgt == this.domNode){ return null; }
while(tgt && tgt.parentNode != container){
// recurse to the top
tgt = tgt.parentNode;
}
return tgt;
},
selectFirstNode: function(){
// summary:
// Select the first displayed item in the list.
var first = this.containerNode.firstChild;
while(first && first.style.display == "none"){
first = first.nextSibling;
}
this._setSelectedAttr(first);
},
selectLastNode: function(){
// summary:
// Select the last displayed item in the list
var last = this.containerNode.lastChild;
while(last && last.style.display == "none"){
last = last.previousSibling;
}
this._setSelectedAttr(last);
},
selectNextNode: function(){
// summary:
// Select the item just below the current selection.
// If nothing selected, select first node.
var selectedNode = this._getSelectedAttr();
if(!selectedNode){
this.selectFirstNode();
}else{
var next = selectedNode.nextSibling;
while(next && next.style.display == "none"){
next = next.nextSibling;
}
if(!next){
this.selectFirstNode();
}else{
this._setSelectedAttr(next);
}
}
},
selectPreviousNode: function(){
// summary:
// Select the item just above the current selection.
// If nothing selected, select last node (if
// you select Previous and try to keep scrolling up the list).
var selectedNode = this._getSelectedAttr();
if(!selectedNode){
this.selectLastNode();
}else{
var prev = selectedNode.previousSibling;
while(prev && prev.style.display == "none"){
prev = prev.previousSibling;
}
if(!prev){
this.selectLastNode();
}else{
this._setSelectedAttr(prev);
}
}
},
_setSelectedAttr: function(/*DomNode*/ node){
// summary:
// Does the actual select.
if(this.selected != node){
var selectedNode = this._getSelectedAttr();
if(selectedNode){
this.onDeselect(selectedNode);
this.selected = null;
}
if(node && node.parentNode == this.containerNode){
this.selected = node;
winUtils.scrollIntoView(node);
this.onSelect(node);
}
}else if(node){
this.onSelect(node);
}
},
_getSelectedAttr: function(){
// summary:
// Returns the selected node.
var v = this.selected;
return (v && v.parentNode == this.containerNode) ? v : (this.selected = null);
}
});
});
},
'dojo/DeferredList':function(){
define(["./_base/kernel", "./_base/Deferred", "./_base/array"], function(dojo, Deferred, darray) {
// module:
// dojo/DeferredList
// summary:
// TODOC
dojo.DeferredList = function(/*Array*/ list, /*Boolean?*/ fireOnOneCallback, /*Boolean?*/ fireOnOneErrback, /*Boolean?*/ consumeErrors, /*Function?*/ canceller){
// summary:
// Provides event handling for a group of Deferred objects.
// description:
// DeferredList takes an array of existing deferreds and returns a new deferred of its own
// this new deferred will typically have its callback fired when all of the deferreds in
// the given list have fired their own deferreds. The parameters `fireOnOneCallback` and
// fireOnOneErrback, will fire before all the deferreds as appropriate
//
// list:
// The list of deferreds to be synchronizied with this DeferredList
// fireOnOneCallback:
// Will cause the DeferredLists callback to be fired as soon as any
// of the deferreds in its list have been fired instead of waiting until
// the entire list has finished
// fireonOneErrback:
// Will cause the errback to fire upon any of the deferreds errback
// canceller:
// A deferred canceller function, see dojo.Deferred
var resultList = [];
Deferred.call(this);
var self = this;
if(list.length === 0 && !fireOnOneCallback){
this.resolve([0, []]);
}
var finished = 0;
darray.forEach(list, function(item, i){
item.then(function(result){
if(fireOnOneCallback){
self.resolve([i, result]);
}else{
addResult(true, result);
}
},function(error){
if(fireOnOneErrback){
self.reject(error);
}else{
addResult(false, error);
}
if(consumeErrors){
return null;
}
throw error;
});
function addResult(succeeded, result){
resultList[i] = [succeeded, result];
finished++;
if(finished === list.length){
self.resolve(resultList);
}
}
});
};
dojo.DeferredList.prototype = new Deferred();
dojo.DeferredList.prototype.gatherResults = function(deferredList){
// summary:
// Gathers the results of the deferreds for packaging
// as the parameters to the Deferred Lists' callback
// deferredList: dojo.DeferredList
// The deferred list from which this function gathers results.
// returns: dojo.DeferredList
// The newly created deferred list which packs results as
// parameters to its callback.
var d = new dojo.DeferredList(deferredList, false, true, false);
d.addCallback(function(results){
var ret = [];
darray.forEach(results, function(result){
ret.push(result[1]);
});
return ret;
});
return d;
};
return dojo.DeferredList;
});
},
'dojo/dnd/common':function(){
define(["../main"], function(dojo) {
// module:
// dojo/dnd/common
// summary:
// TODOC
dojo.getObject("dnd", true, dojo);
dojo.dnd.getCopyKeyState = dojo.isCopyKey;
dojo.dnd._uniqueId = 0;
dojo.dnd.getUniqueId = function(){
// summary:
// returns a unique string for use with any DOM element
var id;
do{
id = dojo._scopeName + "Unique" + (++dojo.dnd._uniqueId);
}while(dojo.byId(id));
return id;
};
dojo.dnd._empty = {};
dojo.dnd.isFormElement = function(/*Event*/ e){
// summary:
// returns true if user clicked on a form element
var t = e.target;
if(t.nodeType == 3 /*TEXT_NODE*/){
t = t.parentNode;
}
return " button textarea input select option ".indexOf(" " + t.tagName.toLowerCase() + " ") >= 0; // Boolean
};
return dojo.dnd;
});
},
'dijit/CalendarLite':function(){
define([
"dojo/_base/array", // array.forEach array.map
"dojo/_base/declare", // declare
"dojo/cldr/supplemental", // cldrSupplemental.getFirstDayOfWeek
"dojo/date", // date
"dojo/date/locale",
"dojo/dom", // dom.setSelectable
"dojo/dom-class", // domClass.contains
"dojo/_base/event", // event.stop
"dojo/_base/lang", // lang.getObject, lang.hitch
"dojo/_base/sniff", // has("ie") has("webkit")
"dojo/string", // string.substitute
"dojo/_base/window", // win.doc.createTextNode
"./_WidgetBase",
"./_TemplatedMixin",
"dojo/text!./templates/Calendar.html"
], function(array, declare, cldrSupplemental, date, local, dom, domClass, event, lang, has, string, win,
_WidgetBase, _TemplatedMixin, template){
/*=====
var _WidgetBase = dijit._WidgetBase;
var _TemplatedMixin = dijit._TemplatedMixin;
=====*/
// module:
// dijit/CalendarLite
// summary:
// Lightweight version of Calendar widget aimed towards mobile use
var CalendarLite = declare("dijit.CalendarLite", [_WidgetBase, _TemplatedMixin], {
// summary:
// Lightweight version of Calendar widget aimed towards mobile use
//
// description:
// A simple GUI for choosing a date in the context of a monthly calendar.
// This widget can't be used in a form because it doesn't serialize the date to an
// `<input>` field. For a form element, use dijit.form.DateTextBox instead.
//
// Note that the parser takes all dates attributes passed in the
// [RFC 3339 format](http://www.faqs.org/rfcs/rfc3339.html), e.g. `2005-06-30T08:05:00-07:00`
// so that they are serializable and locale-independent.
//
// Also note that this widget isn't keyboard accessible; use dijit.Calendar for that
// example:
// | var calendar = new dijit.CalendarLite({}, dojo.byId("calendarNode"));
//
// example:
// | <div data-dojo-type="dijit.CalendarLite"></div>
// Template for main calendar
templateString: template,
// Template for cell for a day of the week (ex: M)
dowTemplateString: '<th class="dijitReset dijitCalendarDayLabelTemplate" role="columnheader"><span class="dijitCalendarDayLabel">${d}</span></th>',
// Templates for a single date (ex: 13), and for a row for a week (ex: 20 21 22 23 24 25 26)
dateTemplateString: '<td class="dijitReset" role="gridcell" data-dojo-attach-point="dateCells"><span class="dijitCalendarDateLabel" data-dojo-attach-point="dateLabels"></span></td>',
weekTemplateString: '<tr class="dijitReset dijitCalendarWeekTemplate" role="row">${d}${d}${d}${d}${d}${d}${d}</tr>',
// value: Date
// The currently selected Date, initially set to invalid date to indicate no selection.
value: new Date(""),
// TODO: for 2.0 make this a string (ISO format) rather than a Date
// datePackage: String
// JavaScript object containing Calendar functions. Uses Gregorian Calendar routines
// from dojo.date by default.
datePackage: date,
// dayWidth: String
// How to represent the days of the week in the calendar header. See locale
dayWidth: "narrow",
// tabIndex: Integer
// Order fields are traversed when user hits the tab key
tabIndex: "0",
// currentFocus: Date
// Date object containing the currently focused date, or the date which would be focused
// if the calendar itself was focused. Also indicates which year and month to display,
// i.e. the current "page" the calendar is on.
currentFocus: new Date(),
baseClass:"dijitCalendar",
_isValidDate: function(/*Date*/ value){
// summary:
// Runs various tests on the value, checking that it's a valid date, rather
// than blank or NaN.
// tags:
// private
return value && !isNaN(value) && typeof value == "object" &&
value.toString() != this.constructor.prototype.value.toString();
},
_getValueAttr: function(){
// summary:
// Support get('value')
// this.value is set to 1AM, but return midnight, local time for back-compat
if(this.value && !isNaN(this.value)){
var value = new this.dateClassObj(this.value);
value.setHours(0, 0, 0, 0);
// If daylight savings pushes midnight to the previous date, fix the Date
// object to point at 1am so it will represent the correct day. See #9366
if(value.getDate() < this.value.getDate()){
value = this.dateFuncObj.add(value, "hour", 1);
}
return value;
}else{
return null;
}
},
_setValueAttr: function(/*Date|Number*/ value, /*Boolean*/ priorityChange){
// summary:
// Support set("value", ...)
// description:
// Set the current date and update the UI. If the date is disabled, the value will
// not change, but the display will change to the corresponding month.
// value:
// Either a Date or the number of seconds since 1970.
// tags:
// protected
if(value){
// convert from Number to Date, or make copy of Date object so that setHours() call below
// doesn't affect original value
value = new this.dateClassObj(value);
}
if(this._isValidDate(value)){
if(!this._isValidDate(this.value) || this.dateFuncObj.compare(value, this.value)){
value.setHours(1, 0, 0, 0); // round to nearest day (1am to avoid issues when DST shift occurs at midnight, see #8521, #9366)
if(!this.isDisabledDate(value, this.lang)){
this._set("value", value);
// Set focus cell to the new value. Arguably this should only happen when there isn't a current
// focus point. This will also repopulate the grid, showing the new selected value (and possibly
// new month/year).
this.set("currentFocus", value);
if(priorityChange || typeof priorityChange == "undefined"){
this.onChange(this.get('value'));
}
}
}
}else{
// clear value, and repopulate grid (to deselect the previously selected day) without changing currentFocus
this._set("value", null);
this.set("currentFocus", this.currentFocus);
}
},
_setText: function(node, text){
// summary:
// This just sets the content of node to the specified text.
// Can't do "node.innerHTML=text" because of an IE bug w/tables, see #3434.
// tags:
// private
while(node.firstChild){
node.removeChild(node.firstChild);
}
node.appendChild(win.doc.createTextNode(text));
},
_populateGrid: function(){
// summary:
// Fills in the calendar grid with each day (1-31)
// tags:
// private
var month = new this.dateClassObj(this.currentFocus);
month.setDate(1);
var firstDay = month.getDay(),
daysInMonth = this.dateFuncObj.getDaysInMonth(month),
daysInPreviousMonth = this.dateFuncObj.getDaysInMonth(this.dateFuncObj.add(month, "month", -1)),
today = new this.dateClassObj(),
dayOffset = cldrSupplemental.getFirstDayOfWeek(this.lang);
if(dayOffset > firstDay){ dayOffset -= 7; }
// Mapping from date (as specified by number returned from Date.valueOf()) to corresponding <td>
this._date2cell = {};
// Iterate through dates in the calendar and fill in date numbers and style info
array.forEach(this.dateCells, function(template, idx){
var i = idx + dayOffset;
var date = new this.dateClassObj(month),
number, clazz = "dijitCalendar", adj = 0;
if(i < firstDay){
number = daysInPreviousMonth - firstDay + i + 1;
adj = -1;
clazz += "Previous";
}else if(i >= (firstDay + daysInMonth)){
number = i - firstDay - daysInMonth + 1;
adj = 1;
clazz += "Next";
}else{
number = i - firstDay + 1;
clazz += "Current";
}
if(adj){
date = this.dateFuncObj.add(date, "month", adj);
}
date.setDate(number);
if(!this.dateFuncObj.compare(date, today, "date")){
clazz = "dijitCalendarCurrentDate " + clazz;
}
if(this._isSelectedDate(date, this.lang)){
clazz = "dijitCalendarSelectedDate " + clazz;
template.setAttribute("aria-selected", true);
}else{
template.setAttribute("aria-selected", false);
}
if(this.isDisabledDate(date, this.lang)){
clazz = "dijitCalendarDisabledDate " + clazz;
template.setAttribute("aria-disabled", true);
}else{
clazz = "dijitCalendarEnabledDate " + clazz;
template.removeAttribute("aria-disabled");
}
var clazz2 = this.getClassForDate(date, this.lang);
if(clazz2){
clazz = clazz2 + " " + clazz;
}
template.className = clazz + "Month dijitCalendarDateTemplate";
// Each cell has an associated integer value representing it's date
var dateVal = date.valueOf();
this._date2cell[dateVal] = template;
template.dijitDateValue = dateVal;
// Set Date string (ex: "13").
this._setText(this.dateLabels[idx], date.getDateLocalized ? date.getDateLocalized(this.lang) : date.getDate());
}, this);
// set name of this month
this.monthWidget.set("month", month);
// Fill in localized prev/current/next years
var y = month.getFullYear() - 1;
var d = new this.dateClassObj();
array.forEach(["previous", "current", "next"], function(name){
d.setFullYear(y++);
this._setText(this[name+"YearLabelNode"],
this.dateLocaleModule.format(d, {selector:'year', locale:this.lang}));
}, this);
},
goToToday: function(){
// summary:
// Sets calendar's value to today's date
this.set('value', new this.dateClassObj());
},
constructor: function(/*Object*/args){
this.datePackage = args.datePackage || this.datePackage;
this.dateFuncObj = typeof this.datePackage == "string" ?
lang.getObject(this.datePackage, false) :// "string" part for back-compat, remove for 2.0
this.datePackage;
this.dateClassObj = this.dateFuncObj.Date || Date;
this.dateLocaleModule = lang.getObject("locale", false, this.dateFuncObj);
},
_createMonthWidget: function(){
// summary:
// Creates the drop down button that displays the current month and lets user pick a new one
return CalendarLite._MonthWidget({
id: this.id + "_mw",
lang: this.lang,
dateLocaleModule: this.dateLocaleModule
}, this.monthNode);
},
buildRendering: function(){
// Markup for days of the week (referenced from template)
var d = this.dowTemplateString,
dayNames = this.dateLocaleModule.getNames('days', this.dayWidth, 'standAlone', this.lang),
dayOffset = cldrSupplemental.getFirstDayOfWeek(this.lang);
this.dayCellsHtml = string.substitute([d,d,d,d,d,d,d].join(""), {d: ""}, function(){
return dayNames[dayOffset++ % 7]
});
// Markup for dates of the month (referenced from template), but without numbers filled in
var r = string.substitute(this.weekTemplateString, {d: this.dateTemplateString});
this.dateRowsHtml = [r,r,r,r,r,r].join("");
// Instantiate from template.
// dateCells and dateLabels arrays filled when _Templated parses my template.
this.dateCells = [];
this.dateLabels = [];
this.inherited(arguments);
dom.setSelectable(this.domNode, false);
var dateObj = new this.dateClassObj(this.currentFocus);
this._supportingWidgets.push(this.monthWidget = this._createMonthWidget());
this.set('currentFocus', dateObj, false); // draw the grid to the month specified by currentFocus
// Set up connects for increment/decrement of months/years
var connect = lang.hitch(this, function(nodeProp, part, amount){
this.connect(this[nodeProp], "onclick", function(){
this._setCurrentFocusAttr(this.dateFuncObj.add(this.currentFocus, part, amount));
});
});
connect("incrementMonth", "month", 1);
connect("decrementMonth", "month", -1);
connect("nextYearLabelNode", "year", 1);
connect("previousYearLabelNode", "year", -1);
},
_setCurrentFocusAttr: function(/*Date*/ date, /*Boolean*/ forceFocus){
// summary:
// If the calendar currently has focus, then focuses specified date,
// changing the currently displayed month/year if necessary.
// If the calendar doesn't have focus, updates currently
// displayed month/year, and sets the cell that will get focus.
// forceFocus:
// If true, will focus() the cell even if calendar itself doesn't have focus
var oldFocus = this.currentFocus,
oldCell = oldFocus && this._date2cell ? this._date2cell[oldFocus.valueOf()] : null;
// round specified value to nearest day (1am to avoid issues when DST shift occurs at midnight, see #8521, #9366)
date = new this.dateClassObj(date);
date.setHours(1, 0, 0, 0);
this._set("currentFocus", date);
// TODO: only re-populate grid when month/year has changed
this._populateGrid();
// set tabIndex=0 on new cell, and focus it (but only if Calendar itself is focused)
var newCell = this._date2cell[date.valueOf()];
newCell.setAttribute("tabIndex", this.tabIndex);
if(this.focused || forceFocus){
newCell.focus();
}
// set tabIndex=-1 on old focusable cell
if(oldCell && oldCell != newCell){
if(has("webkit")){ // see #11064 about webkit bug
oldCell.setAttribute("tabIndex", "-1");
}else{
oldCell.removeAttribute("tabIndex");
}
}
},
focus: function(){
// summary:
// Focus the calendar by focusing one of the calendar cells
this._setCurrentFocusAttr(this.currentFocus, true);
},
_onDayClick: function(/*Event*/ evt){
// summary:
// Handler for day clicks, selects the date if appropriate
// tags:
// protected
event.stop(evt);
for(var node = evt.target; node && !node.dijitDateValue; node = node.parentNode);
if(node && !domClass.contains(node, "dijitCalendarDisabledDate")){
this.set('value', node.dijitDateValue);
}
},
onChange: function(/*Date*/ /*===== date =====*/){
// summary:
// Called only when the selected date has changed
},
_isSelectedDate: function(dateObject /*===== , locale =====*/){
// summary:
// Extension point so developers can subclass Calendar to
// support multiple (concurrently) selected dates
// dateObject: Date
// locale: String?
// tags:
// protected extension
return this._isValidDate(this.value) && !this.dateFuncObj.compare(dateObject, this.value, "date")
},
isDisabledDate: function(/*===== dateObject, locale =====*/){
// summary:
// May be overridden to disable certain dates in the calendar e.g. `isDisabledDate=dojo.date.locale.isWeekend`
// dateObject: Date
// locale: String?
// tags:
// extension
/*=====
return false; // Boolean
=====*/
},
getClassForDate: function(/*===== dateObject, locale =====*/){
// summary:
// May be overridden to return CSS classes to associate with the date entry for the given dateObject,
// for example to indicate a holiday in specified locale.
// dateObject: Date
// locale: String?
// tags:
// extension
/*=====
return ""; // String
=====*/
}
});
CalendarLite._MonthWidget = declare("dijit.CalendarLite._MonthWidget", _WidgetBase, {
// summary:
// Displays name of current month padded to the width of the month
// w/the longest name, so that changing months doesn't change width.
//
// Create as new dijit.Calendar._MonthWidget({
// lang: ...,
// dateLocaleModule: ...
// })
_setMonthAttr: function(month){
// summary:
// Set the current month to display as a label
var monthNames = this.dateLocaleModule.getNames('months', 'wide', 'standAlone', this.lang, month),
spacer =
(has("ie") == 6 ? "" : "<div class='dijitSpacer'>" +
array.map(monthNames, function(s){ return "<div>" + s + "</div>"; }).join("") + "</div>");
// Set name of current month and also fill in spacer element with all the month names
// (invisible) so that the maximum width will affect layout. But not on IE6 because then
// the center <TH> overlaps the right <TH> (due to a browser bug).
this.domNode.innerHTML =
spacer +
"<div class='dijitCalendarMonthLabel dijitCalendarCurrentMonthLabel'>" +
monthNames[month.getMonth()] + "</div>";
}
});
return CalendarLite;
});
},
'dijit/CheckedMenuItem':function(){
require({cache:{
'url:dijit/templates/CheckedMenuItem.html':"<tr class=\"dijitReset dijitMenuItem\" data-dojo-attach-point=\"focusNode\" role=\"menuitemcheckbox\" tabIndex=\"-1\"\n\t\tdata-dojo-attach-event=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuItemIcon dijitCheckedMenuItemIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t\t<span class=\"dijitCheckedMenuItemIconChar\">&#10003;</span>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" data-dojo-attach-point=\"containerNode,labelNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" data-dojo-attach-point=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">&#160;</td>\n</tr>\n"}});
define("dijit/CheckedMenuItem", [
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.toggle
"./MenuItem",
"dojo/text!./templates/CheckedMenuItem.html",
"./hccss"
], function(declare, domClass, MenuItem, template){
/*=====
var MenuItem = dijit.MenuItem;
=====*/
// module:
// dijit/CheckedMenuItem
// summary:
// A checkbox-like menu item for toggling on and off
return declare("dijit.CheckedMenuItem", MenuItem, {
// summary:
// A checkbox-like menu item for toggling on and off
templateString: template,
// checked: Boolean
// Our checked state
checked: false,
_setCheckedAttr: function(/*Boolean*/ checked){
// summary:
// Hook so attr('checked', bool) works.
// Sets the class and state for the check box.
domClass.toggle(this.domNode, "dijitCheckedMenuItemChecked", checked);
this.domNode.setAttribute("aria-checked", checked);
this._set("checked", checked);
},
iconClass: "", // override dijitNoIcon
onChange: function(/*Boolean*/ /*===== checked =====*/){
// summary:
// User defined function to handle check/uncheck events
// tags:
// callback
},
_onClick: function(/*Event*/ e){
// summary:
// Clicking this item just toggles its state
// tags:
// private
if(!this.disabled){
this.set("checked", !this.checked);
this.onChange(this.checked);
}
this.inherited(arguments);
}
});
});
},
'dijit/form/VerticalRuleLabels':function(){
define([
"dojo/_base/declare", // declare
"./HorizontalRuleLabels"
], function(declare, HorizontalRuleLabels){
/*=====
var HorizontalRuleLabels = dijit.form.HorizontalRuleLabels;
=====*/
// module:
// dijit/form/VerticalRuleLabels
// summary:
// Labels for the `dijit.form.VerticalSlider`
return declare("dijit.form.VerticalRuleLabels", HorizontalRuleLabels, {
// summary:
// Labels for the `dijit.form.VerticalSlider`
templateString: '<div class="dijitRuleContainer dijitRuleContainerV dijitRuleLabelsContainer dijitRuleLabelsContainerV"></div>',
_positionPrefix: '<div class="dijitRuleLabelContainer dijitRuleLabelContainerV" style="top:',
_labelPrefix: '"><span class="dijitRuleLabel dijitRuleLabelV">',
_calcPosition: function(pos){
// Overrides HorizontalRuleLabel._calcPosition()
return 100-pos;
},
// needed to prevent labels from being reversed in RTL mode
_isHorizontal: false
});
});
},
'dijit/Declaration':function(){
define([
"dojo/_base/array", // array.forEach array.map
"dojo/_base/connect", // connect.connect
"dojo/_base/declare", // declare
"dojo/_base/lang", // lang.getObject
"dojo/parser", // parser._functionFromScript
"dojo/query", // query
"./_Widget",
"./_TemplatedMixin",
"./_WidgetsInTemplateMixin",
"dojo/NodeList-dom"
], function(array, connect, declare, lang, parser, query, _Widget, _TemplatedMixin, _WidgetsInTemplateMixin){
/*=====
var _Widget = dijit._Widget;
var _TemplatedMixin = dijit._TemplatedMixin;
var _WidgetsInTemplateMixin = dijit._WidgetsInTemplateMixin;
=====*/
// module:
// dijit/Declaration
// summary:
// The Declaration widget allows a developer to declare new widget
// classes directly from a snippet of markup.
return declare("dijit.Declaration", _Widget, {
// summary:
// The Declaration widget allows a developer to declare new widget
// classes directly from a snippet of markup.
// _noScript: [private] Boolean
// Flag to parser to leave alone the script tags contained inside of me
_noScript: true,
// stopParser: [private] Boolean
// Flag to parser to not try and parse widgets declared inside of me
stopParser: true,
// widgetClass: [const] String
// Name of class being declared, ex: "acme.myWidget"
widgetClass: "",
// propList: [const] Object
// Set of attributes for this widget along with default values, ex:
// {delay: 100, title: "hello world"}
defaults: null,
// mixins: [const] String[]
// List containing the prototype for this widget, and also any mixins,
// ex: ["dijit._Widget", "dijit._Container"]
mixins: [],
buildRendering: function(){
var src = this.srcNodeRef.parentNode.removeChild(this.srcNodeRef),
methods = query("> script[type^='dojo/method']", src).orphan(),
connects = query("> script[type^='dojo/connect']", src).orphan(),
srcType = src.nodeName;
var propList = this.defaults || {};
// For all methods defined like <script type="dojo/method" data-dojo-event="foo">,
// add that method to prototype.
// If there's no "event" specified then it's code to run on instantiation,
// so it becomes a connection to "postscript" (handled below).
array.forEach(methods, function(s){
var evt = s.getAttribute("event") || s.getAttribute("data-dojo-event"),
func = parser._functionFromScript(s);
if(evt){
propList[evt] = func;
}else{
connects.push(s);
}
});
// map array of strings like [ "dijit.form.Button" ] to array of mixin objects
// (note that array.map(this.mixins, lang.getObject) doesn't work because it passes
// a bogus third argument to getObject(), confusing it)
this.mixins = this.mixins.length ?
array.map(this.mixins, function(name){ return lang.getObject(name); } ) :
[ _Widget, _TemplatedMixin, _WidgetsInTemplateMixin ];
propList._skipNodeCache = true;
propList.templateString =
"<"+srcType+" class='"+src.className+"'" +
" data-dojo-attach-point='"+
(src.getAttribute("data-dojo-attach-point") || src.getAttribute("dojoAttachPoint") || '')+
"' data-dojo-attach-event='"+
(src.getAttribute("data-dojo-attach-event") || src.getAttribute("dojoAttachEvent") || '')+
"' >"+src.innerHTML.replace(/\%7B/g,"{").replace(/\%7D/g,"}")+"</"+srcType+">";
// create the new widget class
var wc = declare(
this.widgetClass,
this.mixins,
propList
);
// Handle <script> blocks of form:
// <script type="dojo/connect" data-dojo-event="foo">
// and
// <script type="dojo/method">
// (Note that the second one is just shorthand for a dojo/connect to postscript)
// Since this is a connect in the declaration, we are actually connection to the method
// in the _prototype_.
array.forEach(connects, function(s){
var evt = s.getAttribute("event") || s.getAttribute("data-dojo-event") || "postscript",
func = parser._functionFromScript(s);
connect.connect(wc.prototype, evt, func);
});
}
});
});
},
'dijit/MenuSeparator':function(){
require({cache:{
'url:dijit/templates/MenuSeparator.html':"<tr class=\"dijitMenuSeparator\">\n\t<td class=\"dijitMenuSeparatorIconCell\">\n\t\t<div class=\"dijitMenuSeparatorTop\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n\t<td colspan=\"3\" class=\"dijitMenuSeparatorLabelCell\">\n\t\t<div class=\"dijitMenuSeparatorTop dijitMenuSeparatorLabel\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n</tr>"}});
define("dijit/MenuSeparator", [
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"./_WidgetBase",
"./_TemplatedMixin",
"./_Contained",
"dojo/text!./templates/MenuSeparator.html"
], function(declare, dom, _WidgetBase, _TemplatedMixin, _Contained, template){
/*=====
var _WidgetBase = dijit._WidgetBase;
var _TemplatedMixin = dijit._TemplatedMixin;
var _Contained = dijit._Contained;
=====*/
// module:
// dijit/MenuSeparator
// summary:
// A line between two menu items
return declare("dijit.MenuSeparator", [_WidgetBase, _TemplatedMixin, _Contained], {
// summary:
// A line between two menu items
templateString: template,
buildRendering: function(){
this.inherited(arguments);
dom.setSelectable(this.domNode, false);
},
isFocusable: function(){
// summary:
// Override to always return false
// tags:
// protected
return false; // Boolean
}
});
});
},
'dijit/form/_ComboBoxMenu':function(){
define([
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.add domClass.remove
"dojo/dom-construct", // domConstruct.create
"dojo/dom-style", // domStyle.get
"dojo/keys", // keys.DOWN_ARROW keys.PAGE_DOWN keys.PAGE_UP keys.UP_ARROW
"../_WidgetBase",
"../_TemplatedMixin",
"./_ComboBoxMenuMixin",
"./_ListMouseMixin"
], function(declare, domClass, domConstruct, domStyle, keys,
_WidgetBase, _TemplatedMixin, _ComboBoxMenuMixin, _ListMouseMixin){
/*=====
var _WidgetBase = dijit._WidgetBase;
var _TemplatedMixin = dijit._TemplatedMixin;
var _ComboBoxMenuMixin = dijit.form._ComboBoxMenuMixin;
var _ListMouseMixin = dijit.form._ListMouseMixin;
=====*/
// module:
// dijit/form/_ComboBoxMenu
// summary:
// Focus-less menu for internal use in `dijit.form.ComboBox`
return declare("dijit.form._ComboBoxMenu",[_WidgetBase, _TemplatedMixin, _ListMouseMixin, _ComboBoxMenuMixin], {
// summary:
// Focus-less menu for internal use in `dijit.form.ComboBox`
// Abstract methods that must be defined externally:
// onChange: item was explicitly chosen (mousedown somewhere on the menu and mouseup somewhere on the menu)
// onPage: next(1) or previous(-1) button pressed
// tags:
// private
templateString: "<div class='dijitReset dijitMenu' data-dojo-attach-point='containerNode' style='overflow: auto; overflow-x: hidden;'>"
+"<div class='dijitMenuItem dijitMenuPreviousButton' data-dojo-attach-point='previousButton' role='option'></div>"
+"<div class='dijitMenuItem dijitMenuNextButton' data-dojo-attach-point='nextButton' role='option'></div>"
+"</div>",
baseClass: "dijitComboBoxMenu",
postCreate: function(){
this.inherited(arguments);
if(!this.isLeftToRight()){
domClass.add(this.previousButton, "dijitMenuItemRtl");
domClass.add(this.nextButton, "dijitMenuItemRtl");
}
},
_createMenuItem: function(){
return domConstruct.create("div", {
"class": "dijitReset dijitMenuItem" +(this.isLeftToRight() ? "" : " dijitMenuItemRtl"),
role: "option"
});
},
onHover: function(/*DomNode*/ node){
// summary:
// Add hover CSS
domClass.add(node, "dijitMenuItemHover");
},
onUnhover: function(/*DomNode*/ node){
// summary:
// Remove hover CSS
domClass.remove(node, "dijitMenuItemHover");
},
onSelect: function(/*DomNode*/ node){
// summary:
// Add selected CSS
domClass.add(node, "dijitMenuItemSelected");
},
onDeselect: function(/*DomNode*/ node){
// summary:
// Remove selected CSS
domClass.remove(node, "dijitMenuItemSelected");
},
_page: function(/*Boolean*/ up){
// summary:
// Handles page-up and page-down keypresses
var scrollamount = 0;
var oldscroll = this.domNode.scrollTop;
var height = domStyle.get(this.domNode, "height");
// if no item is highlighted, highlight the first option
if(!this.getHighlightedOption()){
this.selectNextNode();
}
while(scrollamount<height){
var highlighted_option = this.getHighlightedOption();
if(up){
// stop at option 1
if(!highlighted_option.previousSibling ||
highlighted_option.previousSibling.style.display == "none"){
break;
}
this.selectPreviousNode();
}else{
// stop at last option
if(!highlighted_option.nextSibling ||
highlighted_option.nextSibling.style.display == "none"){
break;
}
this.selectNextNode();
}
// going backwards
var newscroll = this.domNode.scrollTop;
scrollamount += (newscroll-oldscroll)*(up ? -1:1);
oldscroll = newscroll;
}
},
handleKey: function(evt){
// summary:
// Handle keystroke event forwarded from ComboBox, returning false if it's
// a keystroke I recognize and process, true otherwise.
switch(evt.charOrCode){
case keys.DOWN_ARROW:
this.selectNextNode();
return false;
case keys.PAGE_DOWN:
this._page(false);
return false;
case keys.UP_ARROW:
this.selectPreviousNode();
return false;
case keys.PAGE_UP:
this._page(true);
return false;
default:
return true;
}
}
});
});
},
'url:dijit/layout/templates/ScrollingTabController.html':"<div class=\"dijitTabListContainer-${tabPosition}\" style=\"visibility:hidden\">\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerMenuButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_menuBtn\"\n\t\t\tdata-dojo-props=\"containerId: '${containerId}', iconClass: 'dijitTabStripMenuIcon',\n\t\t\t\t\tdropDownPosition: ['below-alt', 'above-alt']\"\n\t\t\tdata-dojo-attach-point=\"_menuBtn\" showLabel=\"false\" title=\"\">&#9660;</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_leftBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideLeftIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_leftBtn\" data-dojo-attach-event=\"onClick: doSlideLeft\">&#9664;</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_rightBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideRightIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_rightBtn\" data-dojo-attach-event=\"onClick: doSlideRight\">&#9654;</div>\n\t<div class='dijitTabListWrapper' data-dojo-attach-point='tablistWrapper'>\n\t\t<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'\n\t\t\t\tdata-dojo-attach-point='containerNode' class='nowrapTabStrip'></div>\n\t</div>\n</div>",
'dijit/Dialog':function(){
require({cache:{
'url:dijit/templates/Dialog.html':"<div class=\"dijitDialog\" role=\"dialog\" aria-labelledby=\"${id}_title\">\n\t<div data-dojo-attach-point=\"titleBar\" class=\"dijitDialogTitleBar\">\n\t<span data-dojo-attach-point=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"></span>\n\t<span data-dojo-attach-point=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" data-dojo-attach-event=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabIndex=\"-1\">\n\t\t<span data-dojo-attach-point=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t</span>\n\t</div>\n\t\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n</div>\n"}});
define("dijit/Dialog", [
"require",
"dojo/_base/array", // array.forEach array.indexOf array.map
"dojo/_base/connect", // connect._keypress
"dojo/_base/declare", // declare
"dojo/_base/Deferred", // Deferred
"dojo/dom", // dom.isDescendant
"dojo/dom-class", // domClass.add domClass.contains
"dojo/dom-geometry", // domGeometry.position
"dojo/dom-style", // domStyle.set
"dojo/_base/event", // event.stop
"dojo/_base/fx", // fx.fadeIn fx.fadeOut
"dojo/i18n", // i18n.getLocalization
"dojo/_base/kernel", // kernel.isAsync
"dojo/keys",
"dojo/_base/lang", // lang.mixin lang.hitch
"dojo/on",
"dojo/ready",
"dojo/_base/sniff", // has("ie") has("opera")
"dojo/_base/window", // win.body
"dojo/window", // winUtils.getBox
"dojo/dnd/Moveable", // Moveable
"dojo/dnd/TimedMoveable", // TimedMoveable
"./focus",
"./_base/manager", // manager.defaultDuration
"./_Widget",
"./_TemplatedMixin",
"./_CssStateMixin",
"./form/_FormMixin",
"./_DialogMixin",
"./DialogUnderlay",
"./layout/ContentPane",
"dojo/text!./templates/Dialog.html",
".", // for back-compat, exporting dijit._underlay (remove in 2.0)
"dojo/i18n!./nls/common"
], function(require, array, connect, declare, Deferred,
dom, domClass, domGeometry, domStyle, event, fx, i18n, kernel, keys, lang, on, ready, has, win, winUtils,
Moveable, TimedMoveable, focus, manager, _Widget, _TemplatedMixin, _CssStateMixin, _FormMixin, _DialogMixin,
DialogUnderlay, ContentPane, template, dijit){
/*=====
var _Widget = dijit._Widget;
var _TemplatedMixin = dijit._TemplatedMixin;
var _CssStateMixin = dijit._CssStateMixin;
var _FormMixin = dijit.form._FormMixin;
var _DialogMixin = dijit._DialogMixin;
=====*/
// module:
// dijit/Dialog
// summary:
// A modal dialog Widget
/*=====
dijit._underlay = function(kwArgs){
// summary:
// A shared instance of a `dijit.DialogUnderlay`
//
// description:
// A shared instance of a `dijit.DialogUnderlay` created and
// used by `dijit.Dialog`, though never created until some Dialog
// or subclass thereof is shown.
};
=====*/
var _DialogBase = declare("dijit._DialogBase", [_TemplatedMixin, _FormMixin, _DialogMixin, _CssStateMixin], {
// summary:
// A modal dialog Widget
//
// description:
// Pops up a modal dialog window, blocking access to the screen
// and also graying out the screen Dialog is extended from
// ContentPane so it supports all the same parameters (href, etc.)
//
// example:
// | <div data-dojo-type="dijit.Dialog" data-dojo-props="href: 'test.html'"></div>
//
// example:
// | var foo = new dijit.Dialog({ title: "test dialog", content: "test content" };
// | dojo.body().appendChild(foo.domNode);
// | foo.startup();
templateString: template,
baseClass: "dijitDialog",
cssStateNodes: {
closeButtonNode: "dijitDialogCloseIcon"
},
// Map widget attributes to DOMNode attributes.
_setTitleAttr: [
{ node: "titleNode", type: "innerHTML" },
{ node: "titleBar", type: "attribute" }
],
// open: [readonly] Boolean
// True if Dialog is currently displayed on screen.
open: false,
// duration: Integer
// The time in milliseconds it takes the dialog to fade in and out
duration: manager.defaultDuration,
// refocus: Boolean
// A Toggle to modify the default focus behavior of a Dialog, which
// is to re-focus the element which had focus before being opened.
// False will disable refocusing. Default: true
refocus: true,
// autofocus: Boolean
// A Toggle to modify the default focus behavior of a Dialog, which
// is to focus on the first dialog element after opening the dialog.
// False will disable autofocusing. Default: true
autofocus: true,
// _firstFocusItem: [private readonly] DomNode
// The pointer to the first focusable node in the dialog.
// Set by `dijit._DialogMixin._getFocusItems`.
_firstFocusItem: null,
// _lastFocusItem: [private readonly] DomNode
// The pointer to which node has focus prior to our dialog.
// Set by `dijit._DialogMixin._getFocusItems`.
_lastFocusItem: null,
// doLayout: [protected] Boolean
// Don't change this parameter from the default value.
// This ContentPane parameter doesn't make sense for Dialog, since Dialog
// is never a child of a layout container, nor can you specify the size of
// Dialog in order to control the size of an inner widget.
doLayout: false,
// draggable: Boolean
// Toggles the moveable aspect of the Dialog. If true, Dialog
// can be dragged by it's title. If false it will remain centered
// in the viewport.
draggable: true,
//aria-describedby: String
// Allows the user to add an aria-describedby attribute onto the dialog. The value should
// be the id of the container element of text that describes the dialog purpose (usually
// the first text in the dialog).
// <div data-dojo-type="dijit.Dialog" aria-describedby="intro" .....>
// <div id="intro">Introductory text</div>
// <div>rest of dialog contents</div>
// </div>
"aria-describedby":"",
postMixInProperties: function(){
var _nlsResources = i18n.getLocalization("dijit", "common");
lang.mixin(this, _nlsResources);
this.inherited(arguments);
},
postCreate: function(){
domStyle.set(this.domNode, {
display: "none",
position:"absolute"
});
win.body().appendChild(this.domNode);
this.inherited(arguments);
this.connect(this, "onExecute", "hide");
this.connect(this, "onCancel", "hide");
this._modalconnects = [];
},
onLoad: function(){
// summary:
// Called when data has been loaded from an href.
// Unlike most other callbacks, this function can be connected to (via `dojo.connect`)
// but should *not* be overridden.
// tags:
// callback
// when href is specified we need to reposition the dialog after the data is loaded
// and find the focusable elements
this._position();
if(this.autofocus && DialogLevelManager.isTop(this)){
this._getFocusItems(this.domNode);
focus.focus(this._firstFocusItem);
}
this.inherited(arguments);
},
_endDrag: function(){
// summary:
// Called after dragging the Dialog. Saves the position of the dialog in the viewport,
// and also adjust position to be fully within the viewport, so user doesn't lose access to handle
var nodePosition = domGeometry.position(this.domNode),
viewport = winUtils.getBox();
nodePosition.y = Math.min(Math.max(nodePosition.y, 0), (viewport.h - nodePosition.h));
nodePosition.x = Math.min(Math.max(nodePosition.x, 0), (viewport.w - nodePosition.w));
this._relativePosition = nodePosition;
this._position();
},
_setup: function(){
// summary:
// Stuff we need to do before showing the Dialog for the first
// time (but we defer it until right beforehand, for
// performance reasons).
// tags:
// private
var node = this.domNode;
if(this.titleBar && this.draggable){
this._moveable = new ((has("ie") == 6) ? TimedMoveable // prevent overload, see #5285
: Moveable)(node, { handle: this.titleBar });
this.connect(this._moveable, "onMoveStop", "_endDrag");
}else{
domClass.add(node,"dijitDialogFixed");
}
this.underlayAttrs = {
dialogId: this.id,
"class": array.map(this["class"].split(/\s/), function(s){ return s+"_underlay"; }).join(" ")
};
},
_size: function(){
// summary:
// If necessary, shrink dialog contents so dialog fits in viewport
// tags:
// private
this._checkIfSingleChild();
// If we resized the dialog contents earlier, reset them back to original size, so
// that if the user later increases the viewport size, the dialog can display w/out a scrollbar.
// Need to do this before the domGeometry.position(this.domNode) call below.
if(this._singleChild){
if(this._singleChildOriginalStyle){
this._singleChild.domNode.style.cssText = this._singleChildOriginalStyle;
}
delete this._singleChildOriginalStyle;
}else{
domStyle.set(this.containerNode, {
width:"auto",
height:"auto"
});
}
var bb = domGeometry.position(this.domNode);
var viewport = winUtils.getBox();
if(bb.w >= viewport.w || bb.h >= viewport.h){
// Reduce size of dialog contents so that dialog fits in viewport
var w = Math.min(bb.w, Math.floor(viewport.w * 0.75)),
h = Math.min(bb.h, Math.floor(viewport.h * 0.75));
if(this._singleChild && this._singleChild.resize){
this._singleChildOriginalStyle = this._singleChild.domNode.style.cssText;
this._singleChild.resize({w: w, h: h});
}else{
domStyle.set(this.containerNode, {
width: w + "px",
height: h + "px",
overflow: "auto",
position: "relative" // workaround IE bug moving scrollbar or dragging dialog
});
}
}else{
if(this._singleChild && this._singleChild.resize){
this._singleChild.resize();
}
}
},
_position: function(){
// summary:
// Position modal dialog in the viewport. If no relative offset
// in the viewport has been determined (by dragging, for instance),
// center the node. Otherwise, use the Dialog's stored relative offset,
// and position the node to top: left: values based on the viewport.
if(!domClass.contains(win.body(), "dojoMove")){ // don't do anything if called during auto-scroll
var node = this.domNode,
viewport = winUtils.getBox(),
p = this._relativePosition,
bb = p ? null : domGeometry.position(node),
l = Math.floor(viewport.l + (p ? p.x : (viewport.w - bb.w) / 2)),
t = Math.floor(viewport.t + (p ? p.y : (viewport.h - bb.h) / 2))
;
domStyle.set(node,{
left: l + "px",
top: t + "px"
});
}
},
_onKey: function(/*Event*/ evt){
// summary:
// Handles the keyboard events for accessibility reasons
// tags:
// private
if(evt.charOrCode){
var node = evt.target;
if(evt.charOrCode === keys.TAB){
this._getFocusItems(this.domNode);
}
var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
// see if we are shift-tabbing from first focusable item on dialog
if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === keys.TAB){
if(!singleFocusItem){
focus.focus(this._lastFocusItem); // send focus to last item in dialog
}
event.stop(evt);
}else if(node == this._lastFocusItem && evt.charOrCode === keys.TAB && !evt.shiftKey){
if(!singleFocusItem){
focus.focus(this._firstFocusItem); // send focus to first item in dialog
}
event.stop(evt);
}else{
// see if the key is for the dialog
while(node){
if(node == this.domNode || domClass.contains(node, "dijitPopup")){
if(evt.charOrCode == keys.ESCAPE){
this.onCancel();
}else{
return; // just let it go
}
}
node = node.parentNode;
}
// this key is for the disabled document window
if(evt.charOrCode !== keys.TAB){ // allow tabbing into the dialog for a11y
event.stop(evt);
// opera won't tab to a div
}else if(!has("opera")){
try{
this._firstFocusItem.focus();
}catch(e){ /*squelch*/ }
}
}
}
},
show: function(){
// summary:
// Display the dialog
// returns: dojo.Deferred
// Deferred object that resolves when the display animation is complete
if(this.open){ return; }
if(!this._started){
this.startup();
}
// first time we show the dialog, there's some initialization stuff to do
if(!this._alreadyInitialized){
this._setup();
this._alreadyInitialized=true;
}
if(this._fadeOutDeferred){
this._fadeOutDeferred.cancel();
}
this._modalconnects.push(on(window, "scroll", lang.hitch(this, "layout")));
this._modalconnects.push(on(window, "resize", lang.hitch(this, function(){
// IE gives spurious resize events and can actually get stuck
// in an infinite loop if we don't ignore them
var viewport = winUtils.getBox();
if(!this._oldViewport ||
viewport.h != this._oldViewport.h ||
viewport.w != this._oldViewport.w){
this.layout();
this._oldViewport = viewport;
}
})));
this._modalconnects.push(on(this.domNode, connect._keypress, lang.hitch(this, "_onKey")));
domStyle.set(this.domNode, {
opacity:0,
display:""
});
this._set("open", true);
this._onShow(); // lazy load trigger
this._size();
this._position();
// fade-in Animation object, setup below
var fadeIn;
this._fadeInDeferred = new Deferred(lang.hitch(this, function(){
fadeIn.stop();
delete this._fadeInDeferred;
}));
fadeIn = fx.fadeIn({
node: this.domNode,
duration: this.duration,
beforeBegin: lang.hitch(this, function(){
DialogLevelManager.show(this, this.underlayAttrs);
}),
onEnd: lang.hitch(this, function(){
if(this.autofocus && DialogLevelManager.isTop(this)){
// find focusable items each time dialog is shown since if dialog contains a widget the
// first focusable items can change
this._getFocusItems(this.domNode);
focus.focus(this._firstFocusItem);
}
this._fadeInDeferred.callback(true);
delete this._fadeInDeferred;
})
}).play();
return this._fadeInDeferred;
},
hide: function(){
// summary:
// Hide the dialog
// returns: dojo.Deferred
// Deferred object that resolves when the hide animation is complete
// if we haven't been initialized yet then we aren't showing and we can just return
if(!this._alreadyInitialized){
return;
}
if(this._fadeInDeferred){
this._fadeInDeferred.cancel();
}
// fade-in Animation object, setup below
var fadeOut;
this._fadeOutDeferred = new Deferred(lang.hitch(this, function(){
fadeOut.stop();
delete this._fadeOutDeferred;
}));
// fire onHide when the promise resolves.
this._fadeOutDeferred.then(lang.hitch(this, 'onHide'));
fadeOut = fx.fadeOut({
node: this.domNode,
duration: this.duration,
onEnd: lang.hitch(this, function(){
this.domNode.style.display = "none";
DialogLevelManager.hide(this);
this._fadeOutDeferred.callback(true);
delete this._fadeOutDeferred;
})
}).play();
if(this._scrollConnected){
this._scrollConnected = false;
}
var h;
while(h = this._modalconnects.pop()){
h.remove();
}
if(this._relativePosition){
delete this._relativePosition;
}
this._set("open", false);
return this._fadeOutDeferred;
},
layout: function(){
// summary:
// Position the Dialog and the underlay
// tags:
// private
if(this.domNode.style.display != "none"){
if(dijit._underlay){ // avoid race condition during show()
dijit._underlay.layout();
}
this._position();
}
},
destroy: function(){
if(this._fadeInDeferred){
this._fadeInDeferred.cancel();
}
if(this._fadeOutDeferred){
this._fadeOutDeferred.cancel();
}
if(this._moveable){
this._moveable.destroy();
}
var h;
while(h = this._modalconnects.pop()){
h.remove();
}
DialogLevelManager.hide(this);
this.inherited(arguments);
}
});
var Dialog = declare("dijit.Dialog", [ContentPane, _DialogBase], {});
Dialog._DialogBase = _DialogBase; // for monkey patching
var DialogLevelManager = Dialog._DialogLevelManager = {
// summary:
// Controls the various active "levels" on the page, starting with the
// stuff initially visible on the page (at z-index 0), and then having an entry for
// each Dialog shown.
_beginZIndex: 950,
show: function(/*dijit._Widget*/ dialog, /*Object*/ underlayAttrs){
// summary:
// Call right before fade-in animation for new dialog.
// Saves current focus, displays/adjusts underlay for new dialog,
// and sets the z-index of the dialog itself.
//
// New dialog will be displayed on top of all currently displayed dialogs.
//
// Caller is responsible for setting focus in new dialog after the fade-in
// animation completes.
// Save current focus
ds[ds.length-1].focus = focus.curNode;
// Display the underlay, or if already displayed then adjust for this new dialog
var underlay = dijit._underlay;
if(!underlay || underlay._destroyed){
underlay = dijit._underlay = new DialogUnderlay(underlayAttrs);
}else{
underlay.set(dialog.underlayAttrs);
}
// Set z-index a bit above previous dialog
var zIndex = ds[ds.length-1].dialog ? ds[ds.length-1].zIndex + 2 : Dialog._DialogLevelManager._beginZIndex;
if(ds.length == 1){ // first dialog
underlay.show();
}
domStyle.set(dijit._underlay.domNode, 'zIndex', zIndex - 1);
// Dialog
domStyle.set(dialog.domNode, 'zIndex', zIndex);
ds.push({dialog: dialog, underlayAttrs: underlayAttrs, zIndex: zIndex});
},
hide: function(/*dijit._Widget*/ dialog){
// summary:
// Called when the specified dialog is hidden/destroyed, after the fade-out
// animation ends, in order to reset page focus, fix the underlay, etc.
// If the specified dialog isn't open then does nothing.
//
// Caller is responsible for either setting display:none on the dialog domNode,
// or calling dijit.popup.hide(), or removing it from the page DOM.
if(ds[ds.length-1].dialog == dialog){
// Removing the top (or only) dialog in the stack, return focus
// to previous dialog
ds.pop();
var pd = ds[ds.length-1]; // the new active dialog (or the base page itself)
// Adjust underlay
if(ds.length == 1){
// Returning to original page.
// Hide the underlay, unless the underlay widget has already been destroyed
// because we are being called during page unload (when all widgets are destroyed)
if(!dijit._underlay._destroyed){
dijit._underlay.hide();
}
}else{
// Popping back to previous dialog, adjust underlay
domStyle.set(dijit._underlay.domNode, 'zIndex', pd.zIndex - 1);
dijit._underlay.set(pd.underlayAttrs);
}
// Adjust focus
if(dialog.refocus){
// If we are returning control to a previous dialog but for some reason
// that dialog didn't have a focused field, set focus to first focusable item.
// This situation could happen if two dialogs appeared at nearly the same time,
// since a dialog doesn't set it's focus until the fade-in is finished.
var focus = pd.focus;
if(pd.dialog && (!focus || !dom.isDescendant(focus, pd.dialog.domNode))){
pd.dialog._getFocusItems(pd.dialog.domNode);
focus = pd.dialog._firstFocusItem;
}
if(focus){
focus.focus();
}
}
}else{
// Removing a dialog out of order (#9944, #10705).
// Don't need to mess with underlay or z-index or anything.
var idx = array.indexOf(array.map(ds, function(elem){return elem.dialog}), dialog);
if(idx != -1){
ds.splice(idx, 1);
}
}
},
isTop: function(/*dijit._Widget*/ dialog){
// summary:
// Returns true if specified Dialog is the top in the task
return ds[ds.length-1].dialog == dialog;
}
};
// Stack representing the various active "levels" on the page, starting with the
// stuff initially visible on the page (at z-index 0), and then having an entry for
// each Dialog shown.
// Each element in stack has form {
// dialog: dialogWidget,
// focus: returnFromGetFocus(),
// underlayAttrs: attributes to set on underlay (when this widget is active)
// }
var ds = Dialog._dialogStack = [
{dialog: null, focus: null, underlayAttrs: null} // entry for stuff at z-index: 0
];
// Back compat w/1.6, remove for 2.0
if(!kernel.isAsync){
ready(0, function(){
var requires = ["dijit/TooltipDialog"];
require(requires); // use indirection so modules not rolled into a build
});
}
return Dialog;
});
},
'dijit/form/MultiSelect':function(){
define("dijit/form/MultiSelect", [
"dojo/_base/array", // array.indexOf, array.map
"dojo/_base/declare", // declare
"dojo/dom-geometry", // domGeometry.setMarginBox
"dojo/query", // query
"./_FormValueWidget"
], function(array, declare, domGeometry, query, _FormValueWidget){
/*=====
var _FormValueWidget = dijit.form._FormValueWidget;
=====*/
// module:
// dijit/form/MultiSelect
// summary:
// Widget version of a <select multiple=true> element,
// for selecting multiple options.
return declare("dijit.form.MultiSelect", _FormValueWidget, {
// summary:
// Widget version of a <select multiple=true> element,
// for selecting multiple options.
// size: Number
// Number of elements to display on a page
// NOTE: may be removed in version 2.0, since elements may have variable height;
// set the size via style="..." or CSS class names instead.
size: 7,
templateString: "<select multiple='true' ${!nameAttrSetting} data-dojo-attach-point='containerNode,focusNode' data-dojo-attach-event='onchange: _onChange'></select>",
addSelected: function(/*dijit.form.MultiSelect*/ select){
// summary:
// Move the selected nodes of a passed Select widget
// instance to this Select widget.
//
// example:
// | // move all the selected values from "bar" to "foo"
// | dijit.byId("foo").addSelected(dijit.byId("bar"));
select.getSelected().forEach(function(n){
this.containerNode.appendChild(n);
// scroll to bottom to see item
// cannot use scrollIntoView since <option> tags don't support all attributes
// does not work on IE due to a bug where <select> always shows scrollTop = 0
this.domNode.scrollTop = this.domNode.offsetHeight; // overshoot will be ignored
// scrolling the source select is trickier esp. on safari who forgets to change the scrollbar size
var oldscroll = select.domNode.scrollTop;
select.domNode.scrollTop = 0;
select.domNode.scrollTop = oldscroll;
},this);
this._set('value', this.get('value'));
},
getSelected: function(){
// summary:
// Access the NodeList of the selected options directly
return query("option",this.containerNode).filter(function(n){
return n.selected; // Boolean
}); // dojo.NodeList
},
_getValueAttr: function(){
// summary:
// Hook so get('value') works.
// description:
// Returns an array of the selected options' values.
// Don't call getSelect.map() because it doesn't return a real array,
// and that messes up dojo.toJson() calls like in the Form.html test
return array.map(this.getSelected(), function(n){
return n.value;
});
},
multiple: true, // for Form
_setValueAttr: function(/*Array*/ values, /*Boolean?*/ priorityChange){
// summary:
// Hook so set('value', values) works.
// description:
// Set the value(s) of this Select based on passed values
query("option",this.containerNode).forEach(function(n){
n.selected = (array.indexOf(values,n.value) != -1);
});
this.inherited(arguments);
},
invertSelection: function(/*Boolean?*/ onChange){
// summary:
// Invert the selection
// onChange: Boolean
// If false, onChange is not fired.
var val = [];
query("option",this.containerNode).forEach(function(n){
if(!n.selected){ val.push(n.value); }
});
this._setValueAttr(val, !(onChange === false || onChange == null));
},
_onChange: function(/*Event*/){
this._handleOnChange(this.get('value'), true);
},
// for layout widgets:
resize: function(/*Object*/ size){
if(size){
domGeometry.setMarginBox(this.domNode, size);
}
},
postCreate: function(){
this._set('value', this.get('value'));
this.inherited(arguments);
}
});
});
},
'dijit/form/_DateTimeTextBox':function(){
define([
"dojo/date", // date date.compare
"dojo/date/locale", // locale.regexp
"dojo/date/stamp", // stamp.fromISOString stamp.toISOString
"dojo/_base/declare", // declare
"dojo/_base/lang", // lang.getObject
"./RangeBoundTextBox",
"../_HasDropDown",
"dojo/text!./templates/DropDownBox.html"
], function(date, locale, stamp, declare, lang, RangeBoundTextBox, _HasDropDown, template){
/*=====
var _HasDropDown = dijit._HasDropDown;
var RangeBoundTextBox = dijit.form.RangeBoundTextBox;
=====*/
// module:
// dijit/form/_DateTimeTextBox
// summary:
// Base class for validating, serializable, range-bound date or time text box.
new Date("X"); // workaround for #11279, new Date("") == NaN
/*=====
declare(
"dijit.form._DateTimeTextBox.__Constraints",
[RangeBoundTextBox.__Constraints, locale.__FormatOptions], {
// summary:
// Specifies both the rules on valid/invalid values (first/last date/time allowed),
// and also formatting options for how the date/time is displayed.
// example:
// To restrict to dates within 2004, displayed in a long format like "December 25, 2005":
// | {min:'2004-01-01',max:'2004-12-31', formatLength:'long'}
});
=====*/
var _DateTimeTextBox = declare("dijit.form._DateTimeTextBox", [RangeBoundTextBox, _HasDropDown], {
// summary:
// Base class for validating, serializable, range-bound date or time text box.
templateString: template,
// hasDownArrow: [const] Boolean
// Set this textbox to display a down arrow button, to open the drop down list.
hasDownArrow: true,
// openOnClick: [const] Boolean
// Set to true to open drop down upon clicking anywhere on the textbox.
openOnClick: true,
/*=====
// constraints: dijit.form._DateTimeTextBox.__Constraints
// Despite the name, this parameter specifies both constraints on the input
// (including starting/ending dates/times allowed) as well as
// formatting options like whether the date is displayed in long (ex: December 25, 2005)
// or short (ex: 12/25/2005) format. See `dijit.form._DateTimeTextBox.__Constraints` for details.
constraints: {},
======*/
// Override ValidationTextBox.regExpGen().... we use a reg-ex generating function rather
// than a straight regexp to deal with locale (plus formatting options too?)
regExpGen: locale.regexp,
// datePackage: String
// JavaScript namespace to find calendar routines. Uses Gregorian calendar routines
// at dojo.date, by default.
datePackage: date,
postMixInProperties: function(){
this.inherited(arguments);
this._set("type", "text"); // in case type="date"|"time" was specified which messes up parse/format
},
// Override _FormWidget.compare() to work for dates/times
compare: function(/*Date*/ val1, /*Date*/ val2){
var isInvalid1 = this._isInvalidDate(val1);
var isInvalid2 = this._isInvalidDate(val2);
return isInvalid1 ? (isInvalid2 ? 0 : -1) : (isInvalid2 ? 1 : date.compare(val1, val2, this._selector));
},
// flag to _HasDropDown to make drop down Calendar width == <input> width
forceWidth: true,
format: function(/*Date*/ value, /*dojo.date.locale.__FormatOptions*/ constraints){
// summary:
// Formats the value as a Date, according to specified locale (second argument)
// tags:
// protected
if(!value){ return ''; }
return this.dateLocaleModule.format(value, constraints);
},
"parse": function(/*String*/ value, /*dojo.date.locale.__FormatOptions*/ constraints){
// summary:
// Parses as string as a Date, according to constraints
// tags:
// protected
return this.dateLocaleModule.parse(value, constraints) || (this._isEmpty(value) ? null : undefined); // Date
},
// Overrides ValidationTextBox.serialize() to serialize a date in canonical ISO format.
serialize: function(/*anything*/ val, /*Object?*/ options){
if(val.toGregorian){
val = val.toGregorian();
}
return stamp.toISOString(val, options);
},
// dropDownDefaultValue: Date
// The default value to focus in the popupClass widget when the textbox value is empty.
dropDownDefaultValue : new Date(),
// value: Date
// The value of this widget as a JavaScript Date object. Use get("value") / set("value", val) to manipulate.
// When passed to the parser in markup, must be specified according to `dojo.date.stamp.fromISOString`
value: new Date(""), // value.toString()="NaN"
_blankValue: null, // used by filter() when the textbox is blank
// popupClass: [protected extension] String
// Name of the popup widget class used to select a date/time.
// Subclasses should specify this.
popupClass: "", // default is no popup = text only
// _selector: [protected extension] String
// Specifies constraints.selector passed to dojo.date functions, should be either
// "date" or "time".
// Subclass must specify this.
_selector: "",
constructor: function(/*Object*/ args){
this.datePackage = args.datePackage || this.datePackage;
this.dateFuncObj = typeof this.datePackage == "string" ?
lang.getObject(this.datePackage, false) :// "string" part for back-compat, remove for 2.0
this.datePackage;
this.dateClassObj = this.dateFuncObj.Date || Date;
this.dateLocaleModule = lang.getObject("locale", false, this.dateFuncObj);
this.regExpGen = this.dateLocaleModule.regexp;
this._invalidDate = this.constructor.prototype.value.toString();
},
buildRendering: function(){
this.inherited(arguments);
if(!this.hasDownArrow){
this._buttonNode.style.display = "none";
}
// If openOnClick is true, we basically just want to treat the whole widget as the
// button. We need to do that also if the actual drop down button will be hidden,
// so that there's a mouse method for opening the drop down.
if(this.openOnClick || !this.hasDownArrow){
this._buttonNode = this.domNode;
this.baseClass += " dijitComboBoxOpenOnClick";
}
},
_setConstraintsAttr: function(/*Object*/ constraints){
constraints.selector = this._selector;
constraints.fullYear = true; // see #5465 - always format with 4-digit years
var fromISO = stamp.fromISOString;
if(typeof constraints.min == "string"){ constraints.min = fromISO(constraints.min); }
if(typeof constraints.max == "string"){ constraints.max = fromISO(constraints.max); }
this.inherited(arguments);
},
_isInvalidDate: function(/*Date*/ value){
// summary:
// Runs various tests on the value, checking for invalid conditions
// tags:
// private
return !value || isNaN(value) || typeof value != "object" || value.toString() == this._invalidDate;
},
_setValueAttr: function(/*Date|String*/ value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
// summary:
// Sets the date on this textbox. Note: value can be a JavaScript Date literal or a string to be parsed.
if(value !== undefined){
if(typeof value == "string"){
value = stamp.fromISOString(value);
}
if(this._isInvalidDate(value)){
value = null;
}
if(value instanceof Date && !(this.dateClassObj instanceof Date)){
value = new this.dateClassObj(value);
}
}
this.inherited(arguments);
if(this.value instanceof Date){
this.filterString = "";
}
if(this.dropDown){
this.dropDown.set('value', value, false);
}
},
_set: function(attr, value){
// Avoid spurious watch() notifications when value is changed to new Date object w/the same value
if(attr == "value" && this.value instanceof Date && this.compare(value, this.value) == 0){
return;
}
this.inherited(arguments);
},
_setDropDownDefaultValueAttr: function(/*Date*/ val){
if(this._isInvalidDate(val)){
// convert null setting into today's date, since there needs to be *some* default at all times.
val = new this.dateClassObj();
}
this.dropDownDefaultValue = val;
},
openDropDown: function(/*Function*/ callback){
// rebuild drop down every time, so that constraints get copied (#6002)
if(this.dropDown){
this.dropDown.destroy();
}
var PopupProto = lang.isString(this.popupClass) ? lang.getObject(this.popupClass, false) : this.popupClass,
textBox = this,
value = this.get("value");
this.dropDown = new PopupProto({
onChange: function(value){
// this will cause InlineEditBox and other handlers to do stuff so make sure it's last
textBox.set('value', value, true);
},
id: this.id + "_popup",
dir: textBox.dir,
lang: textBox.lang,
value: value,
currentFocus: !this._isInvalidDate(value) ? value : this.dropDownDefaultValue,
constraints: textBox.constraints,
filterString: textBox.filterString, // for TimeTextBox, to filter times shown
datePackage: textBox.datePackage,
isDisabledDate: function(/*Date*/ date){
// summary:
// disables dates outside of the min/max of the _DateTimeTextBox
return !textBox.rangeCheck(date, textBox.constraints);
}
});
this.inherited(arguments);
},
_getDisplayedValueAttr: function(){
return this.textbox.value;
},
_setDisplayedValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange){
this._setValueAttr(this.parse(value, this.constraints), priorityChange, value);
}
});
return _DateTimeTextBox;
});
},
'dijit/form/_ToggleButtonMixin':function(){
define([
"dojo/_base/declare", // declare
"dojo/dom-attr" // domAttr.set
], function(declare, domAttr){
// module:
// dijit/form/_ToggleButtonMixin
// summary:
// A mixin to provide functionality to allow a button that can be in two states (checked or not).
return declare("dijit.form._ToggleButtonMixin", null, {
// summary:
// A mixin to provide functionality to allow a button that can be in two states (checked or not).
// checked: Boolean
// Corresponds to the native HTML <input> element's attribute.
// In markup, specified as "checked='checked'" or just "checked".
// True if the button is depressed, or the checkbox is checked,
// or the radio button is selected, etc.
checked: false,
// aria-pressed for toggle buttons, and aria-checked for checkboxes
_aria_attr: "aria-pressed",
_onClick: function(/*Event*/ evt){
var original = this.checked;
this._set('checked', !original); // partially set the toggled value, assuming the toggle will work, so it can be overridden in the onclick handler
var ret = this.inherited(arguments); // the user could reset the value here
this.set('checked', ret ? this.checked : original); // officially set the toggled or user value, or reset it back
return ret;
},
_setCheckedAttr: function(/*Boolean*/ value, /*Boolean?*/ priorityChange){
this._set("checked", value);
domAttr.set(this.focusNode || this.domNode, "checked", value);
(this.focusNode || this.domNode).setAttribute(this._aria_attr, value ? "true" : "false"); // aria values should be strings
this._handleOnChange(value, priorityChange);
},
reset: function(){
// summary:
// Reset the widget's value to what it was at initialization time
this._hasBeenBlurred = false;
// set checked state to original setting
this.set('checked', this.params.checked || false);
}
});
});
},
'dijit/Calendar':function(){
define([
"dojo/_base/array", // array.map
"dojo/date",
"dojo/date/locale",
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.get
"dojo/dom-class", // domClass.add domClass.contains domClass.remove domClass.toggle
"dojo/_base/event", // event.stop
"dojo/_base/kernel", // kernel.deprecated
"dojo/keys", // keys
"dojo/_base/lang", // lang.hitch
"dojo/_base/sniff", // has("ie")
"./CalendarLite",
"./_Widget",
"./_CssStateMixin",
"./_TemplatedMixin",
"./form/DropDownButton",
"./hccss" // not used directly, but sets CSS class on <body>
], function(array, date, local, declare, domAttr, domClass, event, kernel, keys, lang, has,
CalendarLite, _Widget, _CssStateMixin, _TemplatedMixin, DropDownButton){
/*=====
var CalendarLite = dijit.CalendarLite;
var _CssStateMixin = dijit._CssStateMixin;
var _Widget = dijit._Widget;
var _TemplatedMixin = dijit._TemplatedMixin;
var DropDownButton = dijit.form.DropDownButton;
=====*/
// module:
// dijit/Calendar
// summary:
// A simple GUI for choosing a date in the context of a monthly calendar.
var Calendar = declare("dijit.Calendar",
[CalendarLite, _Widget, _CssStateMixin], // _Widget for deprecated methods like setAttribute()
{
// summary:
// A simple GUI for choosing a date in the context of a monthly calendar.
//
// description:
// See CalendarLite for general description. Calendar extends CalendarLite, adding:
// - month drop down list
// - keyboard navigation
// - CSS classes for hover/mousepress on date, month, and year nodes
// - support of deprecated methods (will be removed in 2.0)
// Set node classes for various mouse events, see dijit._CssStateMixin for more details
cssStateNodes: {
"decrementMonth": "dijitCalendarArrow",
"incrementMonth": "dijitCalendarArrow",
"previousYearLabelNode": "dijitCalendarPreviousYear",
"nextYearLabelNode": "dijitCalendarNextYear"
},
setValue: function(/*Date*/ value){
// summary:
// Deprecated. Use set('value', ...) instead.
// tags:
// deprecated
kernel.deprecated("dijit.Calendar:setValue() is deprecated. Use set('value', ...) instead.", "", "2.0");
this.set('value', value);
},
_createMonthWidget: function(){
// summary:
// Creates the drop down button that displays the current month and lets user pick a new one
return new Calendar._MonthDropDownButton({
id: this.id + "_mddb",
tabIndex: -1,
onMonthSelect: lang.hitch(this, "_onMonthSelect"),
lang: this.lang,
dateLocaleModule: this.dateLocaleModule
}, this.monthNode);
},
buildRendering: function(){
this.inherited(arguments);
// Events specific to Calendar, not used in CalendarLite
this.connect(this.domNode, "onkeypress", "_onKeyPress");
this.connect(this.dateRowsNode, "onmouseover", "_onDayMouseOver");
this.connect(this.dateRowsNode, "onmouseout", "_onDayMouseOut");
this.connect(this.dateRowsNode, "onmousedown", "_onDayMouseDown");
this.connect(this.dateRowsNode, "onmouseup", "_onDayMouseUp");
},
_onMonthSelect: function(/*Number*/ newMonth){
// summary:
// Handler for when user selects a month from the drop down list
// tags:
// protected
// move to selected month, bounding by the number of days in the month
// (ex: dec 31 --> jan 28, not jan 31)
this._setCurrentFocusAttr(this.dateFuncObj.add(this.currentFocus, "month",
newMonth - this.currentFocus.getMonth()));
},
_onDayMouseOver: function(/*Event*/ evt){
// summary:
// Handler for mouse over events on days, sets hovered style
// tags:
// protected
// event can occur on <td> or the <span> inside the td,
// set node to the <td>.
var node =
domClass.contains(evt.target, "dijitCalendarDateLabel") ?
evt.target.parentNode :
evt.target;
if(node && (
(node.dijitDateValue && !domClass.contains(node, "dijitCalendarDisabledDate"))
|| node == this.previousYearLabelNode || node == this.nextYearLabelNode
)){
domClass.add(node, "dijitCalendarHoveredDate");
this._currentNode = node;
}
},
_onDayMouseOut: function(/*Event*/ evt){
// summary:
// Handler for mouse out events on days, clears hovered style
// tags:
// protected
if(!this._currentNode){ return; }
// if mouse out occurs moving from <td> to <span> inside <td>, ignore it
if(evt.relatedTarget && evt.relatedTarget.parentNode == this._currentNode){ return; }
var cls = "dijitCalendarHoveredDate";
if(domClass.contains(this._currentNode, "dijitCalendarActiveDate")){
cls += " dijitCalendarActiveDate";
}
domClass.remove(this._currentNode, cls);
this._currentNode = null;
},
_onDayMouseDown: function(/*Event*/ evt){
var node = evt.target.parentNode;
if(node && node.dijitDateValue && !domClass.contains(node, "dijitCalendarDisabledDate")){
domClass.add(node, "dijitCalendarActiveDate");
this._currentNode = node;
}
},
_onDayMouseUp: function(/*Event*/ evt){
var node = evt.target.parentNode;
if(node && node.dijitDateValue){
domClass.remove(node, "dijitCalendarActiveDate");
}
},
handleKey: function(/*Event*/ evt){
// summary:
// Provides keyboard navigation of calendar.
// description:
// Called from _onKeyPress() to handle keypress on a stand alone Calendar,
// and also from `dijit.form._DateTimeTextBox` to pass a keypress event
// from the `dijit.form.DateTextBox` to be handled in this widget
// returns:
// False if the key was recognized as a navigation key,
// to indicate that the event was handled by Calendar and shouldn't be propogated
// tags:
// protected
var increment = -1,
interval,
newValue = this.currentFocus;
switch(evt.charOrCode){
case keys.RIGHT_ARROW:
increment = 1;
//fallthrough...
case keys.LEFT_ARROW:
interval = "day";
if(!this.isLeftToRight()){ increment *= -1; }
break;
case keys.DOWN_ARROW:
increment = 1;
//fallthrough...
case keys.UP_ARROW:
interval = "week";
break;
case keys.PAGE_DOWN:
increment = 1;
//fallthrough...
case keys.PAGE_UP:
interval = evt.ctrlKey || evt.altKey ? "year" : "month";
break;
case keys.END:
// go to the next month
newValue = this.dateFuncObj.add(newValue, "month", 1);
// subtract a day from the result when we're done
interval = "day";
//fallthrough...
case keys.HOME:
newValue = new this.dateClassObj(newValue);
newValue.setDate(1);
break;
case keys.ENTER:
case " ":
this.set("value", this.currentFocus);
break;
default:
return true;
}
if(interval){
newValue = this.dateFuncObj.add(newValue, interval, increment);
}
this._setCurrentFocusAttr(newValue);
return false;
},
_onKeyPress: function(/*Event*/ evt){
// summary:
// For handling keypress events on a stand alone calendar
if(!this.handleKey(evt)){
event.stop(evt);
}
},
onValueSelected: function(/*Date*/ /*===== date =====*/){
// summary:
// Deprecated. Notification that a date cell was selected. It may be the same as the previous value.
// description:
// Formerly used by `dijit.form._DateTimeTextBox` (and thus `dijit.form.DateTextBox`)
// to get notification when the user has clicked a date. Now onExecute() (above) is used.
// tags:
// protected
},
onChange: function(value){
this.onValueSelected(value); // remove in 2.0
},
getClassForDate: function(/*===== dateObject, locale =====*/){
// summary:
// May be overridden to return CSS classes to associate with the date entry for the given dateObject,
// for example to indicate a holiday in specified locale.
// dateObject: Date
// locale: String?
// tags:
// extension
/*=====
return ""; // String
=====*/
}
});
Calendar._MonthDropDownButton = declare("dijit.Calendar._MonthDropDownButton", DropDownButton, {
// summary:
// DropDownButton for the current month. Displays name of current month
// and a list of month names in the drop down
onMonthSelect: function(){ },
postCreate: function(){
this.inherited(arguments);
this.dropDown = new Calendar._MonthDropDown({
id: this.id + "_mdd", //do not change this id because it is referenced in the template
onChange: this.onMonthSelect
});
},
_setMonthAttr: function(month){
// summary:
// Set the current month to display as a label
var monthNames = this.dateLocaleModule.getNames('months', 'wide', 'standAlone', this.lang, month);
this.dropDown.set("months", monthNames);
// Set name of current month and also fill in spacer element with all the month names
// (invisible) so that the maximum width will affect layout. But not on IE6 because then
// the center <TH> overlaps the right <TH> (due to a browser bug).
this.containerNode.innerHTML =
(has("ie") == 6 ? "" : "<div class='dijitSpacer'>" + this.dropDown.domNode.innerHTML + "</div>") +
"<div class='dijitCalendarMonthLabel dijitCalendarCurrentMonthLabel'>" + monthNames[month.getMonth()] + "</div>";
}
});
Calendar._MonthDropDown = declare("dijit.Calendar._MonthDropDown", [_Widget, _TemplatedMixin], {
// summary:
// The list-of-months drop down from the MonthDropDownButton
// months: String[]
// List of names of months, possibly w/some undefined entries for Hebrew leap months
// (ex: ["January", "February", undefined, "April", ...])
months: [],
templateString: "<div class='dijitCalendarMonthMenu dijitMenu' " +
"data-dojo-attach-event='onclick:_onClick,onmouseover:_onMenuHover,onmouseout:_onMenuHover'></div>",
_setMonthsAttr: function(/*String[]*/ months){
this.domNode.innerHTML = array.map(months, function(month, idx){
return month ? "<div class='dijitCalendarMonthLabel' month='" + idx +"'>" + month + "</div>" : "";
}).join("");
},
_onClick: function(/*Event*/ evt){
this.onChange(domAttr.get(evt.target, "month"));
},
onChange: function(/*Number*/ /*===== month =====*/){
// summary:
// Callback when month is selected from drop down
},
_onMenuHover: function(evt){
domClass.toggle(evt.target, "dijitCalendarMonthLabelHover", evt.type == "mouseover");
}
});
return Calendar;
});
},
'url:dijit/form/templates/Select.html':"<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tdata-dojo-attach-point=\"_buttonNode,tableNode,focusNode\" cellspacing='0' cellpadding='0'\n\trole=\"combobox\" aria-haspopup=\"true\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonContents dijitButtonNode\" role=\"presentation\"\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\" data-dojo-attach-point=\"containerNode,_popupStateNode\"></span\n\t\t\t><input type=\"hidden\" ${!nameAttrSetting} data-dojo-attach-point=\"valueNode\" value=\"${value}\" aria-hidden=\"true\"\n\t\t/></td><td class=\"dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton\"\n\t\t\t\tdata-dojo-attach-point=\"titleNode\" role=\"presentation\"\n\t\t\t><div class=\"dijitReset dijitArrowButtonInner\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitArrowButtonChar\" role=\"presentation\">&#9660;</div\n\t\t></td\n\t></tr></tbody\n></table>\n",
'dijit/_editor/selection':function(){
define([
"dojo/dom", // dom.byId
"dojo/_base/lang",
"dojo/_base/sniff", // has("ie") has("opera")
"dojo/_base/window", // win.body win.doc win.doc.createElement win.doc.selection win.doc.selection.createRange win.doc.selection.type.toLowerCase win.global win.global.getSelection
".." // for exporting symbols to dijit._editor.selection (TODO: remove in 2.0)
], function(dom, lang, has, win, dijit){
// module:
// dijit/_editor/selection
// summary:
// Text selection API
lang.getObject("_editor.selection", true, dijit);
// FIXME:
// all of these methods branch internally for IE. This is probably
// sub-optimal in terms of runtime performance. We should investigate the
// size difference for differentiating at definition time.
lang.mixin(dijit._editor.selection, {
getType: function(){
// summary:
// Get the selection type (like win.doc.select.type in IE).
if(has("ie") < 9){
return win.doc.selection.type.toLowerCase();
}else{
var stype = "text";
// Check if the actual selection is a CONTROL (IMG, TABLE, HR, etc...).
var oSel;
try{
oSel = win.global.getSelection();
}catch(e){ /*squelch*/ }
if(oSel && oSel.rangeCount == 1){
var oRange = oSel.getRangeAt(0);
if( (oRange.startContainer == oRange.endContainer) &&
((oRange.endOffset - oRange.startOffset) == 1) &&
(oRange.startContainer.nodeType != 3 /* text node*/)
){
stype = "control";
}
}
return stype; //String
}
},
getSelectedText: function(){
// summary:
// Return the text (no html tags) included in the current selection or null if no text is selected
if(has("ie") < 9){
if(dijit._editor.selection.getType() == 'control'){
return null;
}
return win.doc.selection.createRange().text;
}else{
var selection = win.global.getSelection();
if(selection){
return selection.toString(); //String
}
}
return '';
},
getSelectedHtml: function(){
// summary:
// Return the html text of the current selection or null if unavailable
if(has("ie") < 9){
if(dijit._editor.selection.getType() == 'control'){
return null;
}
return win.doc.selection.createRange().htmlText;
}else{
var selection = win.global.getSelection();
if(selection && selection.rangeCount){
var i;
var html = "";
for(i = 0; i < selection.rangeCount; i++){
//Handle selections spanning ranges, such as Opera
var frag = selection.getRangeAt(i).cloneContents();
var div = win.doc.createElement("div");
div.appendChild(frag);
html += div.innerHTML;
}
return html; //String
}
return null;
}
},
getSelectedElement: function(){
// summary:
// Retrieves the selected element (if any), just in the case that
// a single element (object like and image or a table) is
// selected.
if(dijit._editor.selection.getType() == "control"){
if(has("ie") < 9){
var range = win.doc.selection.createRange();
if(range && range.item){
return win.doc.selection.createRange().item(0);
}
}else{
var selection = win.global.getSelection();
return selection.anchorNode.childNodes[ selection.anchorOffset ];
}
}
return null;
},
getParentElement: function(){
// summary:
// Get the parent element of the current selection
if(dijit._editor.selection.getType() == "control"){
var p = this.getSelectedElement();
if(p){ return p.parentNode; }
}else{
if(has("ie") < 9){
var r = win.doc.selection.createRange();
r.collapse(true);
return r.parentElement();
}else{
var selection = win.global.getSelection();
if(selection){
var node = selection.anchorNode;
while(node && (node.nodeType != 1)){ // not an element
node = node.parentNode;
}
return node;
}
}
}
return null;
},
hasAncestorElement: function(/*String*/tagName /* ... */){
// summary:
// Check whether current selection has a parent element which is
// of type tagName (or one of the other specified tagName)
// tagName: String
// The tag name to determine if it has an ancestor of.
return this.getAncestorElement.apply(this, arguments) != null; //Boolean
},
getAncestorElement: function(/*String*/tagName /* ... */){
// summary:
// Return the parent element of the current selection which is of
// type tagName (or one of the other specified tagName)
// tagName: String
// The tag name to determine if it has an ancestor of.
var node = this.getSelectedElement() || this.getParentElement();
return this.getParentOfType(node, arguments); //DOMNode
},
isTag: function(/*DomNode*/ node, /*String[]*/ tags){
// summary:
// Function to determine if a node is one of an array of tags.
// node:
// The node to inspect.
// tags:
// An array of tag name strings to check to see if the node matches.
if(node && node.tagName){
var _nlc = node.tagName.toLowerCase();
for(var i=0; i<tags.length; i++){
var _tlc = String(tags[i]).toLowerCase();
if(_nlc == _tlc){
return _tlc; // String
}
}
}
return "";
},
getParentOfType: function(/*DomNode*/ node, /*String[]*/ tags){
// summary:
// Function to locate a parent node that matches one of a set of tags
// node:
// The node to inspect.
// tags:
// An array of tag name strings to check to see if the node matches.
while(node){
if(this.isTag(node, tags).length){
return node; // DOMNode
}
node = node.parentNode;
}
return null;
},
collapse: function(/*Boolean*/beginning){
// summary:
// Function to collapse (clear), the current selection
// beginning: Boolean
// Boolean to indicate whether to collapse the cursor to the beginning of the selection or end.
if(window.getSelection){
var selection = win.global.getSelection();
if(selection.removeAllRanges){ // Mozilla
if(beginning){
selection.collapseToStart();
}else{
selection.collapseToEnd();
}
}else{ // Safari
// pulled from WebCore/ecma/kjs_window.cpp, line 2536
selection.collapse(beginning);
}
}else if(has("ie")){ // IE
var range = win.doc.selection.createRange();
range.collapse(beginning);
range.select();
}
},
remove: function(){
// summary:
// Function to delete the currently selected content from the document.
var sel = win.doc.selection;
if(has("ie") < 9){
if(sel.type.toLowerCase() != "none"){
sel.clear();
}
return sel; //Selection
}else{
sel = win.global.getSelection();
sel.deleteFromDocument();
return sel; //Selection
}
},
selectElementChildren: function(/*DomNode*/element,/*Boolean?*/nochangefocus){
// summary:
// clear previous selection and select the content of the node
// (excluding the node itself)
// element: DOMNode
// The element you wish to select the children content of.
// nochangefocus: Boolean
// Boolean to indicate if the foxus should change or not.
var global = win.global;
var doc = win.doc;
var range;
element = dom.byId(element);
if(doc.selection && has("ie") < 9 && win.body().createTextRange){ // IE
range = element.ownerDocument.body.createTextRange();
range.moveToElementText(element);
if(!nochangefocus){
try{
range.select(); // IE throws an exception here if the widget is hidden. See #5439
}catch(e){ /* squelch */}
}
}else if(global.getSelection){
var selection = win.global.getSelection();
if(has("opera")){
//Opera's selectAllChildren doesn't seem to work right
//against <body> nodes and possibly others ... so
//we use the W3C range API
if(selection.rangeCount){
range = selection.getRangeAt(0);
}else{
range = doc.createRange();
}
range.setStart(element, 0);
range.setEnd(element,(element.nodeType == 3)?element.length:element.childNodes.length);
selection.addRange(range);
}else{
selection.selectAllChildren(element);
}
}
},
selectElement: function(/*DomNode*/element,/*Boolean?*/nochangefocus){
// summary:
// clear previous selection and select element (including all its children)
// element: DOMNode
// The element to select.
// nochangefocus: Boolean
// Boolean indicating if the focus should be changed. IE only.
var range;
var doc = win.doc;
var global = win.global;
element = dom.byId(element);
if(has("ie") < 9 && win.body().createTextRange){
try{
var tg = element.tagName ? element.tagName.toLowerCase() : "";
if(tg === "img" || tg === "table"){
range = win.body().createControlRange();
}else{
range = win.body().createRange();
}
range.addElement(element);
if(!nochangefocus){
range.select();
}
}catch(e){
this.selectElementChildren(element,nochangefocus);
}
}else if(global.getSelection){
var selection = global.getSelection();
range = doc.createRange();
if(selection.removeAllRanges){ // Mozilla
// FIXME: does this work on Safari?
if(has("opera")){
//Opera works if you use the current range on
//the selection if present.
if(selection.getRangeAt(0)){
range = selection.getRangeAt(0);
}
}
range.selectNode(element);
selection.removeAllRanges();
selection.addRange(range);
}
}
},
inSelection: function(node){
// summary:
// This function determines if 'node' is
// in the current selection.
// tags:
// public
if(node){
var newRange;
var doc = win.doc;
var range;
if(win.global.getSelection){
//WC3
var sel = win.global.getSelection();
if(sel && sel.rangeCount > 0){
range = sel.getRangeAt(0);
}
if(range && range.compareBoundaryPoints && doc.createRange){
try{
newRange = doc.createRange();
newRange.setStart(node, 0);
if(range.compareBoundaryPoints(range.START_TO_END, newRange) === 1){
return true;
}
}catch(e){ /* squelch */}
}
}else if(doc.selection){
// Probably IE, so we can't use the range object as the pseudo
// range doesn't implement the boundry checking, we have to
// use IE specific crud.
range = doc.selection.createRange();
try{
newRange = node.ownerDocument.body.createControlRange();
if(newRange){
newRange.addElement(node);
}
}catch(e1){
try{
newRange = node.ownerDocument.body.createTextRange();
newRange.moveToElementText(node);
}catch(e2){/* squelch */}
}
if(range && newRange){
// We can finally compare similar to W3C
if(range.compareEndPoints("EndToStart", newRange) === 1){
return true;
}
}
}
}
return false; // boolean
}
});
return dijit._editor.selection;
});
},
'dijit/form/nls/ComboBox':function(){
define({ root:
//begin v1.x content
({
previousMessage: "Previous choices",
nextMessage: "More choices"
})
//end v1.x content
,
"zh": true,
"zh-tw": true,
"tr": true,
"th": true,
"sv": true,
"sl": true,
"sk": true,
"ru": true,
"ro": true,
"pt": true,
"pt-pt": true,
"pl": true,
"nl": true,
"nb": true,
"ko": true,
"kk": true,
"ja": true,
"it": true,
"hu": true,
"hr": true,
"he": true,
"fr": true,
"fi": true,
"es": true,
"el": true,
"de": true,
"da": true,
"cs": true,
"ca": true,
"az": true,
"ar": true
});
},
'dojo/fx':function(){
define([
"./_base/lang",
"./Evented",
"./_base/kernel",
"./_base/array",
"./_base/connect",
"./_base/fx",
"./dom",
"./dom-style",
"./dom-geometry",
"./ready",
"require" // for context sensitive loading of Toggler
], function(lang, Evented, dojo, arrayUtil, connect, baseFx, dom, domStyle, geom, ready, require) {
// module:
// dojo/fx
// summary:
// TODOC
/*=====
dojo.fx = {
// summary: Effects library on top of Base animations
};
var coreFx = dojo.fx;
=====*/
// For back-compat, remove in 2.0.
if(!dojo.isAsync){
ready(0, function(){
var requires = ["./fx/Toggler"];
require(requires); // use indirection so modules not rolled into a build
});
}
var coreFx = dojo.fx = {};
var _baseObj = {
_fire: function(evt, args){
if(this[evt]){
this[evt].apply(this, args||[]);
}
return this;
}
};
var _chain = function(animations){
this._index = -1;
this._animations = animations||[];
this._current = this._onAnimateCtx = this._onEndCtx = null;
this.duration = 0;
arrayUtil.forEach(this._animations, function(a){
this.duration += a.duration;
if(a.delay){ this.duration += a.delay; }
}, this);
};
_chain.prototype = new Evented();
lang.extend(_chain, {
_onAnimate: function(){
this._fire("onAnimate", arguments);
},
_onEnd: function(){
connect.disconnect(this._onAnimateCtx);
connect.disconnect(this._onEndCtx);
this._onAnimateCtx = this._onEndCtx = null;
if(this._index + 1 == this._animations.length){
this._fire("onEnd");
}else{
// switch animations
this._current = this._animations[++this._index];
this._onAnimateCtx = connect.connect(this._current, "onAnimate", this, "_onAnimate");
this._onEndCtx = connect.connect(this._current, "onEnd", this, "_onEnd");
this._current.play(0, true);
}
},
play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
if(!this._current){ this._current = this._animations[this._index = 0]; }
if(!gotoStart && this._current.status() == "playing"){ return this; }
var beforeBegin = connect.connect(this._current, "beforeBegin", this, function(){
this._fire("beforeBegin");
}),
onBegin = connect.connect(this._current, "onBegin", this, function(arg){
this._fire("onBegin", arguments);
}),
onPlay = connect.connect(this._current, "onPlay", this, function(arg){
this._fire("onPlay", arguments);
connect.disconnect(beforeBegin);
connect.disconnect(onBegin);
connect.disconnect(onPlay);
});
if(this._onAnimateCtx){
connect.disconnect(this._onAnimateCtx);
}
this._onAnimateCtx = connect.connect(this._current, "onAnimate", this, "_onAnimate");
if(this._onEndCtx){
connect.disconnect(this._onEndCtx);
}
this._onEndCtx = connect.connect(this._current, "onEnd", this, "_onEnd");
this._current.play.apply(this._current, arguments);
return this;
},
pause: function(){
if(this._current){
var e = connect.connect(this._current, "onPause", this, function(arg){
this._fire("onPause", arguments);
connect.disconnect(e);
});
this._current.pause();
}
return this;
},
gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
this.pause();
var offset = this.duration * percent;
this._current = null;
arrayUtil.some(this._animations, function(a){
if(a.duration <= offset){
this._current = a;
return true;
}
offset -= a.duration;
return false;
});
if(this._current){
this._current.gotoPercent(offset / this._current.duration, andPlay);
}
return this;
},
stop: function(/*boolean?*/ gotoEnd){
if(this._current){
if(gotoEnd){
for(; this._index + 1 < this._animations.length; ++this._index){
this._animations[this._index].stop(true);
}
this._current = this._animations[this._index];
}
var e = connect.connect(this._current, "onStop", this, function(arg){
this._fire("onStop", arguments);
connect.disconnect(e);
});
this._current.stop();
}
return this;
},
status: function(){
return this._current ? this._current.status() : "stopped";
},
destroy: function(){
if(this._onAnimateCtx){ connect.disconnect(this._onAnimateCtx); }
if(this._onEndCtx){ connect.disconnect(this._onEndCtx); }
}
});
lang.extend(_chain, _baseObj);
coreFx.chain = /*===== dojo.fx.chain = =====*/ function(/*dojo.Animation[]*/ animations){
// summary:
// Chain a list of `dojo.Animation`s to run in sequence
//
// description:
// Return a `dojo.Animation` which will play all passed
// `dojo.Animation` instances in sequence, firing its own
// synthesized events simulating a single animation. (eg:
// onEnd of this animation means the end of the chain,
// not the individual animations within)
//
// example:
// Once `node` is faded out, fade in `otherNode`
// | dojo.fx.chain([
// | dojo.fadeIn({ node:node }),
// | dojo.fadeOut({ node:otherNode })
// | ]).play();
//
return new _chain(animations); // dojo.Animation
};
var _combine = function(animations){
this._animations = animations||[];
this._connects = [];
this._finished = 0;
this.duration = 0;
arrayUtil.forEach(animations, function(a){
var duration = a.duration;
if(a.delay){ duration += a.delay; }
if(this.duration < duration){ this.duration = duration; }
this._connects.push(connect.connect(a, "onEnd", this, "_onEnd"));
}, this);
this._pseudoAnimation = new baseFx.Animation({curve: [0, 1], duration: this.duration});
var self = this;
arrayUtil.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop", "onEnd"],
function(evt){
self._connects.push(connect.connect(self._pseudoAnimation, evt,
function(){ self._fire(evt, arguments); }
));
}
);
};
lang.extend(_combine, {
_doAction: function(action, args){
arrayUtil.forEach(this._animations, function(a){
a[action].apply(a, args);
});
return this;
},
_onEnd: function(){
if(++this._finished > this._animations.length){
this._fire("onEnd");
}
},
_call: function(action, args){
var t = this._pseudoAnimation;
t[action].apply(t, args);
},
play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
this._finished = 0;
this._doAction("play", arguments);
this._call("play", arguments);
return this;
},
pause: function(){
this._doAction("pause", arguments);
this._call("pause", arguments);
return this;
},
gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
var ms = this.duration * percent;
arrayUtil.forEach(this._animations, function(a){
a.gotoPercent(a.duration < ms ? 1 : (ms / a.duration), andPlay);
});
this._call("gotoPercent", arguments);
return this;
},
stop: function(/*boolean?*/ gotoEnd){
this._doAction("stop", arguments);
this._call("stop", arguments);
return this;
},
status: function(){
return this._pseudoAnimation.status();
},
destroy: function(){
arrayUtil.forEach(this._connects, connect.disconnect);
}
});
lang.extend(_combine, _baseObj);
coreFx.combine = /*===== dojo.fx.combine = =====*/ function(/*dojo.Animation[]*/ animations){
// summary:
// Combine a list of `dojo.Animation`s to run in parallel
//
// description:
// Combine an array of `dojo.Animation`s to run in parallel,
// providing a new `dojo.Animation` instance encompasing each
// animation, firing standard animation events.
//
// example:
// Fade out `node` while fading in `otherNode` simultaneously
// | dojo.fx.combine([
// | dojo.fadeIn({ node:node }),
// | dojo.fadeOut({ node:otherNode })
// | ]).play();
//
// example:
// When the longest animation ends, execute a function:
// | var anim = dojo.fx.combine([
// | dojo.fadeIn({ node: n, duration:700 }),
// | dojo.fadeOut({ node: otherNode, duration: 300 })
// | ]);
// | dojo.connect(anim, "onEnd", function(){
// | // overall animation is done.
// | });
// | anim.play(); // play the animation
//
return new _combine(animations); // dojo.Animation
};
coreFx.wipeIn = /*===== dojo.fx.wipeIn = =====*/ function(/*Object*/ args){
// summary:
// Expand a node to it's natural height.
//
// description:
// Returns an animation that will expand the
// node defined in 'args' object from it's current height to
// it's natural height (with no scrollbar).
// Node must have no margin/border/padding.
//
// args: Object
// A hash-map of standard `dojo.Animation` constructor properties
// (such as easing: node: duration: and so on)
//
// example:
// | dojo.fx.wipeIn({
// | node:"someId"
// | }).play()
var node = args.node = dom.byId(args.node), s = node.style, o;
var anim = baseFx.animateProperty(lang.mixin({
properties: {
height: {
// wrapped in functions so we wait till the last second to query (in case value has changed)
start: function(){
// start at current [computed] height, but use 1px rather than 0
// because 0 causes IE to display the whole panel
o = s.overflow;
s.overflow = "hidden";
if(s.visibility == "hidden" || s.display == "none"){
s.height = "1px";
s.display = "";
s.visibility = "";
return 1;
}else{
var height = domStyle.get(node, "height");
return Math.max(height, 1);
}
},
end: function(){
return node.scrollHeight;
}
}
}
}, args));
var fini = function(){
s.height = "auto";
s.overflow = o;
};
connect.connect(anim, "onStop", fini);
connect.connect(anim, "onEnd", fini);
return anim; // dojo.Animation
};
coreFx.wipeOut = /*===== dojo.fx.wipeOut = =====*/ function(/*Object*/ args){
// summary:
// Shrink a node to nothing and hide it.
//
// description:
// Returns an animation that will shrink node defined in "args"
// from it's current height to 1px, and then hide it.
//
// args: Object
// A hash-map of standard `dojo.Animation` constructor properties
// (such as easing: node: duration: and so on)
//
// example:
// | dojo.fx.wipeOut({ node:"someId" }).play()
var node = args.node = dom.byId(args.node), s = node.style, o;
var anim = baseFx.animateProperty(lang.mixin({
properties: {
height: {
end: 1 // 0 causes IE to display the whole panel
}
}
}, args));
connect.connect(anim, "beforeBegin", function(){
o = s.overflow;
s.overflow = "hidden";
s.display = "";
});
var fini = function(){
s.overflow = o;
s.height = "auto";
s.display = "none";
};
connect.connect(anim, "onStop", fini);
connect.connect(anim, "onEnd", fini);
return anim; // dojo.Animation
};
coreFx.slideTo = /*===== dojo.fx.slideTo = =====*/ function(/*Object*/ args){
// summary:
// Slide a node to a new top/left position
//
// description:
// Returns an animation that will slide "node"
// defined in args Object from its current position to
// the position defined by (args.left, args.top).
//
// args: Object
// A hash-map of standard `dojo.Animation` constructor properties
// (such as easing: node: duration: and so on). Special args members
// are `top` and `left`, which indicate the new position to slide to.
//
// example:
// | .slideTo({ node: node, left:"40", top:"50", units:"px" }).play()
var node = args.node = dom.byId(args.node),
top = null, left = null;
var init = (function(n){
return function(){
var cs = domStyle.getComputedStyle(n);
var pos = cs.position;
top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0);
left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0);
if(pos != 'absolute' && pos != 'relative'){
var ret = geom.position(n, true);
top = ret.y;
left = ret.x;
n.style.position="absolute";
n.style.top=top+"px";
n.style.left=left+"px";
}
};
})(node);
init();
var anim = baseFx.animateProperty(lang.mixin({
properties: {
top: args.top || 0,
left: args.left || 0
}
}, args));
connect.connect(anim, "beforeBegin", anim, init);
return anim; // dojo.Animation
};
return coreFx;
});
},
'dijit/_DialogMixin':function(){
define("dijit/_DialogMixin", [
"dojo/_base/declare", // declare
"./a11y" // _getTabNavigable
], function(declare, a11y){
// module:
// dijit/_DialogMixin
// summary:
// _DialogMixin provides functions useful to Dialog and TooltipDialog
return declare("dijit._DialogMixin", null, {
// summary:
// This provides functions useful to Dialog and TooltipDialog
execute: function(/*Object*/ /*===== formContents =====*/){
// summary:
// Callback when the user hits the submit button.
// Override this method to handle Dialog execution.
// description:
// After the user has pressed the submit button, the Dialog
// first calls onExecute() to notify the container to hide the
// dialog and restore focus to wherever it used to be.
//
// *Then* this method is called.
// type:
// callback
},
onCancel: function(){
// summary:
// Called when user has pressed the Dialog's cancel button, to notify container.
// description:
// Developer shouldn't override or connect to this method;
// it's a private communication device between the TooltipDialog
// and the thing that opened it (ex: `dijit.form.DropDownButton`)
// type:
// protected
},
onExecute: function(){
// summary:
// Called when user has pressed the dialog's OK button, to notify container.
// description:
// Developer shouldn't override or connect to this method;
// it's a private communication device between the TooltipDialog
// and the thing that opened it (ex: `dijit.form.DropDownButton`)
// type:
// protected
},
_onSubmit: function(){
// summary:
// Callback when user hits submit button
// type:
// protected
this.onExecute(); // notify container that we are about to execute
this.execute(this.get('value'));
},
_getFocusItems: function(){
// summary:
// Finds focusable items in dialog,
// and sets this._firstFocusItem and this._lastFocusItem
// tags:
// protected
var elems = a11y._getTabNavigable(this.containerNode);
this._firstFocusItem = elems.lowest || elems.first || this.closeButtonNode || this.domNode;
this._lastFocusItem = elems.last || elems.highest || this._firstFocusItem;
}
});
});
},
'dijit/nls/common':function(){
define({ root:
//begin v1.x content
({
buttonOk: "OK",
buttonCancel: "Cancel",
buttonSave: "Save",
itemClose: "Close"
})
//end v1.x content
,
"zh": true,
"zh-tw": true,
"tr": true,
"th": true,
"sv": true,
"sl": true,
"sk": true,
"ru": true,
"ro": true,
"pt": true,
"pt-pt": true,
"pl": true,
"nl": true,
"nb": true,
"ko": true,
"kk": true,
"ja": true,
"it": true,
"hu": true,
"hr": true,
"he": true,
"fr": true,
"fi": true,
"es": true,
"el": true,
"de": true,
"da": true,
"cs": true,
"ca": true,
"az": true,
"ar": true
});
},
'dijit/Tree':function(){
define([
"dojo/_base/array", // array.filter array.forEach array.map
"dojo/_base/connect", // connect.isCopyKey()
"dojo/cookie", // cookie
"dojo/_base/declare", // declare
"dojo/_base/Deferred", // Deferred
"dojo/DeferredList", // DeferredList
"dojo/dom", // dom.isDescendant
"dojo/dom-class", // domClass.add domClass.remove domClass.replace domClass.toggle
"dojo/dom-geometry", // domGeometry.setMarginBox domGeometry.position
"dojo/dom-style",// domStyle.set
"dojo/_base/event", // event.stop
"dojo/fx", // fxUtils.wipeIn fxUtils.wipeOut
"dojo/_base/kernel", // kernel.deprecated
"dojo/keys", // arrows etc.
"dojo/_base/lang", // lang.getObject lang.mixin lang.hitch
"dojo/topic",
"./focus",
"./registry", // registry.getEnclosingWidget(), manager.defaultDuration
"./_base/manager", // manager.getEnclosingWidget(), manager.defaultDuration
"./_Widget",
"./_TemplatedMixin",
"./_Container",
"./_Contained",
"./_CssStateMixin",
"dojo/text!./templates/TreeNode.html",
"dojo/text!./templates/Tree.html",
"./tree/TreeStoreModel",
"./tree/ForestStoreModel",
"./tree/_dndSelector"
], function(array, connect, cookie, declare, Deferred, DeferredList,
dom, domClass, domGeometry, domStyle, event, fxUtils, kernel, keys, lang, topic,
focus, registry, manager, _Widget, _TemplatedMixin, _Container, _Contained, _CssStateMixin,
treeNodeTemplate, treeTemplate, TreeStoreModel, ForestStoreModel, _dndSelector){
/*=====
var _Widget = dijit._Widget;
var _TemplatedMixin = dijit._TemplatedMixin;
var _CssStateMixin = dijit._CssStateMixin;
var _Container = dijit._Container;
var _Contained = dijit._Contained;
=====*/
// module:
// dijit/Tree
// summary:
// dijit.Tree widget, and internal dijit._TreeNode widget
var TreeNode = declare(
"dijit._TreeNode",
[_Widget, _TemplatedMixin, _Container, _Contained, _CssStateMixin],
{
// summary:
// Single node within a tree. This class is used internally
// by Tree and should not be accessed directly.
// tags:
// private
// item: [const] Item
// the dojo.data entry this tree represents
item: null,
// isTreeNode: [protected] Boolean
// Indicates that this is a TreeNode. Used by `dijit.Tree` only,
// should not be accessed directly.
isTreeNode: true,
// label: String
// Text of this tree node
label: "",
_setLabelAttr: {node: "labelNode", type: "innerText"},
// isExpandable: [private] Boolean
// This node has children, so show the expando node (+ sign)
isExpandable: null,
// isExpanded: [readonly] Boolean
// This node is currently expanded (ie, opened)
isExpanded: false,
// state: [private] String
// Dynamic loading-related stuff.
// When an empty folder node appears, it is "UNCHECKED" first,
// then after dojo.data query it becomes "LOADING" and, finally "LOADED"
state: "UNCHECKED",
templateString: treeNodeTemplate,
baseClass: "dijitTreeNode",
// For hover effect for tree node, and focus effect for label
cssStateNodes: {
rowNode: "dijitTreeRow",
labelNode: "dijitTreeLabel"
},
// Tooltip is defined in _WidgetBase but we need to handle the mapping to DOM here
_setTooltipAttr: {node: "rowNode", type: "attribute", attribute: "title"},
buildRendering: function(){
this.inherited(arguments);
// set expand icon for leaf
this._setExpando();
// set icon and label class based on item
this._updateItemClasses(this.item);
if(this.isExpandable){
this.labelNode.setAttribute("aria-expanded", this.isExpanded);
}
//aria-selected should be false on all selectable elements.
this.setSelected(false);
},
_setIndentAttr: function(indent){
// summary:
// Tell this node how many levels it should be indented
// description:
// 0 for top level nodes, 1 for their children, 2 for their
// grandchildren, etc.
// Math.max() is to prevent negative padding on hidden root node (when indent == -1)
var pixels = (Math.max(indent, 0) * this.tree._nodePixelIndent) + "px";
domStyle.set(this.domNode, "backgroundPosition", pixels + " 0px");
domStyle.set(this.rowNode, this.isLeftToRight() ? "paddingLeft" : "paddingRight", pixels);
array.forEach(this.getChildren(), function(child){
child.set("indent", indent+1);
});
this._set("indent", indent);
},
markProcessing: function(){
// summary:
// Visually denote that tree is loading data, etc.
// tags:
// private
this.state = "LOADING";
this._setExpando(true);
},
unmarkProcessing: function(){
// summary:
// Clear markup from markProcessing() call
// tags:
// private
this._setExpando(false);
},
_updateItemClasses: function(item){
// summary:
// Set appropriate CSS classes for icon and label dom node
// (used to allow for item updates to change respective CSS)
// tags:
// private
var tree = this.tree, model = tree.model;
if(tree._v10Compat && item === model.root){
// For back-compat with 1.0, need to use null to specify root item (TODO: remove in 2.0)
item = null;
}
this._applyClassAndStyle(item, "icon", "Icon");
this._applyClassAndStyle(item, "label", "Label");
this._applyClassAndStyle(item, "row", "Row");
},
_applyClassAndStyle: function(item, lower, upper){
// summary:
// Set the appropriate CSS classes and styles for labels, icons and rows.
//
// item:
// The data item.
//
// lower:
// The lower case attribute to use, e.g. 'icon', 'label' or 'row'.
//
// upper:
// The upper case attribute to use, e.g. 'Icon', 'Label' or 'Row'.
//
// tags:
// private
var clsName = "_" + lower + "Class";
var nodeName = lower + "Node";
var oldCls = this[clsName];
this[clsName] = this.tree["get" + upper + "Class"](item, this.isExpanded);
domClass.replace(this[nodeName], this[clsName] || "", oldCls || "");
domStyle.set(this[nodeName], this.tree["get" + upper + "Style"](item, this.isExpanded) || {});
},
_updateLayout: function(){
// summary:
// Set appropriate CSS classes for this.domNode
// tags:
// private
var parent = this.getParent();
if(!parent || !parent.rowNode || parent.rowNode.style.display == "none"){
/* if we are hiding the root node then make every first level child look like a root node */
domClass.add(this.domNode, "dijitTreeIsRoot");
}else{
domClass.toggle(this.domNode, "dijitTreeIsLast", !this.getNextSibling());
}
},
_setExpando: function(/*Boolean*/ processing){
// summary:
// Set the right image for the expando node
// tags:
// private
var styles = ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened",
"dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"],
_a11yStates = ["*","-","+","*"],
idx = processing ? 0 : (this.isExpandable ? (this.isExpanded ? 1 : 2) : 3);
// apply the appropriate class to the expando node
domClass.replace(this.expandoNode, styles[idx], styles);
// provide a non-image based indicator for images-off mode
this.expandoNodeText.innerHTML = _a11yStates[idx];
},
expand: function(){
// summary:
// Show my children
// returns:
// Deferred that fires when expansion is complete
// If there's already an expand in progress or we are already expanded, just return
if(this._expandDeferred){
return this._expandDeferred; // dojo.Deferred
}
// cancel in progress collapse operation
this._wipeOut && this._wipeOut.stop();
// All the state information for when a node is expanded, maybe this should be
// set when the animation completes instead
this.isExpanded = true;
this.labelNode.setAttribute("aria-expanded", "true");
if(this.tree.showRoot || this !== this.tree.rootNode){
this.containerNode.setAttribute("role", "group");
}
domClass.add(this.contentNode,'dijitTreeContentExpanded');
this._setExpando();
this._updateItemClasses(this.item);
if(this == this.tree.rootNode){
this.tree.domNode.setAttribute("aria-expanded", "true");
}
var def,
wipeIn = fxUtils.wipeIn({
node: this.containerNode, duration: manager.defaultDuration,
onEnd: function(){
def.callback(true);
}
});
// Deferred that fires when expand is complete
def = (this._expandDeferred = new Deferred(function(){
// Canceller
wipeIn.stop();
}));
wipeIn.play();
return def; // dojo.Deferred
},
collapse: function(){
// summary:
// Collapse this node (if it's expanded)
if(!this.isExpanded){ return; }
// cancel in progress expand operation
if(this._expandDeferred){
this._expandDeferred.cancel();
delete this._expandDeferred;
}
this.isExpanded = false;
this.labelNode.setAttribute("aria-expanded", "false");
if(this == this.tree.rootNode){
this.tree.domNode.setAttribute("aria-expanded", "false");
}
domClass.remove(this.contentNode,'dijitTreeContentExpanded');
this._setExpando();
this._updateItemClasses(this.item);
if(!this._wipeOut){
this._wipeOut = fxUtils.wipeOut({
node: this.containerNode, duration: manager.defaultDuration
});
}
this._wipeOut.play();
},
// indent: Integer
// Levels from this node to the root node
indent: 0,
setChildItems: function(/* Object[] */ items){
// summary:
// Sets the child items of this node, removing/adding nodes
// from current children to match specified items[] array.
// Also, if this.persist == true, expands any children that were previously
// opened.
// returns:
// Deferred object that fires after all previously opened children
// have been expanded again (or fires instantly if there are no such children).
var tree = this.tree,
model = tree.model,
defs = []; // list of deferreds that need to fire before I am complete
// Orphan all my existing children.
// If items contains some of the same items as before then we will reattach them.
// Don't call this.removeChild() because that will collapse the tree etc.
array.forEach(this.getChildren(), function(child){
_Container.prototype.removeChild.call(this, child);
}, this);
this.state = "LOADED";
if(items && items.length > 0){
this.isExpandable = true;
// Create _TreeNode widget for each specified tree node, unless one already
// exists and isn't being used (presumably it's from a DnD move and was recently
// released
array.forEach(items, function(item){
var id = model.getIdentity(item),
existingNodes = tree._itemNodesMap[id],
node;
if(existingNodes){
for(var i=0;i<existingNodes.length;i++){
if(existingNodes[i] && !existingNodes[i].getParent()){
node = existingNodes[i];
node.set('indent', this.indent+1);
break;
}
}
}
if(!node){
node = this.tree._createTreeNode({
item: item,
tree: tree,
isExpandable: model.mayHaveChildren(item),
label: tree.getLabel(item),
tooltip: tree.getTooltip(item),
dir: tree.dir,
lang: tree.lang,
textDir: tree.textDir,
indent: this.indent + 1
});
if(existingNodes){
existingNodes.push(node);
}else{
tree._itemNodesMap[id] = [node];
}
}
this.addChild(node);
// If node was previously opened then open it again now (this may trigger
// more data store accesses, recursively)
if(this.tree.autoExpand || this.tree._state(node)){
defs.push(tree._expandNode(node));
}
}, this);
// note that updateLayout() needs to be called on each child after
// _all_ the children exist
array.forEach(this.getChildren(), function(child){
child._updateLayout();
});
}else{
this.isExpandable=false;
}
if(this._setExpando){
// change expando to/from dot or + icon, as appropriate
this._setExpando(false);
}
// Set leaf icon or folder icon, as appropriate
this._updateItemClasses(this.item);
// On initial tree show, make the selected TreeNode as either the root node of the tree,
// or the first child, if the root node is hidden
if(this == tree.rootNode){
var fc = this.tree.showRoot ? this : this.getChildren()[0];
if(fc){
fc.setFocusable(true);
tree.lastFocused = fc;
}else{
// fallback: no nodes in tree so focus on Tree <div> itself
tree.domNode.setAttribute("tabIndex", "0");
}
}
return new DeferredList(defs); // dojo.Deferred
},
getTreePath: function(){
var node = this;
var path = [];
while(node && node !== this.tree.rootNode){
path.unshift(node.item);
node = node.getParent();
}
path.unshift(this.tree.rootNode.item);
return path;
},
getIdentity: function(){
return this.tree.model.getIdentity(this.item);
},
removeChild: function(/* treeNode */ node){
this.inherited(arguments);
var children = this.getChildren();
if(children.length == 0){
this.isExpandable = false;
this.collapse();
}
array.forEach(children, function(child){
child._updateLayout();
});
},
makeExpandable: function(){
// summary:
// if this node wasn't already showing the expando node,
// turn it into one and call _setExpando()
// TODO: hmm this isn't called from anywhere, maybe should remove it for 2.0
this.isExpandable = true;
this._setExpando(false);
},
_onLabelFocus: function(){
// summary:
// Called when this row is focused (possibly programatically)
// Note that we aren't using _onFocus() builtin to dijit
// because it's called when focus is moved to a descendant TreeNode.
// tags:
// private
this.tree._onNodeFocus(this);
},
setSelected: function(/*Boolean*/ selected){
// summary:
// A Tree has a (single) currently selected node.
// Mark that this node is/isn't that currently selected node.
// description:
// In particular, setting a node as selected involves setting tabIndex
// so that when user tabs to the tree, focus will go to that node (only).
this.labelNode.setAttribute("aria-selected", selected);
domClass.toggle(this.rowNode, "dijitTreeRowSelected", selected);
},
setFocusable: function(/*Boolean*/ selected){
// summary:
// A Tree has a (single) node that's focusable.
// Mark that this node is/isn't that currently focsuable node.
// description:
// In particular, setting a node as selected involves setting tabIndex
// so that when user tabs to the tree, focus will go to that node (only).
this.labelNode.setAttribute("tabIndex", selected ? "0" : "-1");
},
_onClick: function(evt){
// summary:
// Handler for onclick event on a node
// tags:
// private
this.tree._onClick(this, evt);
},
_onDblClick: function(evt){
// summary:
// Handler for ondblclick event on a node
// tags:
// private
this.tree._onDblClick(this, evt);
},
_onMouseEnter: function(evt){
// summary:
// Handler for onmouseenter event on a node
// tags:
// private
this.tree._onNodeMouseEnter(this, evt);
},
_onMouseLeave: function(evt){
// summary:
// Handler for onmouseenter event on a node
// tags:
// private
this.tree._onNodeMouseLeave(this, evt);
},
_setTextDirAttr: function(textDir){
if(textDir &&((this.textDir != textDir) || !this._created)){
this._set("textDir", textDir);
this.applyTextDir(this.labelNode, this.labelNode.innerText || this.labelNode.textContent || "");
array.forEach(this.getChildren(), function(childNode){
childNode.set("textDir", textDir);
}, this);
}
}
});
var Tree = declare("dijit.Tree", [_Widget, _TemplatedMixin], {
// summary:
// This widget displays hierarchical data from a store.
// store: [deprecated] String||dojo.data.Store
// Deprecated. Use "model" parameter instead.
// The store to get data to display in the tree.
store: null,
// model: dijit.Tree.model
// Interface to read tree data, get notifications of changes to tree data,
// and for handling drop operations (i.e drag and drop onto the tree)
model: null,
// query: [deprecated] anything
// Deprecated. User should specify query to the model directly instead.
// Specifies datastore query to return the root item or top items for the tree.
query: null,
// label: [deprecated] String
// Deprecated. Use dijit.tree.ForestStoreModel directly instead.
// Used in conjunction with query parameter.
// If a query is specified (rather than a root node id), and a label is also specified,
// then a fake root node is created and displayed, with this label.
label: "",
// showRoot: [const] Boolean
// Should the root node be displayed, or hidden?
showRoot: true,
// childrenAttr: [deprecated] String[]
// Deprecated. This information should be specified in the model.
// One ore more attributes that holds children of a tree node
childrenAttr: ["children"],
// paths: String[][] or Item[][]
// Full paths from rootNode to selected nodes expressed as array of items or array of ids.
// Since setting the paths may be asynchronous (because ofwaiting on dojo.data), set("paths", ...)
// returns a Deferred to indicate when the set is complete.
paths: [],
// path: String[] or Item[]
// Backward compatible singular variant of paths.
path: [],
// selectedItems: [readonly] Item[]
// The currently selected items in this tree.
// This property can only be set (via set('selectedItems', ...)) when that item is already
// visible in the tree. (I.e. the tree has already been expanded to show that node.)
// Should generally use `paths` attribute to set the selected items instead.
selectedItems: null,
// selectedItem: [readonly] Item
// Backward compatible singular variant of selectedItems.
selectedItem: null,
// openOnClick: Boolean
// If true, clicking a folder node's label will open it, rather than calling onClick()
openOnClick: false,
// openOnDblClick: Boolean
// If true, double-clicking a folder node's label will open it, rather than calling onDblClick()
openOnDblClick: false,
templateString: treeTemplate,
// persist: Boolean
// Enables/disables use of cookies for state saving.
persist: true,
// autoExpand: Boolean
// Fully expand the tree on load. Overrides `persist`.
autoExpand: false,
// dndController: [protected] Function|String
// Class to use as as the dnd controller. Specifying this class enables DnD.
// Generally you should specify this as dijit.tree.dndSource.
// Setting of dijit.tree._dndSelector handles selection only (no actual DnD).
dndController: _dndSelector,
// parameters to pull off of the tree and pass on to the dndController as its params
dndParams: ["onDndDrop","itemCreator","onDndCancel","checkAcceptance", "checkItemAcceptance", "dragThreshold", "betweenThreshold"],
//declare the above items so they can be pulled from the tree's markup
// onDndDrop: [protected] Function
// Parameter to dndController, see `dijit.tree.dndSource.onDndDrop`.
// Generally this doesn't need to be set.
onDndDrop: null,
/*=====
itemCreator: function(nodes, target, source){
// summary:
// Returns objects passed to `Tree.model.newItem()` based on DnD nodes
// dropped onto the tree. Developer must override this method to enable
// dropping from external sources onto this Tree, unless the Tree.model's items
// happen to look like {id: 123, name: "Apple" } with no other attributes.
// description:
// For each node in nodes[], which came from source, create a hash of name/value
// pairs to be passed to Tree.model.newItem(). Returns array of those hashes.
// nodes: DomNode[]
// The DOMNodes dragged from the source container
// target: DomNode
// The target TreeNode.rowNode
// source: dojo.dnd.Source
// The source container the nodes were dragged from, perhaps another Tree or a plain dojo.dnd.Source
// returns: Object[]
// Array of name/value hashes for each new item to be added to the Tree, like:
// | [
// | { id: 123, label: "apple", foo: "bar" },
// | { id: 456, label: "pear", zaz: "bam" }
// | ]
// tags:
// extension
return [{}];
},
=====*/
itemCreator: null,
// onDndCancel: [protected] Function
// Parameter to dndController, see `dijit.tree.dndSource.onDndCancel`.
// Generally this doesn't need to be set.
onDndCancel: null,
/*=====
checkAcceptance: function(source, nodes){
// summary:
// Checks if the Tree itself can accept nodes from this source
// source: dijit.tree._dndSource
// The source which provides items
// nodes: DOMNode[]
// Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
// source is a dijit.Tree.
// tags:
// extension
return true; // Boolean
},
=====*/
checkAcceptance: null,
/*=====
checkItemAcceptance: function(target, source, position){
// summary:
// Stub function to be overridden if one wants to check for the ability to drop at the node/item level
// description:
// In the base case, this is called to check if target can become a child of source.
// When betweenThreshold is set, position="before" or "after" means that we
// are asking if the source node can be dropped before/after the target node.
// target: DOMNode
// The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
// Use dijit.getEnclosingWidget(target) to get the TreeNode.
// source: dijit.tree.dndSource
// The (set of) nodes we are dropping
// position: String
// "over", "before", or "after"
// tags:
// extension
return true; // Boolean
},
=====*/
checkItemAcceptance: null,
// dragThreshold: Integer
// Number of pixels mouse moves before it's considered the start of a drag operation
dragThreshold: 5,
// betweenThreshold: Integer
// Set to a positive value to allow drag and drop "between" nodes.
//
// If during DnD mouse is over a (target) node but less than betweenThreshold
// pixels from the bottom edge, dropping the the dragged node will make it
// the next sibling of the target node, rather than the child.
//
// Similarly, if mouse is over a target node but less that betweenThreshold
// pixels from the top edge, dropping the dragged node will make it
// the target node's previous sibling rather than the target node's child.
betweenThreshold: 0,
// _nodePixelIndent: Integer
// Number of pixels to indent tree nodes (relative to parent node).
// Default is 19 but can be overridden by setting CSS class dijitTreeIndent
// and calling resize() or startup() on tree after it's in the DOM.
_nodePixelIndent: 19,
_publish: function(/*String*/ topicName, /*Object*/ message){
// summary:
// Publish a message for this widget/topic
topic.publish(this.id, lang.mixin({tree: this, event: topicName}, message || {})); // publish
},
postMixInProperties: function(){
this.tree = this;
if(this.autoExpand){
// There's little point in saving opened/closed state of nodes for a Tree
// that initially opens all it's nodes.
this.persist = false;
}
this._itemNodesMap={};
if(!this.cookieName && this.id){
this.cookieName = this.id + "SaveStateCookie";
}
this._loadDeferred = new Deferred();
this.inherited(arguments);
},
postCreate: function(){
this._initState();
// Create glue between store and Tree, if not specified directly by user
if(!this.model){
this._store2model();
}
// monitor changes to items
this.connect(this.model, "onChange", "_onItemChange");
this.connect(this.model, "onChildrenChange", "_onItemChildrenChange");
this.connect(this.model, "onDelete", "_onItemDelete");
this._load();
this.inherited(arguments);
if(this.dndController){
if(lang.isString(this.dndController)){
this.dndController = lang.getObject(this.dndController);
}
var params={};
for(var i=0; i<this.dndParams.length;i++){
if(this[this.dndParams[i]]){
params[this.dndParams[i]] = this[this.dndParams[i]];
}
}
this.dndController = new this.dndController(this, params);
}
},
_store2model: function(){
// summary:
// User specified a store&query rather than model, so create model from store/query
this._v10Compat = true;
kernel.deprecated("Tree: from version 2.0, should specify a model object rather than a store/query");
var modelParams = {
id: this.id + "_ForestStoreModel",
store: this.store,
query: this.query,
childrenAttrs: this.childrenAttr
};
// Only override the model's mayHaveChildren() method if the user has specified an override
if(this.params.mayHaveChildren){
modelParams.mayHaveChildren = lang.hitch(this, "mayHaveChildren");
}
if(this.params.getItemChildren){
modelParams.getChildren = lang.hitch(this, function(item, onComplete, onError){
this.getItemChildren((this._v10Compat && item === this.model.root) ? null : item, onComplete, onError);
});
}
this.model = new ForestStoreModel(modelParams);
// For backwards compatibility, the visibility of the root node is controlled by
// whether or not the user has specified a label
this.showRoot = Boolean(this.label);
},
onLoad: function(){
// summary:
// Called when tree finishes loading and expanding.
// description:
// If persist == true the loading may encompass many levels of fetches
// from the data store, each asynchronous. Waits for all to finish.
// tags:
// callback
},
_load: function(){
// summary:
// Initial load of the tree.
// Load root node (possibly hidden) and it's children.
this.model.getRoot(
lang.hitch(this, function(item){
var rn = (this.rootNode = this.tree._createTreeNode({
item: item,
tree: this,
isExpandable: true,
label: this.label || this.getLabel(item),
textDir: this.textDir,
indent: this.showRoot ? 0 : -1
}));
if(!this.showRoot){
rn.rowNode.style.display="none";
// if root is not visible, move tree role to the invisible
// root node's containerNode, see #12135
this.domNode.setAttribute("role", "presentation");
rn.labelNode.setAttribute("role", "presentation");
rn.containerNode.setAttribute("role", "tree");
}
this.domNode.appendChild(rn.domNode);
var identity = this.model.getIdentity(item);
if(this._itemNodesMap[identity]){
this._itemNodesMap[identity].push(rn);
}else{
this._itemNodesMap[identity] = [rn];
}
rn._updateLayout(); // sets "dijitTreeIsRoot" CSS classname
// load top level children and then fire onLoad() event
this._expandNode(rn).addCallback(lang.hitch(this, function(){
this._loadDeferred.callback(true);
this.onLoad();
}));
}),
function(err){
console.error(this, ": error loading root: ", err);
}
);
},
getNodesByItem: function(/*Item or id*/ item){
// summary:
// Returns all tree nodes that refer to an item
// returns:
// Array of tree nodes that refer to passed item
if(!item){ return []; }
var identity = lang.isString(item) ? item : this.model.getIdentity(item);
// return a copy so widget don't get messed up by changes to returned array
return [].concat(this._itemNodesMap[identity]);
},
_setSelectedItemAttr: function(/*Item or id*/ item){
this.set('selectedItems', [item]);
},
_setSelectedItemsAttr: function(/*Items or ids*/ items){
// summary:
// Select tree nodes related to passed items.
// WARNING: if model use multi-parented items or desired tree node isn't already loaded
// behavior is undefined. Use set('paths', ...) instead.
var tree = this;
this._loadDeferred.addCallback( lang.hitch(this, function(){
var identities = array.map(items, function(item){
return (!item || lang.isString(item)) ? item : tree.model.getIdentity(item);
});
var nodes = [];
array.forEach(identities, function(id){
nodes = nodes.concat(tree._itemNodesMap[id] || []);
});
this.set('selectedNodes', nodes);
}));
},
_setPathAttr: function(/*Item[] || String[]*/ path){
// summary:
// Singular variant of _setPathsAttr
if(path.length){
return this.set("paths", [path]);
}else{
// Empty list is interpreted as "select nothing"
return this.set("paths", []);
}
},
_setPathsAttr: function(/*Item[][] || String[][]*/ paths){
// summary:
// Select the tree nodes identified by passed paths.
// paths:
// Array of arrays of items or item id's
// returns:
// Deferred to indicate when the set is complete
var tree = this;
// We may need to wait for some nodes to expand, so setting
// each path will involve a Deferred. We bring those deferreds
// together witha DeferredList.
return new DeferredList(array.map(paths, function(path){
var d = new Deferred();
// normalize path to use identity
path = array.map(path, function(item){
return lang.isString(item) ? item : tree.model.getIdentity(item);
});
if(path.length){
// Wait for the tree to load, if it hasn't already.
tree._loadDeferred.addCallback(function(){ selectPath(path, [tree.rootNode], d); });
}else{
d.errback("Empty path");
}
return d;
})).addCallback(setNodes);
function selectPath(path, nodes, def){
// Traverse path; the next path component should be among "nodes".
var nextPath = path.shift();
var nextNode = array.filter(nodes, function(node){
return node.getIdentity() == nextPath;
})[0];
if(!!nextNode){
if(path.length){
tree._expandNode(nextNode).addCallback(function(){ selectPath(path, nextNode.getChildren(), def); });
}else{
//Successfully reached the end of this path
def.callback(nextNode);
}
}else{
def.errback("Could not expand path at " + nextPath);
}
}
function setNodes(newNodes){
//After all expansion is finished, set the selection to
//the set of nodes successfully found.
tree.set("selectedNodes", array.map(
array.filter(newNodes,function(x){return x[0];}),
function(x){return x[1];}));
}
},
_setSelectedNodeAttr: function(node){
this.set('selectedNodes', [node]);
},
_setSelectedNodesAttr: function(nodes){
this._loadDeferred.addCallback( lang.hitch(this, function(){
this.dndController.setSelection(nodes);
}));
},
////////////// Data store related functions //////////////////////
// These just get passed to the model; they are here for back-compat
mayHaveChildren: function(/*dojo.data.Item*/ /*===== item =====*/){
// summary:
// Deprecated. This should be specified on the model itself.
//
// Overridable function to tell if an item has or may have children.
// Controls whether or not +/- expando icon is shown.
// (For efficiency reasons we may not want to check if an element actually
// has children until user clicks the expando node)
// tags:
// deprecated
},
getItemChildren: function(/*===== parentItem, onComplete =====*/){
// summary:
// Deprecated. This should be specified on the model itself.
//
// Overridable function that return array of child items of given parent item,
// or if parentItem==null then return top items in tree
// tags:
// deprecated
},
///////////////////////////////////////////////////////
// Functions for converting an item to a TreeNode
getLabel: function(/*dojo.data.Item*/ item){
// summary:
// Overridable function to get the label for a tree node (given the item)
// tags:
// extension
return this.model.getLabel(item); // String
},
getIconClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
// summary:
// Overridable function to return CSS class name to display icon
// tags:
// extension
return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "dijitLeaf"
},
getLabelClass: function(/*===== item, opened =====*/){
// summary:
// Overridable function to return CSS class name to display label
// item: dojo.data.Item
// opened: Boolean
// returns: String
// CSS class name
// tags:
// extension
},
getRowClass: function(/*===== item, opened =====*/){
// summary:
// Overridable function to return CSS class name to display row
// item: dojo.data.Item
// opened: Boolean
// returns: String
// CSS class name
// tags:
// extension
},
getIconStyle: function(/*===== item, opened =====*/){
// summary:
// Overridable function to return CSS styles to display icon
// item: dojo.data.Item
// opened: Boolean
// returns: Object
// Object suitable for input to dojo.style() like {backgroundImage: "url(...)"}
// tags:
// extension
},
getLabelStyle: function(/*===== item, opened =====*/){
// summary:
// Overridable function to return CSS styles to display label
// item: dojo.data.Item
// opened: Boolean
// returns:
// Object suitable for input to dojo.style() like {color: "red", background: "green"}
// tags:
// extension
},
getRowStyle: function(/*===== item, opened =====*/){
// summary:
// Overridable function to return CSS styles to display row
// item: dojo.data.Item
// opened: Boolean
// returns:
// Object suitable for input to dojo.style() like {background-color: "#bbb"}
// tags:
// extension
},
getTooltip: function(/*dojo.data.Item*/ /*===== item =====*/){
// summary:
// Overridable function to get the tooltip for a tree node (given the item)
// tags:
// extension
return ""; // String
},
/////////// Keyboard and Mouse handlers ////////////////////
_onKeyPress: function(/*Event*/ e){
// summary:
// Translates keypress events into commands for the controller
if(e.altKey){ return; }
var treeNode = registry.getEnclosingWidget(e.target);
if(!treeNode){ return; }
var key = e.charOrCode;
if(typeof key == "string" && key != " "){ // handle printables (letter navigation)
// Check for key navigation.
if(!e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey){
this._onLetterKeyNav( { node: treeNode, key: key.toLowerCase() } );
event.stop(e);
}
}else{ // handle non-printables (arrow keys)
// clear record of recent printables (being saved for multi-char letter navigation),
// because "a", down-arrow, "b" shouldn't search for "ab"
if(this._curSearch){
clearTimeout(this._curSearch.timer);
delete this._curSearch;
}
var map = this._keyHandlerMap;
if(!map){
// setup table mapping keys to events
map = {};
map[keys.ENTER]="_onEnterKey";
//On WebKit based browsers, the combination ctrl-enter
//does not get passed through. To allow accessible
//multi-select on those browsers, the space key is
//also used for selection.
map[keys.SPACE]= map[" "] = "_onEnterKey";
map[this.isLeftToRight() ? keys.LEFT_ARROW : keys.RIGHT_ARROW]="_onLeftArrow";
map[this.isLeftToRight() ? keys.RIGHT_ARROW : keys.LEFT_ARROW]="_onRightArrow";
map[keys.UP_ARROW]="_onUpArrow";
map[keys.DOWN_ARROW]="_onDownArrow";
map[keys.HOME]="_onHomeKey";
map[keys.END]="_onEndKey";
this._keyHandlerMap = map;
}
if(this._keyHandlerMap[key]){
this[this._keyHandlerMap[key]]( { node: treeNode, item: treeNode.item, evt: e } );
event.stop(e);
}
}
},
_onEnterKey: function(/*Object*/ message){
this._publish("execute", { item: message.item, node: message.node } );
this.dndController.userSelect(message.node, connect.isCopyKey( message.evt ), message.evt.shiftKey);
this.onClick(message.item, message.node, message.evt);
},
_onDownArrow: function(/*Object*/ message){
// summary:
// down arrow pressed; get next visible node, set focus there
var node = this._getNextNode(message.node);
if(node && node.isTreeNode){
this.focusNode(node);
}
},
_onUpArrow: function(/*Object*/ message){
// summary:
// Up arrow pressed; move to previous visible node
var node = message.node;
// if younger siblings
var previousSibling = node.getPreviousSibling();
if(previousSibling){
node = previousSibling;
// if the previous node is expanded, dive in deep
while(node.isExpandable && node.isExpanded && node.hasChildren()){
// move to the last child
var children = node.getChildren();
node = children[children.length-1];
}
}else{
// if this is the first child, return the parent
// unless the parent is the root of a tree with a hidden root
var parent = node.getParent();
if(!(!this.showRoot && parent === this.rootNode)){
node = parent;
}
}
if(node && node.isTreeNode){
this.focusNode(node);
}
},
_onRightArrow: function(/*Object*/ message){
// summary:
// Right arrow pressed; go to child node
var node = message.node;
// if not expanded, expand, else move to 1st child
if(node.isExpandable && !node.isExpanded){
this._expandNode(node);
}else if(node.hasChildren()){
node = node.getChildren()[0];
if(node && node.isTreeNode){
this.focusNode(node);
}
}
},
_onLeftArrow: function(/*Object*/ message){
// summary:
// Left arrow pressed.
// If not collapsed, collapse, else move to parent.
var node = message.node;
if(node.isExpandable && node.isExpanded){
this._collapseNode(node);
}else{
var parent = node.getParent();
if(parent && parent.isTreeNode && !(!this.showRoot && parent === this.rootNode)){
this.focusNode(parent);
}
}
},
_onHomeKey: function(){
// summary:
// Home key pressed; get first visible node, and set focus there
var node = this._getRootOrFirstNode();
if(node){
this.focusNode(node);
}
},
_onEndKey: function(){
// summary:
// End key pressed; go to last visible node.
var node = this.rootNode;
while(node.isExpanded){
var c = node.getChildren();
node = c[c.length - 1];
}
if(node && node.isTreeNode){
this.focusNode(node);
}
},
// multiCharSearchDuration: Number
// If multiple characters are typed where each keystroke happens within
// multiCharSearchDuration of the previous keystroke,
// search for nodes matching all the keystrokes.
//
// For example, typing "ab" will search for entries starting with
// "ab" unless the delay between "a" and "b" is greater than multiCharSearchDuration.
multiCharSearchDuration: 250,
_onLetterKeyNav: function(message){
// summary:
// Called when user presses a prinatable key; search for node starting with recently typed letters.
// message: Object
// Like { node: TreeNode, key: 'a' } where key is the key the user pressed.
// Branch depending on whether this key starts a new search, or modifies an existing search
var cs = this._curSearch;
if(cs){
// We are continuing a search. Ex: user has pressed 'a', and now has pressed
// 'b', so we want to search for nodes starting w/"ab".
cs.pattern = cs.pattern + message.key;
clearTimeout(cs.timer);
}else{
// We are starting a new search
cs = this._curSearch = {
pattern: message.key,
startNode: message.node
};
}
// set/reset timer to forget recent keystrokes
var self = this;
cs.timer = setTimeout(function(){
delete self._curSearch;
}, this.multiCharSearchDuration);
// Navigate to TreeNode matching keystrokes [entered so far].
var node = cs.startNode;
do{
node = this._getNextNode(node);
//check for last node, jump to first node if necessary
if(!node){
node = this._getRootOrFirstNode();
}
}while(node !== cs.startNode && (node.label.toLowerCase().substr(0, cs.pattern.length) != cs.pattern));
if(node && node.isTreeNode){
// no need to set focus if back where we started
if(node !== cs.startNode){
this.focusNode(node);
}
}
},
isExpandoNode: function(node, widget){
// summary:
// check whether a dom node is the expandoNode for a particular TreeNode widget
return dom.isDescendant(node, widget.expandoNode);
},
_onClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
// summary:
// Translates click events into commands for the controller to process
var domElement = e.target,
isExpandoClick = this.isExpandoNode(domElement, nodeWidget);
if( (this.openOnClick && nodeWidget.isExpandable) || isExpandoClick ){
// expando node was clicked, or label of a folder node was clicked; open it
if(nodeWidget.isExpandable){
this._onExpandoClick({node:nodeWidget});
}
}else{
this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
this.onClick(nodeWidget.item, nodeWidget, e);
this.focusNode(nodeWidget);
}
event.stop(e);
},
_onDblClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
// summary:
// Translates double-click events into commands for the controller to process
var domElement = e.target,
isExpandoClick = (domElement == nodeWidget.expandoNode || domElement == nodeWidget.expandoNodeText);
if( (this.openOnDblClick && nodeWidget.isExpandable) ||isExpandoClick ){
// expando node was clicked, or label of a folder node was clicked; open it
if(nodeWidget.isExpandable){
this._onExpandoClick({node:nodeWidget});
}
}else{
this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
this.onDblClick(nodeWidget.item, nodeWidget, e);
this.focusNode(nodeWidget);
}
event.stop(e);
},
_onExpandoClick: function(/*Object*/ message){
// summary:
// User clicked the +/- icon; expand or collapse my children.
var node = message.node;
// If we are collapsing, we might be hiding the currently focused node.
// Also, clicking the expando node might have erased focus from the current node.
// For simplicity's sake just focus on the node with the expando.
this.focusNode(node);
if(node.isExpanded){
this._collapseNode(node);
}else{
this._expandNode(node);
}
},
onClick: function(/*===== item, node, evt =====*/){
// summary:
// Callback when a tree node is clicked
// item: dojo.data.Item
// node: TreeNode
// evt: Event
// tags:
// callback
},
onDblClick: function(/*===== item, node, evt =====*/){
// summary:
// Callback when a tree node is double-clicked
// item: dojo.data.Item
// node: TreeNode
// evt: Event
// tags:
// callback
},
onOpen: function(/*===== item, node =====*/){
// summary:
// Callback when a node is opened
// item: dojo.data.Item
// node: TreeNode
// tags:
// callback
},
onClose: function(/*===== item, node =====*/){
// summary:
// Callback when a node is closed
// item: dojo.data.Item
// node: TreeNode
// tags:
// callback
},
_getNextNode: function(node){
// summary:
// Get next visible node
if(node.isExpandable && node.isExpanded && node.hasChildren()){
// if this is an expanded node, get the first child
return node.getChildren()[0]; // _TreeNode
}else{
// find a parent node with a sibling
while(node && node.isTreeNode){
var returnNode = node.getNextSibling();
if(returnNode){
return returnNode; // _TreeNode
}
node = node.getParent();
}
return null;
}
},
_getRootOrFirstNode: function(){
// summary:
// Get first visible node
return this.showRoot ? this.rootNode : this.rootNode.getChildren()[0];
},
_collapseNode: function(/*_TreeNode*/ node){
// summary:
// Called when the user has requested to collapse the node
if(node._expandNodeDeferred){
delete node._expandNodeDeferred;
}
if(node.isExpandable){
if(node.state == "LOADING"){
// ignore clicks while we are in the process of loading data
return;
}
node.collapse();
this.onClose(node.item, node);
this._state(node, false);
}
},
_expandNode: function(/*_TreeNode*/ node, /*Boolean?*/ recursive){
// summary:
// Called when the user has requested to expand the node
// recursive:
// Internal flag used when _expandNode() calls itself, don't set.
// returns:
// Deferred that fires when the node is loaded and opened and (if persist=true) all it's descendants
// that were previously opened too
if(node._expandNodeDeferred && !recursive){
// there's already an expand in progress (or completed), so just return
return node._expandNodeDeferred; // dojo.Deferred
}
var model = this.model,
item = node.item,
_this = this;
switch(node.state){
case "UNCHECKED":
// need to load all the children, and then expand
node.markProcessing();
// Setup deferred to signal when the load and expand are finished.
// Save that deferred in this._expandDeferred as a flag that operation is in progress.
var def = (node._expandNodeDeferred = new Deferred());
// Get the children
model.getChildren(
item,
function(items){
node.unmarkProcessing();
// Display the children and also start expanding any children that were previously expanded
// (if this.persist == true). The returned Deferred will fire when those expansions finish.
var scid = node.setChildItems(items);
// Call _expandNode() again but this time it will just to do the animation (default branch).
// The returned Deferred will fire when the animation completes.
// TODO: seems like I can avoid recursion and just use a deferred to sequence the events?
var ed = _this._expandNode(node, true);
// After the above two tasks (setChildItems() and recursive _expandNode()) finish,
// signal that I am done.
scid.addCallback(function(){
ed.addCallback(function(){
def.callback();
})
});
},
function(err){
console.error(_this, ": error loading root children: ", err);
}
);
break;
default: // "LOADED"
// data is already loaded; just expand node
def = (node._expandNodeDeferred = node.expand());
this.onOpen(node.item, node);
this._state(node, true);
}
return def; // dojo.Deferred
},
////////////////// Miscellaneous functions ////////////////
focusNode: function(/* _tree.Node */ node){
// summary:
// Focus on the specified node (which must be visible)
// tags:
// protected
// set focus so that the label will be voiced using screen readers
focus.focus(node.labelNode);
},
_onNodeFocus: function(/*dijit._Widget*/ node){
// summary:
// Called when a TreeNode gets focus, either by user clicking
// it, or programatically by arrow key handling code.
// description:
// It marks that the current node is the selected one, and the previously
// selected node no longer is.
if(node && node != this.lastFocused){
if(this.lastFocused && !this.lastFocused._destroyed){
// mark that the previously focsable node is no longer focusable
this.lastFocused.setFocusable(false);
}
// mark that the new node is the currently selected one
node.setFocusable(true);
this.lastFocused = node;
}
},
_onNodeMouseEnter: function(/*dijit._Widget*/ /*===== node =====*/){
// summary:
// Called when mouse is over a node (onmouseenter event),
// this is monitored by the DND code
},
_onNodeMouseLeave: function(/*dijit._Widget*/ /*===== node =====*/){
// summary:
// Called when mouse leaves a node (onmouseleave event),
// this is monitored by the DND code
},
//////////////// Events from the model //////////////////////////
_onItemChange: function(/*Item*/ item){
// summary:
// Processes notification of a change to an item's scalar values like label
var model = this.model,
identity = model.getIdentity(item),
nodes = this._itemNodesMap[identity];
if(nodes){
var label = this.getLabel(item),
tooltip = this.getTooltip(item);
array.forEach(nodes, function(node){
node.set({
item: item, // theoretically could be new JS Object representing same item
label: label,
tooltip: tooltip
});
node._updateItemClasses(item);
});
}
},
_onItemChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
// summary:
// Processes notification of a change to an item's children
var model = this.model,
identity = model.getIdentity(parent),
parentNodes = this._itemNodesMap[identity];
if(parentNodes){
array.forEach(parentNodes,function(parentNode){
parentNode.setChildItems(newChildrenList);
});
}
},
_onItemDelete: function(/*Item*/ item){
// summary:
// Processes notification of a deletion of an item
var model = this.model,
identity = model.getIdentity(item),
nodes = this._itemNodesMap[identity];
if(nodes){
array.forEach(nodes,function(node){
// Remove node from set of selected nodes (if it's selected)
this.dndController.removeTreeNode(node);
var parent = node.getParent();
if(parent){
// if node has not already been orphaned from a _onSetItem(parent, "children", ..) call...
parent.removeChild(node);
}
node.destroyRecursive();
}, this);
delete this._itemNodesMap[identity];
}
},
/////////////// Miscellaneous funcs
_initState: function(){
// summary:
// Load in which nodes should be opened automatically
this._openedNodes = {};
if(this.persist && this.cookieName){
var oreo = cookie(this.cookieName);
if(oreo){
array.forEach(oreo.split(','), function(item){
this._openedNodes[item] = true;
}, this);
}
}
},
_state: function(node, expanded){
// summary:
// Query or set expanded state for an node
if(!this.persist){
return false;
}
var path = array.map(node.getTreePath(), function(item){
return this.model.getIdentity(item);
}, this).join("/");
if(arguments.length === 1){
return this._openedNodes[path];
}else{
if(expanded){
this._openedNodes[path] = true;
}else{
delete this._openedNodes[path];
}
var ary = [];
for(var id in this._openedNodes){
ary.push(id);
}
cookie(this.cookieName, ary.join(","), {expires:365});
}
},
destroy: function(){
if(this._curSearch){
clearTimeout(this._curSearch.timer);
delete this._curSearch;
}
if(this.rootNode){
this.rootNode.destroyRecursive();
}
if(this.dndController && !lang.isString(this.dndController)){
this.dndController.destroy();
}
this.rootNode = null;
this.inherited(arguments);
},
destroyRecursive: function(){
// A tree is treated as a leaf, not as a node with children (like a grid),
// but defining destroyRecursive for back-compat.
this.destroy();
},
resize: function(changeSize){
if(changeSize){
domGeometry.setMarginBox(this.domNode, changeSize);
}
// The only JS sizing involved w/tree is the indentation, which is specified
// in CSS and read in through this dummy indentDetector node (tree must be
// visible and attached to the DOM to read this)
this._nodePixelIndent = domGeometry.position(this.tree.indentDetector).w;
if(this.tree.rootNode){
// If tree has already loaded, then reset indent for all the nodes
this.tree.rootNode.set('indent', this.showRoot ? 0 : -1);
}
},
_createTreeNode: function(/*Object*/ args){
// summary:
// creates a TreeNode
// description:
// Developers can override this method to define their own TreeNode class;
// However it will probably be removed in a future release in favor of a way
// of just specifying a widget for the label, rather than one that contains
// the children too.
return new TreeNode(args);
},
_setTextDirAttr: function(textDir){
if(textDir && this.textDir!= textDir){
this._set("textDir",textDir);
this.rootNode.set("textDir", textDir);
}
}
});
Tree._TreeNode = TreeNode; // for monkey patching
return Tree;
});
},
'dijit/form/HorizontalSlider':function(){
define([
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dnd/move",
"dojo/_base/event", // event.stop
"dojo/_base/fx", // fx.animateProperty
"dojo/dom-geometry", // domGeometry.position
"dojo/dom-style", // domStyle.getComputedStyle
"dojo/keys", // keys.DOWN_ARROW keys.END keys.HOME keys.LEFT_ARROW keys.PAGE_DOWN keys.PAGE_UP keys.RIGHT_ARROW keys.UP_ARROW
"dojo/_base/lang", // lang.hitch
"dojo/_base/sniff", // has("ie") has("mozilla")
"dojo/dnd/Moveable", // Moveable
"dojo/dnd/Mover", // Mover Mover.prototype.destroy.apply
"dojo/query", // query
"../registry", // registry.findWidgets
"../focus", // focus.focus()
"../typematic",
"./Button",
"./_FormValueWidget",
"../_Container",
"dojo/text!./templates/HorizontalSlider.html"
], function(array, declare, move, event, fx, domGeometry, domStyle, keys, lang, has, Moveable, Mover, query,
registry, focus, typematic, Button, _FormValueWidget, _Container, template){
/*=====
var Button = dijit.form.Button;
var _FormValueWidget = dijit.form._FormValueWidget;
var _Container = dijit._Container;
=====*/
// module:
// dijit/form/HorizontalSlider
// summary:
// A form widget that allows one to select a value with a horizontally draggable handle
var _SliderMover = declare("dijit.form._SliderMover", Mover, {
onMouseMove: function(e){
var widget = this.widget;
var abspos = widget._abspos;
if(!abspos){
abspos = widget._abspos = domGeometry.position(widget.sliderBarContainer, true);
widget._setPixelValue_ = lang.hitch(widget, "_setPixelValue");
widget._isReversed_ = widget._isReversed();
}
var pixelValue = e[widget._mousePixelCoord] - abspos[widget._startingPixelCoord];
widget._setPixelValue_(widget._isReversed_ ? (abspos[widget._pixelCount]-pixelValue) : pixelValue, abspos[widget._pixelCount], false);
},
destroy: function(e){
Mover.prototype.destroy.apply(this, arguments);
var widget = this.widget;
widget._abspos = null;
widget._setValueAttr(widget.value, true);
}
});
var HorizontalSlider = declare("dijit.form.HorizontalSlider", [_FormValueWidget, _Container], {
// summary:
// A form widget that allows one to select a value with a horizontally draggable handle
templateString: template,
// Overrides FormValueWidget.value to indicate numeric value
value: 0,
// showButtons: [const] Boolean
// Show increment/decrement buttons at the ends of the slider?
showButtons: true,
// minimum:: [const] Integer
// The minimum value the slider can be set to.
minimum: 0,
// maximum: [const] Integer
// The maximum value the slider can be set to.
maximum: 100,
// discreteValues: Integer
// If specified, indicates that the slider handle has only 'discreteValues' possible positions,
// and that after dragging the handle, it will snap to the nearest possible position.
// Thus, the slider has only 'discreteValues' possible values.
//
// For example, if minimum=10, maxiumum=30, and discreteValues=3, then the slider handle has
// three possible positions, representing values 10, 20, or 30.
//
// If discreteValues is not specified or if it's value is higher than the number of pixels
// in the slider bar, then the slider handle can be moved freely, and the slider's value will be
// computed/reported based on pixel position (in this case it will likely be fractional,
// such as 123.456789).
discreteValues: Infinity,
// pageIncrement: Integer
// If discreteValues is also specified, this indicates the amount of clicks (ie, snap positions)
// that the slider handle is moved via pageup/pagedown keys.
// If discreteValues is not specified, it indicates the number of pixels.
pageIncrement: 2,
// clickSelect: Boolean
// If clicking the slider bar changes the value or not
clickSelect: true,
// slideDuration: Number
// The time in ms to take to animate the slider handle from 0% to 100%,
// when clicking the slider bar to make the handle move.
slideDuration: registry.defaultDuration,
// Map widget attributes to DOMNode attributes.
_setIdAttr: "", // Override _FormWidget which sends id to focusNode
baseClass: "dijitSlider",
// Apply CSS classes to up/down arrows and handle per mouse state
cssStateNodes: {
incrementButton: "dijitSliderIncrementButton",
decrementButton: "dijitSliderDecrementButton",
focusNode: "dijitSliderThumb"
},
_mousePixelCoord: "pageX",
_pixelCount: "w",
_startingPixelCoord: "x",
_handleOffsetCoord: "left",
_progressPixelSize: "width",
_onKeyUp: function(/*Event*/ e){
if(this.disabled || this.readOnly || e.altKey || e.ctrlKey || e.metaKey){ return; }
this._setValueAttr(this.value, true);
},
_onKeyPress: function(/*Event*/ e){
if(this.disabled || this.readOnly || e.altKey || e.ctrlKey || e.metaKey){ return; }
switch(e.charOrCode){
case keys.HOME:
this._setValueAttr(this.minimum, false);
break;
case keys.END:
this._setValueAttr(this.maximum, false);
break;
// this._descending === false: if ascending vertical (min on top)
// (this._descending || this.isLeftToRight()): if left-to-right horizontal or descending vertical
case ((this._descending || this.isLeftToRight()) ? keys.RIGHT_ARROW : keys.LEFT_ARROW):
case (this._descending === false ? keys.DOWN_ARROW : keys.UP_ARROW):
case (this._descending === false ? keys.PAGE_DOWN : keys.PAGE_UP):
this.increment(e);
break;
case ((this._descending || this.isLeftToRight()) ? keys.LEFT_ARROW : keys.RIGHT_ARROW):
case (this._descending === false ? keys.UP_ARROW : keys.DOWN_ARROW):
case (this._descending === false ? keys.PAGE_UP : keys.PAGE_DOWN):
this.decrement(e);
break;
default:
return;
}
event.stop(e);
},
_onHandleClick: function(e){
if(this.disabled || this.readOnly){ return; }
if(!has("ie")){
// make sure you get focus when dragging the handle
// (but don't do on IE because it causes a flicker on mouse up (due to blur then focus)
focus.focus(this.sliderHandle);
}
event.stop(e);
},
_isReversed: function(){
// summary:
// Returns true if direction is from right to left
// tags:
// protected extension
return !this.isLeftToRight();
},
_onBarClick: function(e){
if(this.disabled || this.readOnly || !this.clickSelect){ return; }
focus.focus(this.sliderHandle);
event.stop(e);
var abspos = domGeometry.position(this.sliderBarContainer, true);
var pixelValue = e[this._mousePixelCoord] - abspos[this._startingPixelCoord];
this._setPixelValue(this._isReversed() ? (abspos[this._pixelCount] - pixelValue) : pixelValue, abspos[this._pixelCount], true);
this._movable.onMouseDown(e);
},
_setPixelValue: function(/*Number*/ pixelValue, /*Number*/ maxPixels, /*Boolean?*/ priorityChange){
if(this.disabled || this.readOnly){ return; }
var count = this.discreteValues;
if(count <= 1 || count == Infinity){ count = maxPixels; }
count--;
var pixelsPerValue = maxPixels / count;
var wholeIncrements = Math.round(pixelValue / pixelsPerValue);
this._setValueAttr(Math.max(Math.min((this.maximum-this.minimum)*wholeIncrements/count + this.minimum, this.maximum), this.minimum), priorityChange);
},
_setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange){
// summary:
// Hook so set('value', value) works.
this._set("value", value);
this.valueNode.value = value;
this.focusNode.setAttribute("aria-valuenow", value);
this.inherited(arguments);
var percent = (value - this.minimum) / (this.maximum - this.minimum);
var progressBar = (this._descending === false) ? this.remainingBar : this.progressBar;
var remainingBar = (this._descending === false) ? this.progressBar : this.remainingBar;
if(this._inProgressAnim && this._inProgressAnim.status != "stopped"){
this._inProgressAnim.stop(true);
}
if(priorityChange && this.slideDuration > 0 && progressBar.style[this._progressPixelSize]){
// animate the slider
var _this = this;
var props = {};
var start = parseFloat(progressBar.style[this._progressPixelSize]);
var duration = this.slideDuration * (percent-start/100);
if(duration == 0){ return; }
if(duration < 0){ duration = 0 - duration; }
props[this._progressPixelSize] = { start: start, end: percent*100, units:"%" };
this._inProgressAnim = fx.animateProperty({ node: progressBar, duration: duration,
onAnimate: function(v){
remainingBar.style[_this._progressPixelSize] = (100 - parseFloat(v[_this._progressPixelSize])) + "%";
},
onEnd: function(){
delete _this._inProgressAnim;
},
properties: props
});
this._inProgressAnim.play();
}else{
progressBar.style[this._progressPixelSize] = (percent*100) + "%";
remainingBar.style[this._progressPixelSize] = ((1-percent)*100) + "%";
}
},
_bumpValue: function(signedChange, /*Boolean?*/ priorityChange){
if(this.disabled || this.readOnly){ return; }
var s = domStyle.getComputedStyle(this.sliderBarContainer);
var c = domGeometry.getContentBox(this.sliderBarContainer, s);
var count = this.discreteValues;
if(count <= 1 || count == Infinity){ count = c[this._pixelCount]; }
count--;
var value = (this.value - this.minimum) * count / (this.maximum - this.minimum) + signedChange;
if(value < 0){ value = 0; }
if(value > count){ value = count; }
value = value * (this.maximum - this.minimum) / count + this.minimum;
this._setValueAttr(value, priorityChange);
},
_onClkBumper: function(val){
if(this.disabled || this.readOnly || !this.clickSelect){ return; }
this._setValueAttr(val, true);
},
_onClkIncBumper: function(){
this._onClkBumper(this._descending === false ? this.minimum : this.maximum);
},
_onClkDecBumper: function(){
this._onClkBumper(this._descending === false ? this.maximum : this.minimum);
},
decrement: function(/*Event*/ e){
// summary:
// Decrement slider
// tags:
// private
this._bumpValue(e.charOrCode == keys.PAGE_DOWN ? -this.pageIncrement : -1);
},
increment: function(/*Event*/ e){
// summary:
// Increment slider
// tags:
// private
this._bumpValue(e.charOrCode == keys.PAGE_UP ? this.pageIncrement : 1);
},
_mouseWheeled: function(/*Event*/ evt){
// summary:
// Event handler for mousewheel where supported
event.stop(evt);
var janky = !has("mozilla");
var scroll = evt[(janky ? "wheelDelta" : "detail")] * (janky ? 1 : -1);
this._bumpValue(scroll < 0 ? -1 : 1, true); // negative scroll acts like a decrement
},
startup: function(){
if(this._started){ return; }
array.forEach(this.getChildren(), function(child){
if(this[child.container] != this.containerNode){
this[child.container].appendChild(child.domNode);
}
}, this);
this.inherited(arguments);
},
_typematicCallback: function(/*Number*/ count, /*Object*/ button, /*Event*/ e){
if(count == -1){
this._setValueAttr(this.value, true);
}else{
this[(button == (this._descending? this.incrementButton : this.decrementButton)) ? "decrement" : "increment"](e);
}
},
buildRendering: function(){
this.inherited(arguments);
if(this.showButtons){
this.incrementButton.style.display="";
this.decrementButton.style.display="";
}
// find any associated label element and add to slider focusnode.
var label = query('label[for="'+this.id+'"]');
if(label.length){
label[0].id = (this.id+"_label");
this.focusNode.setAttribute("aria-labelledby", label[0].id);
}
this.focusNode.setAttribute("aria-valuemin", this.minimum);
this.focusNode.setAttribute("aria-valuemax", this.maximum);
},
postCreate: function(){
this.inherited(arguments);
if(this.showButtons){
this._connects.push(typematic.addMouseListener(
this.decrementButton, this, "_typematicCallback", 25, 500));
this._connects.push(typematic.addMouseListener(
this.incrementButton, this, "_typematicCallback", 25, 500));
}
this.connect(this.domNode, !has("mozilla") ? "onmousewheel" : "DOMMouseScroll", "_mouseWheeled");
// define a custom constructor for a SliderMover that points back to me
var mover = declare(_SliderMover, {
widget: this
});
this._movable = new Moveable(this.sliderHandle, {mover: mover});
this._layoutHackIE7();
},
destroy: function(){
this._movable.destroy();
if(this._inProgressAnim && this._inProgressAnim.status != "stopped"){
this._inProgressAnim.stop(true);
}
this._supportingWidgets = registry.findWidgets(this.domNode); // tells destroy about pseudo-child widgets (ruler/labels)
this.inherited(arguments);
}
});
HorizontalSlider._Mover = _SliderMover; // for monkey patching
return HorizontalSlider;
});
}}});
require(["dojo/i18n"], function(i18n){
i18n._preloadLocalizations("dijit/nls/dijit-all", ["nl-nl","en-us","da","fi-fi","pt-pt","hu","sk","sl","pl","ca","sv","zh-tw","ar","en-gb","he-il","de-de","ko-kr","ja-jp","nb","ru","es-es","th","cs","it-it","pt-br","fr-fr","el","tr","zh-cn"]);
});
define("dijit/dijit-all", [
".",
"./dijit",
"./ColorPalette",
"./Declaration",
"./Dialog",
"./DialogUnderlay",
"./TooltipDialog",
"./Editor",
"./_editor/plugins/FontChoice",
"./_editor/plugins/LinkDialog",
"./Menu",
"./MenuItem",
"./PopupMenuItem",
"./CheckedMenuItem",
"./MenuBar",
"./MenuBarItem",
"./PopupMenuBarItem",
"./MenuSeparator",
"./ProgressBar",
"./TitlePane",
"./Toolbar",
"./Tooltip",
"./Tree",
"./InlineEditBox",
"./form/Form",
"./form/Button",
"./form/DropDownButton",
"./form/ComboButton",
"./form/ToggleButton",
"./form/CheckBox",
"./form/RadioButton",
"./form/TextBox",
"./form/ValidationTextBox",
"./form/CurrencyTextBox",
"./form/DateTextBox",
"./form/TimeTextBox",
"./form/NumberSpinner",
"./form/NumberTextBox",
"./form/ComboBox",
"./form/FilteringSelect",
"./form/MultiSelect",
"./form/Select",
"./form/HorizontalSlider",
"./form/VerticalSlider",
"./form/HorizontalRule",
"./form/VerticalRule",
"./form/HorizontalRuleLabels",
"./form/VerticalRuleLabels",
"./form/SimpleTextarea",
"./form/Textarea",
"./layout/AccordionContainer",
"./layout/ContentPane",
"./layout/BorderContainer",
"./layout/LayoutContainer",
"./layout/LinkPane",
"./layout/SplitContainer",
"./layout/StackContainer",
"./layout/TabContainer"
], function(dijit){
// module:
// dijit/dijit-all
// summary:
// A rollup that includes every dijit. You probably don't need this.
console.warn("dijit-all may include much more code than your application actually requires. We strongly recommend that you investigate a custom build or the web build tool");
return dijit;
});