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: "" + "" + "" + "", 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: "xx-small" } 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 "
" + name + "
"; } }, _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 "" + name + ""; } }, _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 + ""; } }, _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 }); }; }); }); }, 'url:dijit/templates/CheckedMenuItem.html':"\n\t\n\t\t\"\"\n\t\t\n\t\n\t\n\t\n\t \n\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: '', _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 // 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: "
", // 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':"
\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, "&").replace(//gm, ">").replace(/"/gm, """); if(!noSingleQuotes){ str = str.replace(/'/gm, "'"); } 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
without an open tag , 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 +''; }else{ if(node.childNodes.length){ output += '>' + dijit._editor.getChildrenHtml(node)+''; }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 += '>'; } } } break; case 4: // cdata case 3: // text // FIXME: output = dijit._editor.escapeXml(node.nodeValue, true); break; case 8: //comment // FIXME: output = ''; break; default: output = ""; } 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/_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 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 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 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 // | second line of first paragraph // | second paragraph // // will generate: // // |

// | first paragraph // |
// | second line of first paragraph // |

// |

// | second paragraph // |

// // 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 // | two // | three // | // | four // | five // | six // // It will appear on the screen as two 'paragraphs' of three lines each. Markupwise, this generates: // // BR: // | one
// | two
// | three
// |
// | four
// | five
// | six
// // DIV: // |
one
// |
two
// |
three
// |
 
// |
four
// |
five
// |
six
// 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.then(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
, so just use it. var ne = lang.mixin({},e); ne.shiftKey = true; if(!this.handleEnterKey(ne)){ event.stop(e); } } }); if(has("ie") == 9){ this.connect(editor.document, "onpaste", function(e){ setTimeout(dojo.hitch(this, function(){ // Use the old range/selection code to kick IE 9 into updating // its range by moving it back, then forward, one 'character'. var r = this.editor.document.selection.createRange(); r.move('character',-1); r.select(); r.move('character',1); r.select(); }),0); }); } 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', selectionapi, ['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', selectionapi, [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: ' ', //   // 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
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', '
'); } } 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
, 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| | |\xA0|]*\bclass=['"]Apple-style-span['"][^>]*>(\s| | |\xA0)<\/span>)?(
)?$/.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
, 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
, 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
, 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':"
\n\t
+-\"\"\n\t\t\n\t
\n
\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: // | // |
// // example: // | // |
// |

I am content

// |
// 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 = " "; //   } // 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':"\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: // | // 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: // | 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: // | // // 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':"
\n\t
+-\"\"\n\t\t\n\t
\n
\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: // //
//
(one pane) //
(title bar) ...
//
(content pane)
//
//
// // 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: //
// Button //
// ContentPane //
//
// Create wrapper div, placed where the child is now this.domNode = domConstruct.place("