diff options
Diffstat (limited to 'lib/dijit/dijit-all.js.uncompressed.js')
-rw-r--r-- | lib/dijit/dijit-all.js.uncompressed.js | 28580 |
1 files changed, 0 insertions, 28580 deletions
diff --git a/lib/dijit/dijit-all.js.uncompressed.js b/lib/dijit/dijit-all.js.uncompressed.js deleted file mode 100644 index 1ce604128..000000000 --- a/lib/dijit/dijit-all.js.uncompressed.js +++ /dev/null @@ -1,28580 +0,0 @@ -/* - Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved. - Available via Academic Free License >= 2.1 OR the modified BSD license. - see: http://dojotoolkit.org/license for details -*/ - -/* - This is an optimized version of Dojo, built for deployment and not for - development. To get sources and documentation, please visit: - - http://dojotoolkit.org -*/ - -if(!dojo._hasResource["dojo.colors"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.colors"] = true; -dojo.provide("dojo.colors"); - -//TODO: this module appears to break naming conventions - -/*===== -dojo.colors = { - // summary: Color utilities -} -=====*/ - -(function(){ - // this is a standard conversion prescribed by the CSS3 Color Module - var hue2rgb = function(m1, m2, h){ - if(h < 0){ ++h; } - if(h > 1){ --h; } - var h6 = 6 * h; - if(h6 < 1){ return m1 + (m2 - m1) * h6; } - if(2 * h < 1){ return m2; } - if(3 * h < 2){ return m1 + (m2 - m1) * (2 / 3 - h) * 6; } - return m1; - }; - - dojo.colorFromRgb = function(/*String*/ color, /*dojo.Color?*/ obj){ - // summary: - // get rgb(a) array from css-style color declarations - // description: - // this function can handle all 4 CSS3 Color Module formats: rgb, - // rgba, hsl, hsla, including rgb(a) with percentage values. - var m = color.toLowerCase().match(/^(rgba?|hsla?)\(([\s\.\-,%0-9]+)\)/); - if(m){ - var c = m[2].split(/\s*,\s*/), l = c.length, t = m[1], a; - if((t == "rgb" && l == 3) || (t == "rgba" && l == 4)){ - var r = c[0]; - if(r.charAt(r.length - 1) == "%"){ - // 3 rgb percentage values - a = dojo.map(c, function(x){ - return parseFloat(x) * 2.56; - }); - if(l == 4){ a[3] = c[3]; } - return dojo.colorFromArray(a, obj); // dojo.Color - } - return dojo.colorFromArray(c, obj); // dojo.Color - } - if((t == "hsl" && l == 3) || (t == "hsla" && l == 4)){ - // normalize hsl values - var H = ((parseFloat(c[0]) % 360) + 360) % 360 / 360, - S = parseFloat(c[1]) / 100, - L = parseFloat(c[2]) / 100, - // calculate rgb according to the algorithm - // recommended by the CSS3 Color Module - m2 = L <= 0.5 ? L * (S + 1) : L + S - L * S, - m1 = 2 * L - m2; - a = [ - hue2rgb(m1, m2, H + 1 / 3) * 256, - hue2rgb(m1, m2, H) * 256, - hue2rgb(m1, m2, H - 1 / 3) * 256, - 1 - ]; - if(l == 4){ a[3] = c[3]; } - return dojo.colorFromArray(a, obj); // dojo.Color - } - } - return null; // dojo.Color - }; - - var confine = function(c, low, high){ - // summary: - // sanitize a color component by making sure it is a number, - // and clamping it to valid values - c = Number(c); - return isNaN(c) ? high : c < low ? low : c > high ? high : c; // Number - }; - - dojo.Color.prototype.sanitize = function(){ - // summary: makes sure that the object has correct attributes - var t = this; - t.r = Math.round(confine(t.r, 0, 255)); - t.g = Math.round(confine(t.g, 0, 255)); - t.b = Math.round(confine(t.b, 0, 255)); - t.a = confine(t.a, 0, 1); - return this; // dojo.Color - }; -})(); - - -dojo.colors.makeGrey = function(/*Number*/ g, /*Number?*/ a){ - // summary: creates a greyscale color with an optional alpha - return dojo.colorFromArray([g, g, g, a]); -}; - -// mixin all CSS3 named colors not already in _base, along with SVG 1.0 variant spellings -dojo.mixin(dojo.Color.named, { - aliceblue: [240,248,255], - antiquewhite: [250,235,215], - aquamarine: [127,255,212], - azure: [240,255,255], - beige: [245,245,220], - bisque: [255,228,196], - blanchedalmond: [255,235,205], - blueviolet: [138,43,226], - brown: [165,42,42], - burlywood: [222,184,135], - cadetblue: [95,158,160], - chartreuse: [127,255,0], - chocolate: [210,105,30], - coral: [255,127,80], - cornflowerblue: [100,149,237], - cornsilk: [255,248,220], - crimson: [220,20,60], - cyan: [0,255,255], - darkblue: [0,0,139], - darkcyan: [0,139,139], - darkgoldenrod: [184,134,11], - darkgray: [169,169,169], - darkgreen: [0,100,0], - darkgrey: [169,169,169], - darkkhaki: [189,183,107], - darkmagenta: [139,0,139], - darkolivegreen: [85,107,47], - darkorange: [255,140,0], - darkorchid: [153,50,204], - darkred: [139,0,0], - darksalmon: [233,150,122], - darkseagreen: [143,188,143], - darkslateblue: [72,61,139], - darkslategray: [47,79,79], - darkslategrey: [47,79,79], - darkturquoise: [0,206,209], - darkviolet: [148,0,211], - deeppink: [255,20,147], - deepskyblue: [0,191,255], - dimgray: [105,105,105], - dimgrey: [105,105,105], - dodgerblue: [30,144,255], - firebrick: [178,34,34], - floralwhite: [255,250,240], - forestgreen: [34,139,34], - gainsboro: [220,220,220], - ghostwhite: [248,248,255], - gold: [255,215,0], - goldenrod: [218,165,32], - greenyellow: [173,255,47], - grey: [128,128,128], - honeydew: [240,255,240], - hotpink: [255,105,180], - indianred: [205,92,92], - indigo: [75,0,130], - ivory: [255,255,240], - khaki: [240,230,140], - lavender: [230,230,250], - lavenderblush: [255,240,245], - lawngreen: [124,252,0], - lemonchiffon: [255,250,205], - lightblue: [173,216,230], - lightcoral: [240,128,128], - lightcyan: [224,255,255], - lightgoldenrodyellow: [250,250,210], - lightgray: [211,211,211], - lightgreen: [144,238,144], - lightgrey: [211,211,211], - lightpink: [255,182,193], - lightsalmon: [255,160,122], - lightseagreen: [32,178,170], - lightskyblue: [135,206,250], - lightslategray: [119,136,153], - lightslategrey: [119,136,153], - lightsteelblue: [176,196,222], - lightyellow: [255,255,224], - limegreen: [50,205,50], - linen: [250,240,230], - magenta: [255,0,255], - mediumaquamarine: [102,205,170], - mediumblue: [0,0,205], - mediumorchid: [186,85,211], - mediumpurple: [147,112,219], - mediumseagreen: [60,179,113], - mediumslateblue: [123,104,238], - mediumspringgreen: [0,250,154], - mediumturquoise: [72,209,204], - mediumvioletred: [199,21,133], - midnightblue: [25,25,112], - mintcream: [245,255,250], - mistyrose: [255,228,225], - moccasin: [255,228,181], - navajowhite: [255,222,173], - oldlace: [253,245,230], - olivedrab: [107,142,35], - orange: [255,165,0], - orangered: [255,69,0], - orchid: [218,112,214], - palegoldenrod: [238,232,170], - palegreen: [152,251,152], - paleturquoise: [175,238,238], - palevioletred: [219,112,147], - papayawhip: [255,239,213], - peachpuff: [255,218,185], - peru: [205,133,63], - pink: [255,192,203], - plum: [221,160,221], - powderblue: [176,224,230], - rosybrown: [188,143,143], - royalblue: [65,105,225], - saddlebrown: [139,69,19], - salmon: [250,128,114], - sandybrown: [244,164,96], - seagreen: [46,139,87], - seashell: [255,245,238], - sienna: [160,82,45], - skyblue: [135,206,235], - slateblue: [106,90,205], - slategray: [112,128,144], - slategrey: [112,128,144], - snow: [255,250,250], - springgreen: [0,255,127], - steelblue: [70,130,180], - tan: [210,180,140], - thistle: [216,191,216], - tomato: [255,99,71], - transparent: [0, 0, 0, 0], - turquoise: [64,224,208], - violet: [238,130,238], - wheat: [245,222,179], - whitesmoke: [245,245,245], - yellowgreen: [154,205,50] -}); - -} - -if(!dojo._hasResource["dojo.i18n"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.i18n"] = true; -dojo.provide("dojo.i18n"); - -/*===== -dojo.i18n = { - // summary: Utility classes to enable loading of resources for internationalization (i18n) -}; -=====*/ - -dojo.i18n.getLocalization = function(/*String*/packageName, /*String*/bundleName, /*String?*/locale){ - // summary: - // Returns an Object containing the localization for a given resource - // bundle in a package, matching the specified locale. - // description: - // Returns a hash containing name/value pairs in its prototypesuch - // that values can be easily overridden. Throws an exception if the - // bundle is not found. Bundle must have already been loaded by - // `dojo.requireLocalization()` or by a build optimization step. NOTE: - // try not to call this method as part of an object property - // definition (`var foo = { bar: dojo.i18n.getLocalization() }`). In - // some loading situations, the bundle may not be available in time - // for the object definition. Instead, call this method inside a - // function that is run after all modules load or the page loads (like - // in `dojo.addOnLoad()`), or in a widget lifecycle method. - // packageName: - // package which is associated with this resource - // bundleName: - // the base filename of the resource bundle (without the ".js" suffix) - // locale: - // the variant to load (optional). By default, the locale defined by - // the host environment: dojo.locale - - locale = dojo.i18n.normalizeLocale(locale); - - // look for nearest locale match - var elements = locale.split('-'); - var module = [packageName,"nls",bundleName].join('.'); - var bundle = dojo._loadedModules[module]; - if(bundle){ - var localization; - for(var i = elements.length; i > 0; i--){ - var loc = elements.slice(0, i).join('_'); - if(bundle[loc]){ - localization = bundle[loc]; - break; - } - } - if(!localization){ - localization = bundle.ROOT; - } - - // make a singleton prototype so that the caller won't accidentally change the values globally - if(localization){ - var clazz = function(){}; - clazz.prototype = localization; - return new clazz(); // Object - } - } - - throw new Error("Bundle not found: " + bundleName + " in " + packageName+" , locale=" + locale); -}; - -dojo.i18n.normalizeLocale = function(/*String?*/locale){ - // summary: - // Returns canonical form of locale, as used by Dojo. - // - // description: - // All variants are case-insensitive and are separated by '-' as specified in [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt). - // If no locale is specified, the dojo.locale is returned. dojo.locale is defined by - // the user agent's locale unless overridden by djConfig. - - var result = locale ? locale.toLowerCase() : dojo.locale; - if(result == "root"){ - result = "ROOT"; - } - return result; // String -}; - -dojo.i18n._requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String?*/availableFlatLocales){ - // summary: - // See dojo.requireLocalization() - // description: - // Called by the bootstrap, but factored out so that it is only - // included in the build when needed. - - var targetLocale = dojo.i18n.normalizeLocale(locale); - var bundlePackage = [moduleName, "nls", bundleName].join("."); - // NOTE: - // When loading these resources, the packaging does not match what is - // on disk. This is an implementation detail, as this is just a - // private data structure to hold the loaded resources. e.g. - // `tests/hello/nls/en-us/salutations.js` is loaded as the object - // `tests.hello.nls.salutations.en_us={...}` The structure on disk is - // intended to be most convenient for developers and translators, but - // in memory it is more logical and efficient to store in a different - // order. Locales cannot use dashes, since the resulting path will - // not evaluate as valid JS, so we translate them to underscores. - - //Find the best-match locale to load if we have available flat locales. - var bestLocale = ""; - if(availableFlatLocales){ - var flatLocales = availableFlatLocales.split(","); - for(var i = 0; i < flatLocales.length; i++){ - //Locale must match from start of string. - //Using ["indexOf"] so customBase builds do not see - //this as a dojo._base.array dependency. - if(targetLocale["indexOf"](flatLocales[i]) == 0){ - if(flatLocales[i].length > bestLocale.length){ - bestLocale = flatLocales[i]; - } - } - } - if(!bestLocale){ - bestLocale = "ROOT"; - } - } - - //See if the desired locale is already loaded. - var tempLocale = availableFlatLocales ? bestLocale : targetLocale; - var bundle = dojo._loadedModules[bundlePackage]; - var localizedBundle = null; - if(bundle){ - if(dojo.config.localizationComplete && bundle._built){return;} - var jsLoc = tempLocale.replace(/-/g, '_'); - var translationPackage = bundlePackage+"."+jsLoc; - localizedBundle = dojo._loadedModules[translationPackage]; - } - - if(!localizedBundle){ - bundle = dojo["provide"](bundlePackage); - var syms = dojo._getModuleSymbols(moduleName); - var modpath = syms.concat("nls").join("/"); - var parent; - - dojo.i18n._searchLocalePath(tempLocale, availableFlatLocales, function(loc){ - var jsLoc = loc.replace(/-/g, '_'); - var translationPackage = bundlePackage + "." + jsLoc; - var loaded = false; - if(!dojo._loadedModules[translationPackage]){ - // Mark loaded whether it's found or not, so that further load attempts will not be made - dojo["provide"](translationPackage); - var module = [modpath]; - if(loc != "ROOT"){module.push(loc);} - module.push(bundleName); - var filespec = module.join("/") + '.js'; - loaded = dojo._loadPath(filespec, null, function(hash){ - // Use singleton with prototype to point to parent bundle, then mix-in result from loadPath - var clazz = function(){}; - clazz.prototype = parent; - bundle[jsLoc] = new clazz(); - for(var j in hash){ bundle[jsLoc][j] = hash[j]; } - }); - }else{ - loaded = true; - } - if(loaded && bundle[jsLoc]){ - parent = bundle[jsLoc]; - }else{ - bundle[jsLoc] = parent; - } - - if(availableFlatLocales){ - //Stop the locale path searching if we know the availableFlatLocales, since - //the first call to this function will load the only bundle that is needed. - return true; - } - }); - } - - //Save the best locale bundle as the target locale bundle when we know the - //the available bundles. - if(availableFlatLocales && targetLocale != bestLocale){ - bundle[targetLocale.replace(/-/g, '_')] = bundle[bestLocale.replace(/-/g, '_')]; - } -}; - -(function(){ - // If other locales are used, dojo.requireLocalization should load them as - // well, by default. - // - // Override dojo.requireLocalization to do load the default bundle, then - // iterate through the extraLocale list and load those translations as - // well, unless a particular locale was requested. - - var extra = dojo.config.extraLocale; - if(extra){ - if(!extra instanceof Array){ - extra = [extra]; - } - - var req = dojo.i18n._requireLocalization; - dojo.i18n._requireLocalization = function(m, b, locale, availableFlatLocales){ - req(m,b,locale, availableFlatLocales); - if(locale){return;} - for(var i=0; i<extra.length; i++){ - req(m,b,extra[i], availableFlatLocales); - } - }; - } -})(); - -dojo.i18n._searchLocalePath = function(/*String*/locale, /*Boolean*/down, /*Function*/searchFunc){ - // summary: - // A helper method to assist in searching for locale-based resources. - // Will iterate through the variants of a particular locale, either up - // or down, executing a callback function. For example, "en-us" and - // true will try "en-us" followed by "en" and finally "ROOT". - - locale = dojo.i18n.normalizeLocale(locale); - - var elements = locale.split('-'); - var searchlist = []; - for(var i = elements.length; i > 0; i--){ - searchlist.push(elements.slice(0, i).join('-')); - } - searchlist.push(false); - if(down){searchlist.reverse();} - - for(var j = searchlist.length - 1; j >= 0; j--){ - var loc = searchlist[j] || "ROOT"; - var stop = searchFunc(loc); - if(stop){ break; } - } -}; - -dojo.i18n._preloadLocalizations = function(/*String*/bundlePrefix, /*Array*/localesGenerated){ - // summary: - // Load built, flattened resource bundles, if available for all - // locales used in the page. Only called by built layer files. - - function preload(locale){ - locale = dojo.i18n.normalizeLocale(locale); - dojo.i18n._searchLocalePath(locale, true, function(loc){ - for(var i=0; i<localesGenerated.length;i++){ - if(localesGenerated[i] == loc){ - dojo["require"](bundlePrefix+"_"+loc); - return true; // Boolean - } - } - return false; // Boolean - }); - } - preload(); - var extra = dojo.config.extraLocale||[]; - for(var i=0; i<extra.length; i++){ - preload(extra[i]); - } -}; - -} - -if(!dojo._hasResource["dijit._PaletteMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit._PaletteMixin"] = true; -dojo.provide("dijit._PaletteMixin"); - - -dojo.declare("dijit._PaletteMixin", - [dijit._CssStateMixin], - { - // summary: - // A keyboard accessible palette, for picking a color/emoticon/etc. - // description: - // A mixin for a grid showing various entities, so the user can pick a certain entity. - - // defaultTimeout: Number - // Number of milliseconds before a held key or button becomes typematic - defaultTimeout: 500, - - // timeoutChangeRate: Number - // Fraction of time used to change the typematic timer between events - // 1.0 means that each typematic event fires at defaultTimeout intervals - // < 1.0 means that each typematic event fires at an increasing faster rate - timeoutChangeRate: 0.90, - - // value: String - // Currently selected color/emoticon/etc. - value: null, - - // _selectedCell: [private] Integer - // Index of the currently selected cell. Initially, none selected - _selectedCell: -1, - - // _currentFocus: [private] DomNode - // The currently focused cell (if the palette itself has focus), or otherwise - // the cell to be focused when the palette itself gets focus. - // Different from value, which represents the selected (i.e. clicked) cell. -/*===== - _currentFocus: null, -=====*/ - - // _xDim: [protected] Integer - // This is the number of cells horizontally across. -/*===== - _xDim: null, -=====*/ - - // _yDim: [protected] Integer - // This is the number of cells vertically down. -/*===== - _yDim: null, -=====*/ - - // tabIndex: String - // Widget tab index. - tabIndex: "0", - - // cellClass: [protected] String - // CSS class applied to each cell in the palette - cellClass: "dijitPaletteCell", - - // dyeClass: [protected] String - // Name of javascript class for Object created for each cell of the palette. - // dyeClass should implements dijit.Dye interface - dyeClass: '', - - _preparePalette: function(choices, titles) { - // summary: - // Subclass must call _preparePalette() from postCreate(), passing in the tooltip - // for each cell - // choices: String[][] - // id's for each cell of the palette, used to create Dye JS object for each cell - // titles: String[] - // Localized tooltip for each cell - - this._cells = []; - var url = this._blankGif; - - var dyeClassObj = dojo.getObject(this.dyeClass); - - for(var row=0; row < choices.length; row++){ - var rowNode = dojo.create("tr", {tabIndex: "-1"}, this.gridNode); - for(var col=0; col < choices[row].length; col++){ - var value = choices[row][col]; - if(value){ - var cellObject = new dyeClassObj(value); - - var cellNode = dojo.create("td", { - "class": this.cellClass, - tabIndex: "-1", - title: titles[value] - }); - - // prepare cell inner structure - cellObject.fillCell(cellNode, url); - - this.connect(cellNode, "ondijitclick", "_onCellClick"); - this._trackMouseState(cellNode, this.cellClass); - - dojo.place(cellNode, rowNode); - - cellNode.index = this._cells.length; - - // save cell info into _cells - this._cells.push({node:cellNode, dye:cellObject}); - } - } - } - this._xDim = choices[0].length; - this._yDim = choices.length; - - // Now set all events - // The palette itself is navigated to with the tab key on the keyboard - // Keyboard navigation within the Palette is with the arrow keys - // Spacebar selects the cell. - // For the up key the index is changed by negative the x dimension. - - var keyIncrementMap = { - UP_ARROW: -this._xDim, - // The down key the index is increase by the x dimension. - DOWN_ARROW: this._xDim, - // Right and left move the index by 1. - RIGHT_ARROW: this.isLeftToRight() ? 1 : -1, - LEFT_ARROW: this.isLeftToRight() ? -1 : 1 - }; - for(var key in keyIncrementMap){ - this._connects.push( - dijit.typematic.addKeyListener( - this.domNode, - {charOrCode:dojo.keys[key], ctrlKey:false, altKey:false, shiftKey:false}, - this, - function(){ - var increment = keyIncrementMap[key]; - return function(count){ this._navigateByKey(increment, count); }; - }(), - this.timeoutChangeRate, - this.defaultTimeout - ) - ); - } - }, - - postCreate: function(){ - this.inherited(arguments); - - // Set initial navigable node. - this._setCurrent(this._cells[0].node); - }, - - focus: function(){ - // summary: - // Focus this widget. Puts focus on the most recently focused cell. - - // The cell already has tabIndex set, just need to set CSS and focus it - dijit.focus(this._currentFocus); - }, - - _onCellClick: function(/*Event*/ evt){ - // summary: - // Handler for click, enter key & space key. Selects the cell. - // evt: - // The event. - // tags: - // private - - var target = evt.currentTarget, - value = this._getDye(target).getValue(); - - // First focus the clicked cell, and then send onChange() notification. - // onChange() (via _setValueAttr) must be after the focus call, because - // it may trigger a refocus to somewhere else (like the Editor content area), and that - // second focus should win. - // Use setTimeout because IE doesn't like changing focus inside of an event handler. - this._setCurrent(target); - setTimeout(dojo.hitch(this, function(){ - dijit.focus(target); - this._setValueAttr(value, true); - })); - - // workaround bug where hover class is not removed on popup because the popup is - // closed and then there's no onblur event on the cell - dojo.removeClass(target, "dijitPaletteCellHover"); - - dojo.stopEvent(evt); - }, - - _setCurrent: function(/*DomNode*/ node){ - // summary: - // Sets which node is the focused cell. - // description: - // At any point in time there's exactly one - // cell with tabIndex != -1. If focus is inside the palette then - // focus is on that cell. - // - // After calling this method, arrow key handlers and mouse click handlers - // should focus the cell in a setTimeout(). - // tags: - // protected - if("_currentFocus" in this){ - // Remove tabIndex on old cell - dojo.attr(this._currentFocus, "tabIndex", "-1"); - } - - // Set tabIndex of new cell - this._currentFocus = node; - if(node){ - dojo.attr(node, "tabIndex", this.tabIndex); - } - }, - - _setValueAttr: function(value, priorityChange){ - // summary: - // This selects a cell. It triggers the onChange event. - // value: String value of the cell to select - // tags: - // protected - // priorityChange: - // Optional parameter used to tell the select whether or not to fire - // onChange event. - - // clear old value and selected cell - this.value = null; - if(this._selectedCell >= 0){ - dojo.removeClass(this._cells[this._selectedCell].node, "dijitPaletteCellSelected"); - } - this._selectedCell = -1; - - // search for cell matching specified value - if(value){ - for(var i = 0; i < this._cells.length; i++){ - if(value == this._cells[i].dye.getValue()){ - this._selectedCell = i; - this.value = value; - - dojo.addClass(this._cells[i].node, "dijitPaletteCellSelected"); - - if(priorityChange || priorityChange === undefined){ - this.onChange(value); - } - - break; - } - } - } - }, - - onChange: function(value){ - // summary: - // Callback when a cell is selected. - // value: String - // Value corresponding to cell. - }, - - _navigateByKey: function(increment, typeCount){ - // summary: - // This is the callback for typematic. - // It changes the focus and the highlighed cell. - // increment: - // How much the key is navigated. - // typeCount: - // How many times typematic has fired. - // tags: - // private - - // typecount == -1 means the key is released. - if(typeCount == -1){ return; } - - var newFocusIndex = this._currentFocus.index + increment; - if(newFocusIndex < this._cells.length && newFocusIndex > -1){ - var focusNode = this._cells[newFocusIndex].node; - this._setCurrent(focusNode); - - // Actually focus the node, for the benefit of screen readers. - // Use setTimeout because IE doesn't like changing focus inside of an event handler - setTimeout(dojo.hitch(dijit, "focus", focusNode), 0); - } - }, - - _getDye: function(/*DomNode*/ cell){ - // summary: - // Get JS object for given cell DOMNode - - return this._cells[cell.index].dye; - } -}); - -/*===== -dojo.declare("dijit.Dye", - null, - { - // summary: - // Interface for the JS Object associated with a palette cell (i.e. DOMNode) - - constructor: function(alias){ - // summary: - // Initialize according to value or alias like "white" - // alias: String - }, - - getValue: function(){ - // summary: - // Return "value" of cell; meaning of "value" varies by subclass. - // description: - // For example color hex value, emoticon ascii value etc, entity hex value. - }, - - fillCell: function(cell, blankGif){ - // summary: - // Add cell DOMNode inner structure - // cell: DomNode - // The surrounding cell - // blankGif: String - // URL for blank cell image - } - } -); -=====*/ - -} - -if(!dojo._hasResource["dijit.ColorPalette"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.ColorPalette"] = true; -dojo.provide("dijit.ColorPalette"); - - - - - - - - - - -dojo.declare("dijit.ColorPalette", - [dijit._Widget, dijit._Templated, dijit._PaletteMixin], - { - // summary: - // A keyboard accessible color-picking widget - // description: - // Grid showing various colors, so the user can pick a certain color. - // Can be used standalone, or as a popup. - // - // example: - // | <div dojoType="dijit.ColorPalette"></div> - // - // example: - // | var picker = new dijit.ColorPalette({ },srcNode); - // | picker.startup(); - - - // palette: String - // Size of grid, either "7x10" or "3x4". - palette: "7x10", - - // _palettes: [protected] Map - // This represents the value of the colors. - // The first level is a hashmap of the different palettes available. - // The next two dimensions represent the columns and rows of colors. - _palettes: { - "7x10": [["white", "seashell", "cornsilk", "lemonchiffon","lightyellow", "palegreen", "paleturquoise", "lightcyan", "lavender", "plum"], - ["lightgray", "pink", "bisque", "moccasin", "khaki", "lightgreen", "lightseagreen", "lightskyblue", "cornflowerblue", "violet"], - ["silver", "lightcoral", "sandybrown", "orange", "palegoldenrod", "chartreuse", "mediumturquoise", "skyblue", "mediumslateblue","orchid"], - ["gray", "red", "orangered", "darkorange", "yellow", "limegreen", "darkseagreen", "royalblue", "slateblue", "mediumorchid"], - ["dimgray", "crimson", "chocolate", "coral", "gold", "forestgreen", "seagreen", "blue", "blueviolet", "darkorchid"], - ["darkslategray","firebrick","saddlebrown", "sienna", "olive", "green", "darkcyan", "mediumblue","darkslateblue", "darkmagenta" ], - ["black", "darkred", "maroon", "brown", "darkolivegreen", "darkgreen", "midnightblue", "navy", "indigo", "purple"]], - - "3x4": [["white", "lime", "green", "blue"], - ["silver", "yellow", "fuchsia", "navy"], - ["gray", "red", "purple", "black"]] - }, - - // _imagePaths: [protected] Map - // This is stores the path to the palette images - _imagePaths: { - "7x10": dojo.moduleUrl("dijit.themes", "a11y/colors7x10.png"), - "3x4": dojo.moduleUrl("dijit.themes", "a11y/colors3x4.png"), - "7x10-rtl": dojo.moduleUrl("dijit.themes", "a11y/colors7x10-rtl.png"), - "3x4-rtl": dojo.moduleUrl("dijit.themes", "a11y/colors3x4-rtl.png") - }, - - // templateString: String - // The template of this widget. - templateString: dojo.cache("dijit", "templates/ColorPalette.html", "<div class=\"dijitInline dijitColorPalette\">\n\t<img class=\"dijitColorPaletteUnder\" dojoAttachPoint=\"imageNode\" waiRole=\"presentation\" alt=\"\"/>\n\t<table class=\"dijitPaletteTable\" cellSpacing=\"0\" cellPadding=\"0\">\n\t\t<tbody dojoAttachPoint=\"gridNode\"></tbody>\n\t</table>\n</div>\n"), - - baseClass: "dijitColorPalette", - - dyeClass: 'dijit._Color', - - buildRendering: function(){ - // Instantiate the template, which makes a skeleton into which we'll insert a bunch of - // <img> nodes - - this.inherited(arguments); - - this.imageNode.setAttribute("src", this._imagePaths[this.palette + (this.isLeftToRight() ? "" : "-rtl")].toString()); - - var i18nColorNames = dojo.i18n.getLocalization("dojo", "colors", this.lang); - this._preparePalette( - this._palettes[this.palette], - i18nColorNames - ); - } -}); - -dojo.declare("dijit._Color", dojo.Color, - // summary: - // Object associated with each cell in a ColorPalette palette. - // Implements dijit.Dye. - { - constructor: function(/*String*/alias){ - this._alias = alias; - this.setColor(dojo.Color.named[alias]); - }, - - getValue: function(){ - // summary: - // Note that although dijit._Color is initialized with a value like "white" getValue() always - // returns a hex value - return this.toHex(); - }, - - fillCell: function(/*DOMNode*/ cell, /*String*/ blankGif){ - dojo.create("img", { - src: blankGif, - "class": "dijitPaletteImg", - alt: this._alias - }, cell); - } - } -); - -} - -if(!dojo._hasResource["dijit.Declaration"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.Declaration"] = true; -dojo.provide("dijit.Declaration"); - - - -dojo.declare( - "dijit.Declaration", - dijit._Widget, - { - // summary: - // The Declaration widget allows a developer to declare new widget - // classes directly from a snippet of markup. - - // _noScript: [private] Boolean - // Flag to parser to leave alone the script tags contained inside of me - _noScript: true, - - // widgetClass: String - // Name of class being declared, ex: "acme.myWidget" - widgetClass: "", - - // propList: Object - // Set of attributes for this widget along with default values, ex: - // {delay: 100, title: "hello world"} - defaults: null, - - // mixins: String[] - // List containing the prototype for this widget, and also any mixins, - // ex: ["dijit._Widget", "dijit._Container"] - mixins: [], - - buildRendering: function(){ - var src = this.srcNodeRef.parentNode.removeChild(this.srcNodeRef), - methods = dojo.query("> script[type^='dojo/method'][event]", src).orphan(), - postscriptConnects = dojo.query("> script[type^='dojo/method']", src).orphan(), - regularConnects = dojo.query("> script[type^='dojo/connect']", src).orphan(), - srcType = src.nodeName; - - var propList = this.defaults || {}; - - // For all methods defined like <script type="dojo/method" event="foo">, - // add that method to prototype - dojo.forEach(methods, function(s){ - var evt = s.getAttribute("event"), - func = dojo.parser._functionFromScript(s); - propList[evt] = func; - }); - - // map array of strings like [ "dijit.form.Button" ] to array of mixin objects - // (note that dojo.map(this.mixins, dojo.getObject) doesn't work because it passes - // a bogus third argument to getObject(), confusing it) - this.mixins = this.mixins.length ? - dojo.map(this.mixins, function(name){ return dojo.getObject(name); } ) : - [ dijit._Widget, dijit._Templated ]; - - propList.widgetsInTemplate = true; - propList._skipNodeCache = true; - propList.templateString = "<"+srcType+" class='"+src.className+"' dojoAttachPoint='"+(src.getAttribute("dojoAttachPoint") || '')+"' dojoAttachEvent='"+(src.getAttribute("dojoAttachEvent") || '')+"' >"+src.innerHTML.replace(/\%7B/g,"{").replace(/\%7D/g,"}")+"</"+srcType+">"; - - // strip things so we don't create stuff under us in the initial setup phase - dojo.query("[dojoType]", src).forEach(function(node){ - node.removeAttribute("dojoType"); - }); - - // create the new widget class - var wc = dojo.declare( - this.widgetClass, - this.mixins, - propList - ); - - // Handle <script> blocks of form: - // <script type="dojo/connect" event="foo"> - // and - // <script type="dojo/method"> - // (Note that the second one is just shorthand for a dojo/connect to postscript) - // Since this is a connect in the declaration, we are actually connection to the method - // in the _prototype_. - var connects = regularConnects.concat(postscriptConnects); - dojo.forEach(connects, function(s){ - var evt = s.getAttribute("event") || "postscript", - func = dojo.parser._functionFromScript(s); - dojo.connect(wc.prototype, evt, func); - }); - } - } -); - -} - -if(!dojo._hasResource["dojo.dnd.common"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.dnd.common"] = true; -dojo.provide("dojo.dnd.common"); - -dojo.dnd.getCopyKeyState = dojo.isCopyKey; - -dojo.dnd._uniqueId = 0; -dojo.dnd.getUniqueId = function(){ - // summary: - // returns a unique string for use with any DOM element - var id; - do{ - id = dojo._scopeName + "Unique" + (++dojo.dnd._uniqueId); - }while(dojo.byId(id)); - return id; -}; - -dojo.dnd._empty = {}; - -dojo.dnd.isFormElement = function(/*Event*/ e){ - // summary: - // returns true if user clicked on a form element - var t = e.target; - if(t.nodeType == 3 /*TEXT_NODE*/){ - t = t.parentNode; - } - return " button textarea input select option ".indexOf(" " + t.tagName.toLowerCase() + " ") >= 0; // Boolean -}; - -} - -if(!dojo._hasResource["dojo.dnd.autoscroll"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.dnd.autoscroll"] = true; -dojo.provide("dojo.dnd.autoscroll"); - -dojo.dnd.getViewport = function(){ - // summary: - // Returns a viewport size (visible part of the window) - - // TODO: remove this when getViewport() moved to dojo core, see #7028 - - // FIXME: need more docs!! - var d = dojo.doc, dd = d.documentElement, w = window, b = dojo.body(); - if(dojo.isMozilla){ - return {w: dd.clientWidth, h: w.innerHeight}; // Object - }else if(!dojo.isOpera && w.innerWidth){ - return {w: w.innerWidth, h: w.innerHeight}; // Object - }else if (!dojo.isOpera && dd && dd.clientWidth){ - return {w: dd.clientWidth, h: dd.clientHeight}; // Object - }else if (b.clientWidth){ - return {w: b.clientWidth, h: b.clientHeight}; // Object - } - return null; // Object -}; - -dojo.dnd.V_TRIGGER_AUTOSCROLL = 32; -dojo.dnd.H_TRIGGER_AUTOSCROLL = 32; - -dojo.dnd.V_AUTOSCROLL_VALUE = 16; -dojo.dnd.H_AUTOSCROLL_VALUE = 16; - -dojo.dnd.autoScroll = function(e){ - // summary: - // a handler for onmousemove event, which scrolls the window, if - // necesary - // e: Event - // onmousemove event - - // FIXME: needs more docs! - var v = dojo.dnd.getViewport(), dx = 0, dy = 0; - if(e.clientX < dojo.dnd.H_TRIGGER_AUTOSCROLL){ - dx = -dojo.dnd.H_AUTOSCROLL_VALUE; - }else if(e.clientX > v.w - dojo.dnd.H_TRIGGER_AUTOSCROLL){ - dx = dojo.dnd.H_AUTOSCROLL_VALUE; - } - if(e.clientY < dojo.dnd.V_TRIGGER_AUTOSCROLL){ - dy = -dojo.dnd.V_AUTOSCROLL_VALUE; - }else if(e.clientY > v.h - dojo.dnd.V_TRIGGER_AUTOSCROLL){ - dy = dojo.dnd.V_AUTOSCROLL_VALUE; - } - window.scrollBy(dx, dy); -}; - -dojo.dnd._validNodes = {"div": 1, "p": 1, "td": 1}; -dojo.dnd._validOverflow = {"auto": 1, "scroll": 1}; - -dojo.dnd.autoScrollNodes = function(e){ - // summary: - // a handler for onmousemove event, which scrolls the first avaialble - // Dom element, it falls back to dojo.dnd.autoScroll() - // e: Event - // onmousemove event - - // FIXME: needs more docs! - for(var n = e.target; n;){ - if(n.nodeType == 1 && (n.tagName.toLowerCase() in dojo.dnd._validNodes)){ - var s = dojo.getComputedStyle(n); - if(s.overflow.toLowerCase() in dojo.dnd._validOverflow){ - var b = dojo._getContentBox(n, s), t = dojo.position(n, true); - //console.log(b.l, b.t, t.x, t.y, n.scrollLeft, n.scrollTop); - var w = Math.min(dojo.dnd.H_TRIGGER_AUTOSCROLL, b.w / 2), - h = Math.min(dojo.dnd.V_TRIGGER_AUTOSCROLL, b.h / 2), - rx = e.pageX - t.x, ry = e.pageY - t.y, dx = 0, dy = 0; - if(dojo.isWebKit || dojo.isOpera){ - // FIXME: this code should not be here, it should be taken into account - // either by the event fixing code, or the dojo.position() - // FIXME: this code doesn't work on Opera 9.5 Beta - rx += dojo.body().scrollLeft, ry += dojo.body().scrollTop; - } - if(rx > 0 && rx < b.w){ - if(rx < w){ - dx = -w; - }else if(rx > b.w - w){ - dx = w; - } - } - //console.log("ry =", ry, "b.h =", b.h, "h =", h); - if(ry > 0 && ry < b.h){ - if(ry < h){ - dy = -h; - }else if(ry > b.h - h){ - dy = h; - } - } - var oldLeft = n.scrollLeft, oldTop = n.scrollTop; - n.scrollLeft = n.scrollLeft + dx; - n.scrollTop = n.scrollTop + dy; - if(oldLeft != n.scrollLeft || oldTop != n.scrollTop){ return; } - } - } - try{ - n = n.parentNode; - }catch(x){ - n = null; - } - } - dojo.dnd.autoScroll(e); -}; - -} - -if(!dojo._hasResource["dojo.dnd.Mover"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.dnd.Mover"] = true; -dojo.provide("dojo.dnd.Mover"); - - - - -dojo.declare("dojo.dnd.Mover", null, { - constructor: function(node, e, host){ - // summary: - // an object, which makes a node follow the mouse. - // Used as a default mover, and as a base class for custom movers. - // node: Node - // a node (or node's id) to be moved - // e: Event - // a mouse event, which started the move; - // only pageX and pageY properties are used - // host: Object? - // object which implements the functionality of the move, - // and defines proper events (onMoveStart and onMoveStop) - this.node = dojo.byId(node); - this.marginBox = {l: e.pageX, t: e.pageY}; - this.mouseButton = e.button; - var h = this.host = host, d = node.ownerDocument, - firstEvent = dojo.connect(d, "onmousemove", this, "onFirstMove"); - this.events = [ - dojo.connect(d, "onmousemove", this, "onMouseMove"), - dojo.connect(d, "onmouseup", this, "onMouseUp"), - // cancel text selection and text dragging - dojo.connect(d, "ondragstart", dojo.stopEvent), - dojo.connect(d.body, "onselectstart", dojo.stopEvent), - firstEvent - ]; - // notify that the move has started - if(h && h.onMoveStart){ - h.onMoveStart(this); - } - }, - // mouse event processors - onMouseMove: function(e){ - // summary: - // event processor for onmousemove - // e: Event - // mouse event - dojo.dnd.autoScroll(e); - var m = this.marginBox; - this.host.onMove(this, {l: m.l + e.pageX, t: m.t + e.pageY}, e); - dojo.stopEvent(e); - }, - onMouseUp: function(e){ - if(dojo.isWebKit && dojo.isMac && this.mouseButton == 2 ? - e.button == 0 : this.mouseButton == e.button){ - this.destroy(); - } - dojo.stopEvent(e); - }, - // utilities - onFirstMove: function(e){ - // summary: - // makes the node absolute; it is meant to be called only once. - // relative and absolutely positioned nodes are assumed to use pixel units - var s = this.node.style, l, t, h = this.host; - switch(s.position){ - case "relative": - case "absolute": - // assume that left and top values are in pixels already - l = Math.round(parseFloat(s.left)) || 0; - t = Math.round(parseFloat(s.top)) || 0; - break; - default: - s.position = "absolute"; // enforcing the absolute mode - var m = dojo.marginBox(this.node); - // event.pageX/pageY (which we used to generate the initial - // margin box) includes padding and margin set on the body. - // However, setting the node's position to absolute and then - // doing dojo.marginBox on it *doesn't* take that additional - // space into account - so we need to subtract the combined - // padding and margin. We use getComputedStyle and - // _getMarginBox/_getContentBox to avoid the extra lookup of - // the computed style. - var b = dojo.doc.body; - var bs = dojo.getComputedStyle(b); - var bm = dojo._getMarginBox(b, bs); - var bc = dojo._getContentBox(b, bs); - l = m.l - (bc.l - bm.l); - t = m.t - (bc.t - bm.t); - break; - } - this.marginBox.l = l - this.marginBox.l; - this.marginBox.t = t - this.marginBox.t; - if(h && h.onFirstMove){ - h.onFirstMove(this, e); - } - dojo.disconnect(this.events.pop()); - }, - destroy: function(){ - // summary: - // stops the move, deletes all references, so the object can be garbage-collected - dojo.forEach(this.events, dojo.disconnect); - // undo global settings - var h = this.host; - if(h && h.onMoveStop){ - h.onMoveStop(this); - } - // destroy objects - this.events = this.node = this.host = null; - } -}); - -} - -if(!dojo._hasResource["dojo.dnd.Moveable"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.dnd.Moveable"] = true; -dojo.provide("dojo.dnd.Moveable"); - - - -/*===== -dojo.declare("dojo.dnd.__MoveableArgs", [], { - // handle: Node||String - // A node (or node's id), which is used as a mouse handle. - // If omitted, the node itself is used as a handle. - handle: null, - - // delay: Number - // delay move by this number of pixels - delay: 0, - - // skip: Boolean - // skip move of form elements - skip: false, - - // mover: Object - // a constructor of custom Mover - mover: dojo.dnd.Mover -}); -=====*/ - -dojo.declare("dojo.dnd.Moveable", null, { - // object attributes (for markup) - handle: "", - delay: 0, - skip: false, - - constructor: function(node, params){ - // summary: - // an object, which makes a node moveable - // node: Node - // a node (or node's id) to be moved - // params: dojo.dnd.__MoveableArgs? - // optional parameters - this.node = dojo.byId(node); - if(!params){ params = {}; } - this.handle = params.handle ? dojo.byId(params.handle) : null; - if(!this.handle){ this.handle = this.node; } - this.delay = params.delay > 0 ? params.delay : 0; - this.skip = params.skip; - this.mover = params.mover ? params.mover : dojo.dnd.Mover; - this.events = [ - dojo.connect(this.handle, "onmousedown", this, "onMouseDown"), - // cancel text selection and text dragging - dojo.connect(this.handle, "ondragstart", this, "onSelectStart"), - dojo.connect(this.handle, "onselectstart", this, "onSelectStart") - ]; - }, - - // markup methods - markupFactory: function(params, node){ - return new dojo.dnd.Moveable(node, params); - }, - - // methods - destroy: function(){ - // summary: - // stops watching for possible move, deletes all references, so the object can be garbage-collected - dojo.forEach(this.events, dojo.disconnect); - this.events = this.node = this.handle = null; - }, - - // mouse event processors - onMouseDown: function(e){ - // summary: - // event processor for onmousedown, creates a Mover for the node - // e: Event - // mouse event - if(this.skip && dojo.dnd.isFormElement(e)){ return; } - if(this.delay){ - this.events.push( - dojo.connect(this.handle, "onmousemove", this, "onMouseMove"), - dojo.connect(this.handle, "onmouseup", this, "onMouseUp") - ); - this._lastX = e.pageX; - this._lastY = e.pageY; - }else{ - this.onDragDetected(e); - } - dojo.stopEvent(e); - }, - onMouseMove: function(e){ - // summary: - // event processor for onmousemove, used only for delayed drags - // e: Event - // mouse event - if(Math.abs(e.pageX - this._lastX) > this.delay || Math.abs(e.pageY - this._lastY) > this.delay){ - this.onMouseUp(e); - this.onDragDetected(e); - } - dojo.stopEvent(e); - }, - onMouseUp: function(e){ - // summary: - // event processor for onmouseup, used only for delayed drags - // e: Event - // mouse event - for(var i = 0; i < 2; ++i){ - dojo.disconnect(this.events.pop()); - } - dojo.stopEvent(e); - }, - onSelectStart: function(e){ - // summary: - // event processor for onselectevent and ondragevent - // e: Event - // mouse event - if(!this.skip || !dojo.dnd.isFormElement(e)){ - dojo.stopEvent(e); - } - }, - - // local events - onDragDetected: function(/* Event */ e){ - // summary: - // called when the drag is detected; - // responsible for creation of the mover - new this.mover(this.node, e, this); - }, - onMoveStart: function(/* dojo.dnd.Mover */ mover){ - // summary: - // called before every move operation - dojo.publish("/dnd/move/start", [mover]); - dojo.addClass(dojo.body(), "dojoMove"); - dojo.addClass(this.node, "dojoMoveItem"); - }, - onMoveStop: function(/* dojo.dnd.Mover */ mover){ - // summary: - // called after every move operation - dojo.publish("/dnd/move/stop", [mover]); - dojo.removeClass(dojo.body(), "dojoMove"); - dojo.removeClass(this.node, "dojoMoveItem"); - }, - onFirstMove: function(/* dojo.dnd.Mover */ mover, /* Event */ e){ - // summary: - // called during the very first move notification; - // can be used to initialize coordinates, can be overwritten. - - // default implementation does nothing - }, - onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop, /* Event */ e){ - // summary: - // called during every move notification; - // should actually move the node; can be overwritten. - this.onMoving(mover, leftTop); - var s = mover.node.style; - s.left = leftTop.l + "px"; - s.top = leftTop.t + "px"; - this.onMoved(mover, leftTop); - }, - onMoving: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){ - // summary: - // called before every incremental move; can be overwritten. - - // default implementation does nothing - }, - onMoved: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){ - // summary: - // called after every incremental move; can be overwritten. - - // default implementation does nothing - } -}); - -} - -if(!dojo._hasResource["dojo.dnd.move"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.dnd.move"] = true; -dojo.provide("dojo.dnd.move"); - - - - -/*===== -dojo.declare("dojo.dnd.move.__constrainedMoveableArgs", [dojo.dnd.__MoveableArgs], { - // constraints: Function - // Calculates a constraint box. - // It is called in a context of the moveable object. - constraints: function(){}, - - // within: Boolean - // restrict move within boundaries. - within: false -}); -=====*/ - -dojo.declare("dojo.dnd.move.constrainedMoveable", dojo.dnd.Moveable, { - // object attributes (for markup) - constraints: function(){}, - within: false, - - // markup methods - markupFactory: function(params, node){ - return new dojo.dnd.move.constrainedMoveable(node, params); - }, - - constructor: function(node, params){ - // summary: - // an object that makes a node moveable - // node: Node - // a node (or node's id) to be moved - // params: dojo.dnd.move.__constrainedMoveableArgs? - // an optional object with additional parameters; - // the rest is passed to the base class - if(!params){ params = {}; } - this.constraints = params.constraints; - this.within = params.within; - }, - onFirstMove: function(/* dojo.dnd.Mover */ mover){ - // summary: - // called during the very first move notification; - // can be used to initialize coordinates, can be overwritten. - var c = this.constraintBox = this.constraints.call(this, mover); - c.r = c.l + c.w; - c.b = c.t + c.h; - if(this.within){ - var mb = dojo.marginBox(mover.node); - c.r -= mb.w; - c.b -= mb.h; - } - }, - onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){ - // summary: - // called during every move notification; - // should actually move the node; can be overwritten. - var c = this.constraintBox, s = mover.node.style; - s.left = (leftTop.l < c.l ? c.l : c.r < leftTop.l ? c.r : leftTop.l) + "px"; - s.top = (leftTop.t < c.t ? c.t : c.b < leftTop.t ? c.b : leftTop.t) + "px"; - } -}); - -/*===== -dojo.declare("dojo.dnd.move.__boxConstrainedMoveableArgs", [dojo.dnd.move.__constrainedMoveableArgs], { - // box: Object - // a constraint box - box: {} -}); -=====*/ - -dojo.declare("dojo.dnd.move.boxConstrainedMoveable", dojo.dnd.move.constrainedMoveable, { - // box: - // object attributes (for markup) - box: {}, - - // markup methods - markupFactory: function(params, node){ - return new dojo.dnd.move.boxConstrainedMoveable(node, params); - }, - - constructor: function(node, params){ - // summary: - // an object, which makes a node moveable - // node: Node - // a node (or node's id) to be moved - // params: dojo.dnd.move.__boxConstrainedMoveableArgs? - // an optional object with parameters - var box = params && params.box; - this.constraints = function(){ return box; }; - } -}); - -/*===== -dojo.declare("dojo.dnd.move.__parentConstrainedMoveableArgs", [dojo.dnd.move.__constrainedMoveableArgs], { - // area: String - // A parent's area to restrict the move. - // Can be "margin", "border", "padding", or "content". - area: "" -}); -=====*/ - -dojo.declare("dojo.dnd.move.parentConstrainedMoveable", dojo.dnd.move.constrainedMoveable, { - // area: - // object attributes (for markup) - area: "content", - - // markup methods - markupFactory: function(params, node){ - return new dojo.dnd.move.parentConstrainedMoveable(node, params); - }, - - constructor: function(node, params){ - // summary: - // an object, which makes a node moveable - // node: Node - // a node (or node's id) to be moved - // params: dojo.dnd.move.__parentConstrainedMoveableArgs? - // an optional object with parameters - var area = params && params.area; - this.constraints = function(){ - var n = this.node.parentNode, - s = dojo.getComputedStyle(n), - mb = dojo._getMarginBox(n, s); - if(area == "margin"){ - return mb; // Object - } - var t = dojo._getMarginExtents(n, s); - mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h; - if(area == "border"){ - return mb; // Object - } - t = dojo._getBorderExtents(n, s); - mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h; - if(area == "padding"){ - return mb; // Object - } - t = dojo._getPadExtents(n, s); - mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h; - return mb; // Object - }; - } -}); - -// WARNING: below are obsolete objects, instead of custom movers use custom moveables (above) - -dojo.dnd.move.constrainedMover = function(fun, within){ - // summary: - // returns a constrained version of dojo.dnd.Mover - // description: - // this function produces n object, which will put a constraint on - // the margin box of dragged object in absolute coordinates - // fun: Function - // called on drag, and returns a constraint box - // within: Boolean - // if true, constraints the whole dragged object withtin the rectangle, - // otherwise the constraint is applied to the left-top corner - - dojo.deprecated("dojo.dnd.move.constrainedMover, use dojo.dnd.move.constrainedMoveable instead"); - var mover = function(node, e, notifier){ - dojo.dnd.Mover.call(this, node, e, notifier); - }; - dojo.extend(mover, dojo.dnd.Mover.prototype); - dojo.extend(mover, { - onMouseMove: function(e){ - // summary: event processor for onmousemove - // e: Event: mouse event - dojo.dnd.autoScroll(e); - var m = this.marginBox, c = this.constraintBox, - l = m.l + e.pageX, t = m.t + e.pageY; - l = l < c.l ? c.l : c.r < l ? c.r : l; - t = t < c.t ? c.t : c.b < t ? c.b : t; - this.host.onMove(this, {l: l, t: t}); - }, - onFirstMove: function(){ - // summary: called once to initialize things; it is meant to be called only once - dojo.dnd.Mover.prototype.onFirstMove.call(this); - var c = this.constraintBox = fun.call(this); - c.r = c.l + c.w; - c.b = c.t + c.h; - if(within){ - var mb = dojo.marginBox(this.node); - c.r -= mb.w; - c.b -= mb.h; - } - } - }); - return mover; // Object -}; - -dojo.dnd.move.boxConstrainedMover = function(box, within){ - // summary: - // a specialization of dojo.dnd.constrainedMover, which constrains to the specified box - // box: Object - // a constraint box (l, t, w, h) - // within: Boolean - // if true, constraints the whole dragged object withtin the rectangle, - // otherwise the constraint is applied to the left-top corner - - dojo.deprecated("dojo.dnd.move.boxConstrainedMover, use dojo.dnd.move.boxConstrainedMoveable instead"); - return dojo.dnd.move.constrainedMover(function(){ return box; }, within); // Object -}; - -dojo.dnd.move.parentConstrainedMover = function(area, within){ - // summary: - // a specialization of dojo.dnd.constrainedMover, which constrains to the parent node - // area: String - // "margin" to constrain within the parent's margin box, "border" for the border box, - // "padding" for the padding box, and "content" for the content box; "content" is the default value. - // within: Boolean - // if true, constraints the whole dragged object within the rectangle, - // otherwise the constraint is applied to the left-top corner - - dojo.deprecated("dojo.dnd.move.parentConstrainedMover, use dojo.dnd.move.parentConstrainedMoveable instead"); - var fun = function(){ - var n = this.node.parentNode, - s = dojo.getComputedStyle(n), - mb = dojo._getMarginBox(n, s); - if(area == "margin"){ - return mb; // Object - } - var t = dojo._getMarginExtents(n, s); - mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h; - if(area == "border"){ - return mb; // Object - } - t = dojo._getBorderExtents(n, s); - mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h; - if(area == "padding"){ - return mb; // Object - } - t = dojo._getPadExtents(n, s); - mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h; - return mb; // Object - }; - return dojo.dnd.move.constrainedMover(fun, within); // Object -}; - -// patching functions one level up for compatibility - -dojo.dnd.constrainedMover = dojo.dnd.move.constrainedMover; -dojo.dnd.boxConstrainedMover = dojo.dnd.move.boxConstrainedMover; -dojo.dnd.parentConstrainedMover = dojo.dnd.move.parentConstrainedMover; - -} - -if(!dojo._hasResource["dojo.dnd.TimedMoveable"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.dnd.TimedMoveable"] = true; -dojo.provide("dojo.dnd.TimedMoveable"); - - - -/*===== -dojo.declare("dojo.dnd.__TimedMoveableArgs", [dojo.dnd.__MoveableArgs], { - // timeout: Number - // delay move by this number of ms, - // accumulating position changes during the timeout - timeout: 0 -}); -=====*/ - -(function(){ - // precalculate long expressions - var oldOnMove = dojo.dnd.Moveable.prototype.onMove; - - dojo.declare("dojo.dnd.TimedMoveable", dojo.dnd.Moveable, { - // summary: - // A specialized version of Moveable to support an FPS throttling. - // This class puts an upper restriction on FPS, which may reduce - // the CPU load. The additional parameter "timeout" regulates - // the delay before actually moving the moveable object. - - // object attributes (for markup) - timeout: 40, // in ms, 40ms corresponds to 25 fps - - constructor: function(node, params){ - // summary: - // an object that makes a node moveable with a timer - // node: Node||String - // a node (or node's id) to be moved - // params: dojo.dnd.__TimedMoveableArgs - // object with additional parameters. - - // sanitize parameters - if(!params){ params = {}; } - if(params.timeout && typeof params.timeout == "number" && params.timeout >= 0){ - this.timeout = params.timeout; - } - }, - - // markup methods - markupFactory: function(params, node){ - return new dojo.dnd.TimedMoveable(node, params); - }, - - onMoveStop: function(/* dojo.dnd.Mover */ mover){ - if(mover._timer){ - // stop timer - clearTimeout(mover._timer) - // reflect the last received position - oldOnMove.call(this, mover, mover._leftTop) - } - dojo.dnd.Moveable.prototype.onMoveStop.apply(this, arguments); - }, - onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){ - mover._leftTop = leftTop; - if(!mover._timer){ - var _t = this; // to avoid using dojo.hitch() - mover._timer = setTimeout(function(){ - // we don't have any pending requests - mover._timer = null; - // reflect the last received position - oldOnMove.call(_t, mover, mover._leftTop); - }, this.timeout); - } - } - }); -})(); - -} - -if(!dojo._hasResource["dojo.fx.Toggler"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.fx.Toggler"] = true; -dojo.provide("dojo.fx.Toggler"); - -dojo.declare("dojo.fx.Toggler", null, { - // summary: - // A simple `dojo.Animation` toggler API. - // - // description: - // class constructor for an animation toggler. It accepts a packed - // set of arguments about what type of animation to use in each - // direction, duration, etc. All available members are mixed into - // these animations from the constructor (for example, `node`, - // `showDuration`, `hideDuration`). - // - // example: - // | var t = new dojo.fx.Toggler({ - // | node: "nodeId", - // | showDuration: 500, - // | // hideDuration will default to "200" - // | showFunc: dojo.fx.wipeIn, - // | // hideFunc will default to "fadeOut" - // | }); - // | t.show(100); // delay showing for 100ms - // | // ...time passes... - // | t.hide(); - - // node: DomNode - // the node to target for the showing and hiding animations - node: null, - - // showFunc: Function - // The function that returns the `dojo.Animation` to show the node - showFunc: dojo.fadeIn, - - // hideFunc: Function - // The function that returns the `dojo.Animation` to hide the node - hideFunc: dojo.fadeOut, - - // showDuration: - // Time in milliseconds to run the show Animation - showDuration: 200, - - // hideDuration: - // Time in milliseconds to run the hide Animation - hideDuration: 200, - - // FIXME: need a policy for where the toggler should "be" the next - // time show/hide are called if we're stopped somewhere in the - // middle. - // FIXME: also would be nice to specify individual showArgs/hideArgs mixed into - // each animation individually. - // FIXME: also would be nice to have events from the animations exposed/bridged - - /*===== - _showArgs: null, - _showAnim: null, - - _hideArgs: null, - _hideAnim: null, - - _isShowing: false, - _isHiding: false, - =====*/ - - constructor: function(args){ - var _t = this; - - dojo.mixin(_t, args); - _t.node = args.node; - _t._showArgs = dojo.mixin({}, args); - _t._showArgs.node = _t.node; - _t._showArgs.duration = _t.showDuration; - _t.showAnim = _t.showFunc(_t._showArgs); - - _t._hideArgs = dojo.mixin({}, args); - _t._hideArgs.node = _t.node; - _t._hideArgs.duration = _t.hideDuration; - _t.hideAnim = _t.hideFunc(_t._hideArgs); - - dojo.connect(_t.showAnim, "beforeBegin", dojo.hitch(_t.hideAnim, "stop", true)); - dojo.connect(_t.hideAnim, "beforeBegin", dojo.hitch(_t.showAnim, "stop", true)); - }, - - show: function(delay){ - // summary: Toggle the node to showing - // delay: Integer? - // Ammount of time to stall playing the show animation - return this.showAnim.play(delay || 0); - }, - - hide: function(delay){ - // summary: Toggle the node to hidden - // delay: Integer? - // Ammount of time to stall playing the hide animation - return this.hideAnim.play(delay || 0); - } -}); - -} - -if(!dojo._hasResource["dojo.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.fx"] = true; -dojo.provide("dojo.fx"); - // FIXME: remove this back-compat require in 2.0 -/*===== -dojo.fx = { - // summary: Effects library on top of Base animations -}; -=====*/ -(function(){ - - var d = dojo, - _baseObj = { - _fire: function(evt, args){ - if(this[evt]){ - this[evt].apply(this, args||[]); - } - return this; - } - }; - - var _chain = function(animations){ - this._index = -1; - this._animations = animations||[]; - this._current = this._onAnimateCtx = this._onEndCtx = null; - - this.duration = 0; - d.forEach(this._animations, function(a){ - this.duration += a.duration; - if(a.delay){ this.duration += a.delay; } - }, this); - }; - d.extend(_chain, { - _onAnimate: function(){ - this._fire("onAnimate", arguments); - }, - _onEnd: function(){ - d.disconnect(this._onAnimateCtx); - d.disconnect(this._onEndCtx); - this._onAnimateCtx = this._onEndCtx = null; - if(this._index + 1 == this._animations.length){ - this._fire("onEnd"); - }else{ - // switch animations - this._current = this._animations[++this._index]; - this._onAnimateCtx = d.connect(this._current, "onAnimate", this, "_onAnimate"); - this._onEndCtx = d.connect(this._current, "onEnd", this, "_onEnd"); - this._current.play(0, true); - } - }, - play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){ - if(!this._current){ this._current = this._animations[this._index = 0]; } - if(!gotoStart && this._current.status() == "playing"){ return this; } - var beforeBegin = d.connect(this._current, "beforeBegin", this, function(){ - this._fire("beforeBegin"); - }), - onBegin = d.connect(this._current, "onBegin", this, function(arg){ - this._fire("onBegin", arguments); - }), - onPlay = d.connect(this._current, "onPlay", this, function(arg){ - this._fire("onPlay", arguments); - d.disconnect(beforeBegin); - d.disconnect(onBegin); - d.disconnect(onPlay); - }); - if(this._onAnimateCtx){ - d.disconnect(this._onAnimateCtx); - } - this._onAnimateCtx = d.connect(this._current, "onAnimate", this, "_onAnimate"); - if(this._onEndCtx){ - d.disconnect(this._onEndCtx); - } - this._onEndCtx = d.connect(this._current, "onEnd", this, "_onEnd"); - this._current.play.apply(this._current, arguments); - return this; - }, - pause: function(){ - if(this._current){ - var e = d.connect(this._current, "onPause", this, function(arg){ - this._fire("onPause", arguments); - d.disconnect(e); - }); - this._current.pause(); - } - return this; - }, - gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){ - this.pause(); - var offset = this.duration * percent; - this._current = null; - d.some(this._animations, function(a){ - if(a.duration <= offset){ - this._current = a; - return true; - } - offset -= a.duration; - return false; - }); - if(this._current){ - this._current.gotoPercent(offset / this._current.duration, andPlay); - } - return this; - }, - stop: function(/*boolean?*/ gotoEnd){ - if(this._current){ - if(gotoEnd){ - for(; this._index + 1 < this._animations.length; ++this._index){ - this._animations[this._index].stop(true); - } - this._current = this._animations[this._index]; - } - var e = d.connect(this._current, "onStop", this, function(arg){ - this._fire("onStop", arguments); - d.disconnect(e); - }); - this._current.stop(); - } - return this; - }, - status: function(){ - return this._current ? this._current.status() : "stopped"; - }, - destroy: function(){ - if(this._onAnimateCtx){ d.disconnect(this._onAnimateCtx); } - if(this._onEndCtx){ d.disconnect(this._onEndCtx); } - } - }); - d.extend(_chain, _baseObj); - - dojo.fx.chain = function(/*dojo.Animation[]*/ animations){ - // summary: - // Chain a list of `dojo.Animation`s to run in sequence - // - // description: - // Return a `dojo.Animation` which will play all passed - // `dojo.Animation` instances in sequence, firing its own - // synthesized events simulating a single animation. (eg: - // onEnd of this animation means the end of the chain, - // not the individual animations within) - // - // example: - // Once `node` is faded out, fade in `otherNode` - // | dojo.fx.chain([ - // | dojo.fadeIn({ node:node }), - // | dojo.fadeOut({ node:otherNode }) - // | ]).play(); - // - return new _chain(animations) // dojo.Animation - }; - - var _combine = function(animations){ - this._animations = animations||[]; - this._connects = []; - this._finished = 0; - - this.duration = 0; - d.forEach(animations, function(a){ - var duration = a.duration; - if(a.delay){ duration += a.delay; } - if(this.duration < duration){ this.duration = duration; } - this._connects.push(d.connect(a, "onEnd", this, "_onEnd")); - }, this); - - this._pseudoAnimation = new d.Animation({curve: [0, 1], duration: this.duration}); - var self = this; - d.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop", "onEnd"], - function(evt){ - self._connects.push(d.connect(self._pseudoAnimation, evt, - function(){ self._fire(evt, arguments); } - )); - } - ); - }; - d.extend(_combine, { - _doAction: function(action, args){ - d.forEach(this._animations, function(a){ - a[action].apply(a, args); - }); - return this; - }, - _onEnd: function(){ - if(++this._finished > this._animations.length){ - this._fire("onEnd"); - } - }, - _call: function(action, args){ - var t = this._pseudoAnimation; - t[action].apply(t, args); - }, - play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){ - this._finished = 0; - this._doAction("play", arguments); - this._call("play", arguments); - return this; - }, - pause: function(){ - this._doAction("pause", arguments); - this._call("pause", arguments); - return this; - }, - gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){ - var ms = this.duration * percent; - d.forEach(this._animations, function(a){ - a.gotoPercent(a.duration < ms ? 1 : (ms / a.duration), andPlay); - }); - this._call("gotoPercent", arguments); - return this; - }, - stop: function(/*boolean?*/ gotoEnd){ - this._doAction("stop", arguments); - this._call("stop", arguments); - return this; - }, - status: function(){ - return this._pseudoAnimation.status(); - }, - destroy: function(){ - d.forEach(this._connects, dojo.disconnect); - } - }); - d.extend(_combine, _baseObj); - - dojo.fx.combine = function(/*dojo.Animation[]*/ animations){ - // summary: - // Combine a list of `dojo.Animation`s to run in parallel - // - // description: - // Combine an array of `dojo.Animation`s to run in parallel, - // providing a new `dojo.Animation` instance encompasing each - // animation, firing standard animation events. - // - // example: - // Fade out `node` while fading in `otherNode` simultaneously - // | dojo.fx.combine([ - // | dojo.fadeIn({ node:node }), - // | dojo.fadeOut({ node:otherNode }) - // | ]).play(); - // - // example: - // When the longest animation ends, execute a function: - // | var anim = dojo.fx.combine([ - // | dojo.fadeIn({ node: n, duration:700 }), - // | dojo.fadeOut({ node: otherNode, duration: 300 }) - // | ]); - // | dojo.connect(anim, "onEnd", function(){ - // | // overall animation is done. - // | }); - // | anim.play(); // play the animation - // - return new _combine(animations); // dojo.Animation - }; - - dojo.fx.wipeIn = function(/*Object*/ args){ - // summary: - // Expand a node to it's natural height. - // - // description: - // Returns an animation that will expand the - // node defined in 'args' object from it's current height to - // it's natural height (with no scrollbar). - // Node must have no margin/border/padding. - // - // args: Object - // A hash-map of standard `dojo.Animation` constructor properties - // (such as easing: node: duration: and so on) - // - // example: - // | dojo.fx.wipeIn({ - // | node:"someId" - // | }).play() - var node = args.node = d.byId(args.node), s = node.style, o; - - var anim = d.animateProperty(d.mixin({ - properties: { - height: { - // wrapped in functions so we wait till the last second to query (in case value has changed) - start: function(){ - // start at current [computed] height, but use 1px rather than 0 - // because 0 causes IE to display the whole panel - o = s.overflow; - s.overflow = "hidden"; - if(s.visibility == "hidden" || s.display == "none"){ - s.height = "1px"; - s.display = ""; - s.visibility = ""; - return 1; - }else{ - var height = d.style(node, "height"); - return Math.max(height, 1); - } - }, - end: function(){ - return node.scrollHeight; - } - } - } - }, args)); - - d.connect(anim, "onEnd", function(){ - s.height = "auto"; - s.overflow = o; - }); - - return anim; // dojo.Animation - } - - dojo.fx.wipeOut = function(/*Object*/ args){ - // summary: - // Shrink a node to nothing and hide it. - // - // description: - // Returns an animation that will shrink node defined in "args" - // from it's current height to 1px, and then hide it. - // - // args: Object - // A hash-map of standard `dojo.Animation` constructor properties - // (such as easing: node: duration: and so on) - // - // example: - // | dojo.fx.wipeOut({ node:"someId" }).play() - - var node = args.node = d.byId(args.node), s = node.style, o; - - var anim = d.animateProperty(d.mixin({ - properties: { - height: { - end: 1 // 0 causes IE to display the whole panel - } - } - }, args)); - - d.connect(anim, "beforeBegin", function(){ - o = s.overflow; - s.overflow = "hidden"; - s.display = ""; - }); - d.connect(anim, "onEnd", function(){ - s.overflow = o; - s.height = "auto"; - s.display = "none"; - }); - - return anim; // dojo.Animation - } - - dojo.fx.slideTo = function(/*Object*/ args){ - // summary: - // Slide a node to a new top/left position - // - // description: - // Returns an animation that will slide "node" - // defined in args Object from its current position to - // the position defined by (args.left, args.top). - // - // args: Object - // A hash-map of standard `dojo.Animation` constructor properties - // (such as easing: node: duration: and so on). Special args members - // are `top` and `left`, which indicate the new position to slide to. - // - // example: - // | dojo.fx.slideTo({ node: node, left:"40", top:"50", units:"px" }).play() - - var node = args.node = d.byId(args.node), - top = null, left = null; - - var init = (function(n){ - return function(){ - var cs = d.getComputedStyle(n); - var pos = cs.position; - top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0); - left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0); - if(pos != 'absolute' && pos != 'relative'){ - var ret = d.position(n, true); - top = ret.y; - left = ret.x; - n.style.position="absolute"; - n.style.top=top+"px"; - n.style.left=left+"px"; - } - }; - })(node); - init(); - - var anim = d.animateProperty(d.mixin({ - properties: { - top: args.top || 0, - left: args.left || 0 - } - }, args)); - d.connect(anim, "beforeBegin", anim, init); - - return anim; // dojo.Animation - } - -})(); - -} - -if(!dojo._hasResource["dijit.form._FormMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form._FormMixin"] = true; -dojo.provide("dijit.form._FormMixin"); - - - -dojo.declare("dijit.form._FormMixin", null, - { - // summary: - // Mixin for containers of form widgets (i.e. widgets that represent a single value - // and can be children of a <form> node or dijit.form.Form widget) - // description: - // Can extract all the form widgets - // values and combine them into a single javascript object, or alternately - // take such an object and set the values for all the contained - // form widgets - -/*===== - // value: Object - // Name/value hash for each child widget with a name and value. - // Child widgets without names are not part of the hash. - // - // If there are multiple child widgets w/the same name, value is an array, - // unless they are radio buttons in which case value is a scalar (since only - // one radio button can be checked at a time). - // - // If a child widget's name is a dot separated list (like a.b.c.d), it's a nested structure. - // - // Example: - // | { name: "John Smith", interests: ["sports", "movies"] } -=====*/ - - // TODO: - // * Repeater - // * better handling for arrays. Often form elements have names with [] like - // * people[3].sex (for a list of people [{name: Bill, sex: M}, ...]) - // - // - - reset: function(){ - dojo.forEach(this.getDescendants(), function(widget){ - if(widget.reset){ - widget.reset(); - } - }); - }, - - validate: function(){ - // summary: - // returns if the form is valid - same as isValid - but - // provides a few additional (ui-specific) features. - // 1 - it will highlight any sub-widgets that are not - // valid - // 2 - it will call focus() on the first invalid - // sub-widget - var didFocus = false; - return dojo.every(dojo.map(this.getDescendants(), function(widget){ - // Need to set this so that "required" widgets get their - // state set. - widget._hasBeenBlurred = true; - var valid = widget.disabled || !widget.validate || widget.validate(); - if(!valid && !didFocus){ - // Set focus of the first non-valid widget - dojo.window.scrollIntoView(widget.containerNode || widget.domNode); - widget.focus(); - didFocus = true; - } - return valid; - }), function(item){ return item; }); - }, - - setValues: function(val){ - dojo.deprecated(this.declaredClass+"::setValues() is deprecated. Use set('value', val) instead.", "", "2.0"); - return this.set('value', val); - }, - _setValueAttr: function(/*object*/obj){ - // summary: - // Fill in form values from according to an Object (in the format returned by attr('value')) - - // generate map from name --> [list of widgets with that name] - var map = { }; - dojo.forEach(this.getDescendants(), function(widget){ - if(!widget.name){ return; } - var entry = map[widget.name] || (map[widget.name] = [] ); - entry.push(widget); - }); - - for(var name in map){ - if(!map.hasOwnProperty(name)){ - continue; - } - var widgets = map[name], // array of widgets w/this name - values = dojo.getObject(name, false, obj); // list of values for those widgets - - if(values === undefined){ - continue; - } - if(!dojo.isArray(values)){ - values = [ values ]; - } - if(typeof widgets[0].checked == 'boolean'){ - // for checkbox/radio, values is a list of which widgets should be checked - dojo.forEach(widgets, function(w, i){ - w.set('value', dojo.indexOf(values, w.value) != -1); - }); - }else if(widgets[0].multiple){ - // it takes an array (e.g. multi-select) - widgets[0].set('value', values); - }else{ - // otherwise, values is a list of values to be assigned sequentially to each widget - dojo.forEach(widgets, function(w, i){ - w.set('value', values[i]); - }); - } - } - - /*** - * TODO: code for plain input boxes (this shouldn't run for inputs that are part of widgets) - - dojo.forEach(this.containerNode.elements, function(element){ - if(element.name == ''){return}; // like "continue" - var namePath = element.name.split("."); - var myObj=obj; - var name=namePath[namePath.length-1]; - for(var j=1,len2=namePath.length;j<len2;++j){ - var p=namePath[j - 1]; - // repeater support block - var nameA=p.split("["); - if(nameA.length > 1){ - if(typeof(myObj[nameA[0]]) == "undefined"){ - myObj[nameA[0]]=[ ]; - } // if - - nameIndex=parseInt(nameA[1]); - if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){ - myObj[nameA[0]][nameIndex] = { }; - } - myObj=myObj[nameA[0]][nameIndex]; - continue; - } // repeater support ends - - if(typeof(myObj[p]) == "undefined"){ - myObj=undefined; - break; - }; - myObj=myObj[p]; - } - - if(typeof(myObj) == "undefined"){ - return; // like "continue" - } - if(typeof(myObj[name]) == "undefined" && this.ignoreNullValues){ - return; // like "continue" - } - - // TODO: widget values (just call attr('value', ...) on the widget) - - // TODO: maybe should call dojo.getNodeProp() instead - switch(element.type){ - case "checkbox": - element.checked = (name in myObj) && - dojo.some(myObj[name], function(val){ return val == element.value; }); - break; - case "radio": - element.checked = (name in myObj) && myObj[name] == element.value; - break; - case "select-multiple": - element.selectedIndex=-1; - dojo.forEach(element.options, function(option){ - option.selected = dojo.some(myObj[name], function(val){ return option.value == val; }); - }); - break; - case "select-one": - element.selectedIndex="0"; - dojo.forEach(element.options, function(option){ - option.selected = option.value == myObj[name]; - }); - break; - case "hidden": - case "text": - case "textarea": - case "password": - element.value = myObj[name] || ""; - break; - } - }); - */ - }, - - getValues: function(){ - dojo.deprecated(this.declaredClass+"::getValues() is deprecated. Use get('value') instead.", "", "2.0"); - return this.get('value'); - }, - _getValueAttr: function(){ - // summary: - // Returns Object representing form values. - // description: - // Returns name/value hash for each form element. - // If there are multiple elements w/the same name, value is an array, - // unless they are radio buttons in which case value is a scalar since only - // one can be checked at a time. - // - // If the name is a dot separated list (like a.b.c.d), creates a nested structure. - // Only works on widget form elements. - // example: - // | { name: "John Smith", interests: ["sports", "movies"] } - - // get widget values - var obj = { }; - dojo.forEach(this.getDescendants(), function(widget){ - var name = widget.name; - if(!name || widget.disabled){ return; } - - // Single value widget (checkbox, radio, or plain <input> type widget - var value = widget.get('value'); - - // Store widget's value(s) as a scalar, except for checkboxes which are automatically arrays - if(typeof widget.checked == 'boolean'){ - if(/Radio/.test(widget.declaredClass)){ - // radio button - if(value !== false){ - dojo.setObject(name, value, obj); - }else{ - // give radio widgets a default of null - value = dojo.getObject(name, false, obj); - if(value === undefined){ - dojo.setObject(name, null, obj); - } - } - }else{ - // checkbox/toggle button - var ary=dojo.getObject(name, false, obj); - if(!ary){ - ary=[]; - dojo.setObject(name, ary, obj); - } - if(value !== false){ - ary.push(value); - } - } - }else{ - var prev=dojo.getObject(name, false, obj); - if(typeof prev != "undefined"){ - if(dojo.isArray(prev)){ - prev.push(value); - }else{ - dojo.setObject(name, [prev, value], obj); - } - }else{ - // unique name - dojo.setObject(name, value, obj); - } - } - }); - - /*** - * code for plain input boxes (see also dojo.formToObject, can we use that instead of this code? - * but it doesn't understand [] notation, presumably) - var obj = { }; - dojo.forEach(this.containerNode.elements, function(elm){ - if(!elm.name) { - return; // like "continue" - } - var namePath = elm.name.split("."); - var myObj=obj; - var name=namePath[namePath.length-1]; - for(var j=1,len2=namePath.length;j<len2;++j){ - var nameIndex = null; - var p=namePath[j - 1]; - var nameA=p.split("["); - if(nameA.length > 1){ - if(typeof(myObj[nameA[0]]) == "undefined"){ - myObj[nameA[0]]=[ ]; - } // if - nameIndex=parseInt(nameA[1]); - if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){ - myObj[nameA[0]][nameIndex] = { }; - } - } else if(typeof(myObj[nameA[0]]) == "undefined"){ - myObj[nameA[0]] = { } - } // if - - if(nameA.length == 1){ - myObj=myObj[nameA[0]]; - } else{ - myObj=myObj[nameA[0]][nameIndex]; - } // if - } // for - - if((elm.type != "select-multiple" && elm.type != "checkbox" && elm.type != "radio") || (elm.type == "radio" && elm.checked)){ - if(name == name.split("[")[0]){ - myObj[name]=elm.value; - } else{ - // can not set value when there is no name - } - } else if(elm.type == "checkbox" && elm.checked){ - if(typeof(myObj[name]) == 'undefined'){ - myObj[name]=[ ]; - } - myObj[name].push(elm.value); - } else if(elm.type == "select-multiple"){ - if(typeof(myObj[name]) == 'undefined'){ - myObj[name]=[ ]; - } - for(var jdx=0,len3=elm.options.length; jdx<len3; ++jdx){ - if(elm.options[jdx].selected){ - myObj[name].push(elm.options[jdx].value); - } - } - } // if - name=undefined; - }); // forEach - ***/ - return obj; - }, - - // TODO: ComboBox might need time to process a recently input value. This should be async? - isValid: function(){ - // summary: - // Returns true if all of the widgets are valid - - // This also populate this._invalidWidgets[] array with list of invalid widgets... - // TODO: put that into separate function? It's confusing to have that as a side effect - // of a method named isValid(). - - this._invalidWidgets = dojo.filter(this.getDescendants(), function(widget){ - return !widget.disabled && widget.isValid && !widget.isValid(); - }); - return !this._invalidWidgets.length; - }, - - - onValidStateChange: function(isValid){ - // summary: - // Stub function to connect to if you want to do something - // (like disable/enable a submit button) when the valid - // state changes on the form as a whole. - }, - - _widgetChange: function(widget){ - // summary: - // Connected to a widget's onChange function - update our - // valid state, if needed. - var isValid = this._lastValidState; - if(!widget || this._lastValidState === undefined){ - // We have passed a null widget, or we haven't been validated - // yet - let's re-check all our children - // This happens when we connect (or reconnect) our children - isValid = this.isValid(); - if(this._lastValidState === undefined){ - // Set this so that we don't fire an onValidStateChange - // the first time - this._lastValidState = isValid; - } - }else if(widget.isValid){ - this._invalidWidgets = dojo.filter(this._invalidWidgets || [], function(w){ - return (w != widget); - }, this); - if(!widget.isValid() && !widget.get("disabled")){ - this._invalidWidgets.push(widget); - } - isValid = (this._invalidWidgets.length === 0); - } - if(isValid !== this._lastValidState){ - this._lastValidState = isValid; - this.onValidStateChange(isValid); - } - }, - - connectChildren: function(){ - // summary: - // Connects to the onChange function of all children to - // track valid state changes. You can call this function - // directly, ex. in the event that you programmatically - // add a widget to the form *after* the form has been - // initialized. - dojo.forEach(this._changeConnections, dojo.hitch(this, "disconnect")); - var _this = this; - - // we connect to validate - so that it better reflects the states - // of the widgets - also, we only connect if it has a validate - // function (to avoid too many unneeded connections) - var conns = (this._changeConnections = []); - dojo.forEach(dojo.filter(this.getDescendants(), - function(item){ return item.validate; } - ), - function(widget){ - // We are interested in whenever the widget is validated - or - // whenever the disabled attribute on that widget is changed - conns.push(_this.connect(widget, "validate", - dojo.hitch(_this, "_widgetChange", widget))); - conns.push(_this.connect(widget, "_setDisabledAttr", - dojo.hitch(_this, "_widgetChange", widget))); - }); - - // Call the widget change function to update the valid state, in - // case something is different now. - this._widgetChange(null); - }, - - startup: function(){ - this.inherited(arguments); - // Initialize our valid state tracking. Needs to be done in startup - // because it's not guaranteed that our children are initialized - // yet. - this._changeConnections = []; - this.connectChildren(); - } - }); - -} - -if(!dojo._hasResource["dijit._DialogMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit._DialogMixin"] = true; -dojo.provide("dijit._DialogMixin"); - - - -dojo.declare("dijit._DialogMixin", null, - { - // summary: - // This provides functions useful to Dialog and TooltipDialog - - attributeMap: dijit._Widget.prototype.attributeMap, - - execute: function(/*Object*/ formContents){ - // summary: - // Callback when the user hits the submit button. - // Override this method to handle Dialog execution. - // description: - // After the user has pressed the submit button, the Dialog - // first calls onExecute() to notify the container to hide the - // dialog and restore focus to wherever it used to be. - // - // *Then* this method is called. - // type: - // callback - }, - - onCancel: function(){ - // summary: - // Called when user has pressed the Dialog's cancel button, to notify container. - // description: - // Developer shouldn't override or connect to this method; - // it's a private communication device between the TooltipDialog - // and the thing that opened it (ex: `dijit.form.DropDownButton`) - // type: - // protected - }, - - onExecute: function(){ - // summary: - // Called when user has pressed the dialog's OK button, to notify container. - // description: - // Developer shouldn't override or connect to this method; - // it's a private communication device between the TooltipDialog - // and the thing that opened it (ex: `dijit.form.DropDownButton`) - // type: - // protected - }, - - _onSubmit: function(){ - // summary: - // Callback when user hits submit button - // type: - // protected - this.onExecute(); // notify container that we are about to execute - this.execute(this.get('value')); - }, - - _getFocusItems: function(/*Node*/ dialogNode){ - // summary: - // Find focusable Items each time a dialog is opened, - // setting _firstFocusItem and _lastFocusItem - // tags: - // protected - - var elems = dijit._getTabNavigable(dojo.byId(dialogNode)); - this._firstFocusItem = elems.lowest || elems.first || dialogNode; - this._lastFocusItem = elems.last || elems.highest || this._firstFocusItem; - if(dojo.isMoz && this._firstFocusItem.tagName.toLowerCase() == "input" && - dojo.getNodeProp(this._firstFocusItem, "type").toLowerCase() == "file"){ - // FF doesn't behave well when first element is input type=file, set first focusable to dialog container - dojo.attr(dialogNode, "tabIndex", "0"); - this._firstFocusItem = dialogNode; - } - } - } -); - -} - -if(!dojo._hasResource["dijit.DialogUnderlay"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.DialogUnderlay"] = true; -dojo.provide("dijit.DialogUnderlay"); - - - - - - -dojo.declare( - "dijit.DialogUnderlay", - [dijit._Widget, dijit._Templated], - { - // summary: - // The component that blocks the screen behind a `dijit.Dialog` - // - // description: - // A component used to block input behind a `dijit.Dialog`. Only a single - // instance of this widget is created by `dijit.Dialog`, and saved as - // a reference to be shared between all Dialogs as `dijit._underlay` - // - // The underlay itself can be styled based on and id: - // | #myDialog_underlay { background-color:red; } - // - // In the case of `dijit.Dialog`, this id is based on the id of the Dialog, - // suffixed with _underlay. - - // Template has two divs; outer div is used for fade-in/fade-out, and also to hold background iframe. - // Inner div has opacity specified in CSS file. - templateString: "<div class='dijitDialogUnderlayWrapper'><div class='dijitDialogUnderlay' dojoAttachPoint='node'></div></div>", - - // Parameters on creation or updatable later - - // dialogId: String - // Id of the dialog.... DialogUnderlay's id is based on this id - dialogId: "", - - // class: String - // This class name is used on the DialogUnderlay node, in addition to dijitDialogUnderlay - "class": "", - - attributeMap: { id: "domNode" }, - - _setDialogIdAttr: function(id){ - dojo.attr(this.node, "id", id + "_underlay"); - }, - - _setClassAttr: function(clazz){ - this.node.className = "dijitDialogUnderlay " + clazz; - }, - - postCreate: function(){ - // summary: - // Append the underlay to the body - dojo.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 = dojo.window.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 dijit.BackgroundIframe(this.domNode); - }, - - hide: function(){ - // summary: - // Hides the dialog underlay - this.bgIframe.destroy(); - this.domNode.style.display = "none"; - }, - - uninitialize: function(){ - if(this.bgIframe){ - this.bgIframe.destroy(); - } - this.inherited(arguments); - } - } -); - -} - -if(!dojo._hasResource["dojo.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.html"] = true; -dojo.provide("dojo.html"); - -// the parser might be needed.. - - -(function(){ // private scope, sort of a namespace - - // idCounter is incremented with each instantiation to allow asignment of a unique id for tracking, logging purposes - var idCounter = 0, - d = dojo; - - dojo.html._secureForInnerHtml = function(/*String*/ cont){ - // summary: - // removes !DOCTYPE and title elements from the html string. - // - // khtml is picky about dom faults, you can't attach a style or <title> node as child of body - // must go into head, so we need to cut out those tags - // cont: - // An html string for insertion into the dom - // - return cont.replace(/(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig, ""); // String - }; - -/*==== - dojo.html._emptyNode = function(node){ - // summary: - // removes all child nodes from the given node - // node: DOMNode - // the parent element - }; -=====*/ - dojo.html._emptyNode = dojo.empty; - - dojo.html._setNodeContent = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont){ - // summary: - // inserts the given content into the given node - // node: - // the parent element - // content: - // the content to be set on the parent element. - // This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes - - // always empty - d.empty(node); - - if(cont) { - if(typeof cont == "string") { - cont = d._toDom(cont, node.ownerDocument); - } - if(!cont.nodeType && d.isArrayLike(cont)) { - // handle as enumerable, but it may shrink as we enumerate it - for(var startlen=cont.length, i=0; i<cont.length; i=startlen==cont.length ? i+1 : 0) { - d.place( cont[i], node, "last"); - } - } else { - // pass nodes, documentFragments and unknowns through to dojo.place - d.place(cont, node, "last"); - } - } - - // return DomNode - return node; - }; - - // we wrap up the content-setting operation in a object - dojo.declare("dojo.html._ContentSetter", null, - { - // node: DomNode|String - // An node which will be the parent element that we set content into - node: "", - - // content: String|DomNode|DomNode[] - // The content to be placed in the node. Can be an HTML string, a node reference, or a enumerable list of nodes - content: "", - - // id: String? - // Usually only used internally, and auto-generated with each instance - id: "", - - // cleanContent: Boolean - // Should the content be treated as a full html document, - // and the real content stripped of <html>, <body> wrapper before injection - cleanContent: false, - - // extractContent: Boolean - // Should the content be treated as a full html document, and the real content stripped of <html>, <body> wrapper before injection - extractContent: false, - - // parseContent: Boolean - // Should the node by passed to the parser after the new content is set - parseContent: false, - - // lifecyle methods - constructor: function(/* Object */params, /* String|DomNode */node){ - // summary: - // Provides a configurable, extensible object to wrap the setting on content on a node - // call the set() method to actually set the content.. - - // the original params are mixed directly into the instance "this" - dojo.mixin(this, params || {}); - - // give precedence to params.node vs. the node argument - // and ensure its a node, not an id string - node = this.node = dojo.byId( this.node || node ); - - if(!this.id){ - this.id = [ - "Setter", - (node) ? node.id || node.tagName : "", - idCounter++ - ].join("_"); - } - }, - set: function(/* String|DomNode|NodeList? */ cont, /* Object? */ params){ - // summary: - // front-end to the set-content sequence - // cont: - // An html string, node or enumerable list of nodes for insertion into the dom - // If not provided, the object's content property will be used - if(undefined !== cont){ - this.content = cont; - } - // in the re-use scenario, set needs to be able to mixin new configuration - if(params){ - this._mixin(params); - } - - this.onBegin(); - this.setContent(); - this.onEnd(); - - return this.node; - }, - setContent: function(){ - // summary: - // sets the content on the node - - var node = this.node; - if(!node) { - // can't proceed - throw new Error(this.declaredClass + ": setContent given no node"); - } - try{ - node = dojo.html._setNodeContent(node, this.content); - }catch(e){ - // check if a domfault occurs when we are appending this.errorMessage - // like for instance if domNode is a UL and we try append a DIV - - // FIXME: need to allow the user to provide a content error message string - var errMess = this.onContentError(e); - try{ - node.innerHTML = errMess; - }catch(e){ - console.error('Fatal ' + this.declaredClass + '.setContent could not change content due to '+e.message, e); - } - } - // always put back the node for the next method - this.node = node; // DomNode - }, - - empty: function() { - // summary - // cleanly empty out existing content - - // destroy any widgets from a previous run - // NOTE: if you dont want this you'll need to empty - // the parseResults array property yourself to avoid bad things happenning - if(this.parseResults && this.parseResults.length) { - dojo.forEach(this.parseResults, function(w) { - if(w.destroy){ - w.destroy(); - } - }); - delete this.parseResults; - } - // this is fast, but if you know its already empty or safe, you could - // override empty to skip this step - dojo.html._emptyNode(this.node); - }, - - onBegin: function(){ - // summary - // Called after instantiation, but before set(); - // It allows modification of any of the object properties - // - including the node and content provided - before the set operation actually takes place - // This default implementation checks for cleanContent and extractContent flags to - // optionally pre-process html string content - var cont = this.content; - - if(dojo.isString(cont)){ - if(this.cleanContent){ - cont = dojo.html._secureForInnerHtml(cont); - } - - if(this.extractContent){ - var match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im); - if(match){ cont = match[1]; } - } - } - - // clean out the node and any cruft associated with it - like widgets - this.empty(); - - this.content = cont; - return this.node; /* DomNode */ - }, - - onEnd: function(){ - // summary - // Called after set(), when the new content has been pushed into the node - // It provides an opportunity for post-processing before handing back the node to the caller - // This default implementation checks a parseContent flag to optionally run the dojo parser over the new content - if(this.parseContent){ - // populates this.parseResults if you need those.. - this._parse(); - } - return this.node; /* DomNode */ - }, - - tearDown: function(){ - // summary - // manually reset the Setter instance if its being re-used for example for another set() - // description - // tearDown() is not called automatically. - // In normal use, the Setter instance properties are simply allowed to fall out of scope - // but the tearDown method can be called to explicitly reset this instance. - delete this.parseResults; - delete this.node; - delete this.content; - }, - - onContentError: function(err){ - return "Error occured setting content: " + err; - }, - - _mixin: function(params){ - // mix properties/methods into the instance - // TODO: the intention with tearDown is to put the Setter's state - // back to that of the original constructor (vs. deleting/resetting everything regardless of ctor params) - // so we could do something here to move the original properties aside for later restoration - var empty = {}, key; - for(key in params){ - if(key in empty){ continue; } - // TODO: here's our opportunity to mask the properties we dont consider configurable/overridable - // .. but history shows we'll almost always guess wrong - this[key] = params[key]; - } - }, - _parse: function(){ - // summary: - // runs the dojo parser over the node contents, storing any results in this.parseResults - // Any errors resulting from parsing are passed to _onError for handling - - var rootNode = this.node; - try{ - // store the results (widgets, whatever) for potential retrieval - this.parseResults = dojo.parser.parse({ - rootNode: rootNode, - dir: this.dir, - lang: this.lang - }); - }catch(e){ - this._onError('Content', e, "Error parsing in _ContentSetter#"+this.id); - } - }, - - _onError: function(type, err, consoleText){ - // summary: - // shows user the string that is returned by on[type]Error - // overide/implement on[type]Error and return your own string to customize - var errText = this['on' + type + 'Error'].call(this, err); - if(consoleText){ - console.error(consoleText, err); - }else if(errText){ // a empty string won't change current content - dojo.html._setNodeContent(this.node, errText, true); - } - } - }); // end dojo.declare() - - dojo.html.set = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont, /* Object? */ params){ - // summary: - // inserts (replaces) the given content into the given node. dojo.place(cont, node, "only") - // may be a better choice for simple HTML insertion. - // description: - // Unless you need to use the params capabilities of this method, you should use - // dojo.place(cont, node, "only"). dojo.place() has more robust support for injecting - // an HTML string into the DOM, but it only handles inserting an HTML string as DOM - // elements, or inserting a DOM node. dojo.place does not handle NodeList insertions - // or the other capabilities as defined by the params object for this method. - // node: - // the parent element that will receive the content - // cont: - // the content to be set on the parent element. - // This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes - // params: - // Optional flags/properties to configure the content-setting. See dojo.html._ContentSetter - // example: - // A safe string/node/nodelist content replacement/injection with hooks for extension - // Example Usage: - // dojo.html.set(node, "some string"); - // dojo.html.set(node, contentNode, {options}); - // dojo.html.set(node, myNode.childNodes, {options}); - if(undefined == cont){ - console.warn("dojo.html.set: no cont argument provided, using empty string"); - cont = ""; - } - if(!params){ - // simple and fast - return dojo.html._setNodeContent(node, cont, true); - }else{ - // more options but slower - // note the arguments are reversed in order, to match the convention for instantiation via the parser - var op = new dojo.html._ContentSetter(dojo.mixin( - params, - { content: cont, node: node } - )); - return op.set(); - } - }; -})(); - -} - -if(!dojo._hasResource["dijit.layout.ContentPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.ContentPane"] = true; -dojo.provide("dijit.layout.ContentPane"); - - - - // for dijit.layout.marginBox2contentBox() - - - - - - -dojo.declare( - "dijit.layout.ContentPane", dijit._Widget, -{ - // summary: - // A widget that acts as a container for mixed HTML and widgets, and includes an Ajax interface - // description: - // A widget that can be used as a stand alone widget - // or as a base class for other widgets. - // - // Handles replacement of document fragment using either external uri or javascript - // generated markup or DOM content, instantiating widgets within that content. - // Don't confuse it with an iframe, it only needs/wants document fragments. - // It's useful as a child of LayoutContainer, SplitContainer, or TabContainer. - // But note that those classes can contain any widget as a child. - // example: - // Some quick samples: - // To change the innerHTML use .set('content', '<b>new content</b>') - // - // Or you can send it a NodeList, .set('content', dojo.query('div [class=selected]', userSelection)) - // please note that the nodes in NodeList will copied, not moved - // - // To do a ajax update use .set('href', url) - - // href: String - // The href of the content that displays now. - // Set this at construction if you want to load data externally when the - // pane is shown. (Set preload=true to load it immediately.) - // Changing href after creation doesn't have any effect; Use set('href', ...); - href: "", - -/*===== - // content: String || DomNode || NodeList || dijit._Widget - // The innerHTML of the ContentPane. - // Note that the initialization parameter / argument to attr("content", ...) - // can be a String, DomNode, Nodelist, or _Widget. - content: "", -=====*/ - - // extractContent: Boolean - // Extract visible content from inside of <body> .... </body>. - // I.e., strip <html> and <head> (and it's contents) from the href - extractContent: false, - - // parseOnLoad: Boolean - // Parse content and create the widgets, if any. - parseOnLoad: true, - - // preventCache: Boolean - // Prevent caching of data from href's by appending a timestamp to the href. - preventCache: false, - - // preload: Boolean - // Force load of data on initialization even if pane is hidden. - preload: false, - - // refreshOnShow: Boolean - // Refresh (re-download) content when pane goes from hidden to shown - refreshOnShow: false, - - // loadingMessage: String - // Message that shows while downloading - loadingMessage: "<span class='dijitContentPaneLoading'>${loadingState}</span>", - - // errorMessage: String - // Message that shows if an error occurs - errorMessage: "<span class='dijitContentPaneError'>${errorState}</span>", - - // isLoaded: [readonly] Boolean - // True if the ContentPane has data in it, either specified - // during initialization (via href or inline content), or set - // via attr('content', ...) / attr('href', ...) - // - // False if it doesn't have any content, or if ContentPane is - // still in the process of downloading href. - isLoaded: false, - - baseClass: "dijitContentPane", - - // doLayout: Boolean - // - false - don't adjust size of children - // - true - if there is a single visible child widget, set it's size to - // however big the ContentPane is - doLayout: true, - - // ioArgs: Object - // Parameters to pass to xhrGet() request, for example: - // | <div dojoType="dijit.layout.ContentPane" href="./bar" ioArgs="{timeout: 500}"> - ioArgs: {}, - - // isContainer: [protected] Boolean - // Indicates that this widget acts as a "parent" to the descendant widgets. - // When the parent is started it will call startup() on the child widgets. - // See also `isLayoutContainer`. - isContainer: true, - - // isLayoutContainer: [protected] Boolean - // Indicates that this widget will call resize() on it's child widgets - // when they become visible. - isLayoutContainer: true, - - // onLoadDeferred: [readonly] dojo.Deferred - // This is the `dojo.Deferred` returned by attr('href', ...) and refresh(). - // Calling onLoadDeferred.addCallback() or addErrback() registers your - // callback to be called only once, when the prior attr('href', ...) call or - // the initial href parameter to the constructor finishes loading. - // - // This is different than an onLoad() handler which gets called any time any href is loaded. - onLoadDeferred: null, - - // Override _Widget's attributeMap because we don't want the title attribute (used to specify - // tab labels) to be copied to ContentPane.domNode... otherwise a tooltip shows up over the - // entire pane. - attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, { - title: [] - }), - - postMixInProperties: function(){ - this.inherited(arguments); - var messages = dojo.i18n.getLocalization("dijit", "loading", this.lang); - this.loadingMessage = dojo.string.substitute(this.loadingMessage, messages); - this.errorMessage = dojo.string.substitute(this.errorMessage, messages); - - // Detect if we were initialized with data - if(!this.href && this.srcNodeRef && this.srcNodeRef.innerHTML){ - this.isLoaded = true; - } - }, - - buildRendering: function(){ - // Overrides Widget.buildRendering(). - // Since we have no template we need to set this.containerNode ourselves. - // For subclasses of ContentPane do have a template, does nothing. - this.inherited(arguments); - if(!this.containerNode){ - // make getDescendants() work - this.containerNode = this.domNode; - } - }, - - postCreate: function(){ - // remove the title attribute so it doesn't show up when hovering - // over a node - this.domNode.title = ""; - - if(!dojo.attr(this.domNode,"role")){ - dijit.setWaiRole(this.domNode, "group"); - } - - dojo.addClass(this.domNode, this.baseClass); - }, - - startup: function(){ - // summary: - // See `dijit.layout._LayoutWidget.startup` for description. - // Although ContentPane doesn't extend _LayoutWidget, it does implement - // the same API. - if(this._started){ return; } - - var parent = dijit._Contained.prototype.getParent.call(this); - this._childOfLayoutWidget = parent && parent.isLayoutContainer; - - // I need to call resize() on my child/children (when I become visible), unless - // I'm the child of a layout widget in which case my parent will call resize() on me and I'll do it then. - this._needLayout = !this._childOfLayoutWidget; - - if(this.isLoaded){ - dojo.forEach(this.getChildren(), function(child){ - child.startup(); - }); - } - - if(this._isShown() || this.preload){ - this._onShow(); - } - - this.inherited(arguments); - }, - - _checkIfSingleChild: function(){ - // summary: - // Test if we have exactly one visible widget as a child, - // and if so assume that we are a container for that widget, - // and should propogate startup() and resize() calls to it. - // Skips over things like data stores since they aren't visible. - - var childNodes = dojo.query("> *", this.containerNode).filter(function(node){ - return node.tagName !== "SCRIPT"; // or a regexp for hidden elements like script|area|map|etc.. - }), - childWidgetNodes = childNodes.filter(function(node){ - return dojo.hasAttr(node, "dojoType") || dojo.hasAttr(node, "widgetId"); - }), - candidateWidgets = dojo.filter(childWidgetNodes.map(dijit.byNode), function(widget){ - return widget && widget.domNode && widget.resize; - }); - - if( - // all child nodes are widgets - childNodes.length == childWidgetNodes.length && - - // all but one are invisible (like dojo.data) - candidateWidgets.length == 1 - ){ - this._singleChild = candidateWidgets[0]; - }else{ - delete this._singleChild; - } - - // So we can set overflow: hidden to avoid a safari bug w/scrollbars showing up (#9449) - dojo.toggleClass(this.containerNode, this.baseClass + "SingleChild", !!this._singleChild); - }, - - setHref: function(/*String|Uri*/ href){ - // summary: - // Deprecated. Use set('href', ...) instead. - dojo.deprecated("dijit.layout.ContentPane.setHref() is deprecated. Use set('href', ...) instead.", "", "2.0"); - return this.set("href", href); - }, - _setHrefAttr: function(/*String|Uri*/ href){ - // summary: - // Hook so attr("href", ...) works. - // description: - // Reset the (external defined) content of this pane and replace with new url - // Note: It delays the download until widget is shown if preload is false. - // href: - // url to the page you want to get, must be within the same domain as your mainpage - - // Cancel any in-flight requests (an attr('href') will cancel any in-flight attr('href', ...)) - this.cancel(); - - this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel")); - - this.href = href; - - // _setHrefAttr() is called during creation and by the user, after creation. - // only in the second case do we actually load the URL; otherwise it's done in startup() - if(this._created && (this.preload || this._isShown())){ - this._load(); - }else{ - // Set flag to indicate that href needs to be loaded the next time the - // ContentPane is made visible - this._hrefChanged = true; - } - - return this.onLoadDeferred; // dojo.Deferred - }, - - setContent: function(/*String|DomNode|Nodelist*/data){ - // summary: - // Deprecated. Use set('content', ...) instead. - dojo.deprecated("dijit.layout.ContentPane.setContent() is deprecated. Use set('content', ...) instead.", "", "2.0"); - this.set("content", data); - }, - _setContentAttr: function(/*String|DomNode|Nodelist*/data){ - // summary: - // Hook to make attr("content", ...) work. - // Replaces old content with data content, include style classes from old content - // data: - // the new Content may be String, DomNode or NodeList - // - // if data is a NodeList (or an array of nodes) nodes are copied - // so you can import nodes from another document implicitly - - // clear href so we can't run refresh and clear content - // refresh should only work if we downloaded the content - this.href = ""; - - // Cancel any in-flight requests (an attr('content') will cancel any in-flight attr('href', ...)) - this.cancel(); - - // Even though user is just setting content directly, still need to define an onLoadDeferred - // because the _onLoadHandler() handler is still getting called from setContent() - this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel")); - - this._setContent(data || ""); - - this._isDownloaded = false; // mark that content is from a attr('content') not an attr('href') - - return this.onLoadDeferred; // dojo.Deferred - }, - _getContentAttr: function(){ - // summary: - // Hook to make attr("content") work - return this.containerNode.innerHTML; - }, - - cancel: function(){ - // summary: - // Cancels an in-flight download of content - if(this._xhrDfd && (this._xhrDfd.fired == -1)){ - this._xhrDfd.cancel(); - } - delete this._xhrDfd; // garbage collect - - this.onLoadDeferred = null; - }, - - uninitialize: function(){ - if(this._beingDestroyed){ - this.cancel(); - } - this.inherited(arguments); - }, - - destroyRecursive: function(/*Boolean*/ preserveDom){ - // summary: - // Destroy the ContentPane and its contents - - // if we have multiple controllers destroying us, bail after the first - if(this._beingDestroyed){ - return; - } - this.inherited(arguments); - }, - - resize: function(changeSize, resultSize){ - // summary: - // See `dijit.layout._LayoutWidget.resize` for description. - // Although ContentPane doesn't extend _LayoutWidget, it does implement - // the same API. - - // For the TabContainer --> BorderContainer --> ContentPane case, _onShow() is - // never called, so resize() is our trigger to do the initial href download. - if(!this._wasShown){ - this._onShow(); - } - - this._resizeCalled = true; - - // Set margin box size, unless it wasn't specified, in which case use current size. - if(changeSize){ - dojo.marginBox(this.domNode, changeSize); - } - - // Compute content box size of containerNode in case we [later] need to size our single child. - var cn = this.containerNode; - if(cn === this.domNode){ - // If changeSize or resultSize was passed to this method and this.containerNode == - // this.domNode then we can compute the content-box size without querying the node, - // which is more reliable (similar to LayoutWidget.resize) (see for example #9449). - var mb = resultSize || {}; - dojo.mixin(mb, changeSize || {}); // changeSize overrides resultSize - if(!("h" in mb) || !("w" in mb)){ - mb = dojo.mixin(dojo.marginBox(cn), mb); // just use dojo.marginBox() to fill in missing values - } - this._contentBox = dijit.layout.marginBox2contentBox(cn, mb); - }else{ - this._contentBox = dojo.contentBox(cn); - } - - // Make my children layout, or size my single child widget - this._layoutChildren(); - }, - - _isShown: function(){ - // summary: - // Returns true if the content is currently shown. - // description: - // If I am a child of a layout widget then it actually returns true if I've ever been visible, - // not whether I'm currently visible, since that's much faster than tracing up the DOM/widget - // tree every call, and at least solves the performance problem on page load by deferring loading - // hidden ContentPanes until they are first shown - - if(this._childOfLayoutWidget){ - // If we are TitlePane, etc - we return that only *IF* we've been resized - if(this._resizeCalled && "open" in this){ - return this.open; - } - return this._resizeCalled; - }else if("open" in this){ - return this.open; // for TitlePane, etc. - }else{ - // TODO: with _childOfLayoutWidget check maybe this branch no longer necessary? - var node = this.domNode; - return (node.style.display != 'none') && (node.style.visibility != 'hidden') && !dojo.hasClass(node, "dijitHidden"); - } - }, - - _onShow: function(){ - // summary: - // Called when the ContentPane is made visible - // description: - // For a plain ContentPane, this is called on initialization, from startup(). - // If the ContentPane is a hidden pane of a TabContainer etc., then it's - // called whenever the pane is made visible. - // - // Does necessary processing, including href download and layout/resize of - // child widget(s) - - if(this.href){ - if(!this._xhrDfd && // if there's an href that isn't already being loaded - (!this.isLoaded || this._hrefChanged || this.refreshOnShow) - ){ - this.refresh(); - } - }else{ - // If we are the child of a layout widget then the layout widget will call resize() on - // us, and then we will size our child/children. Otherwise, we need to do it now. - if(!this._childOfLayoutWidget && this._needLayout){ - // If a layout has been scheduled for when we become visible, do it now - this._layoutChildren(); - } - } - - this.inherited(arguments); - - // Need to keep track of whether ContentPane has been shown (which is different than - // whether or not it's currently visible). - this._wasShown = true; - }, - - refresh: function(){ - // summary: - // [Re]download contents of href and display - // description: - // 1. cancels any currently in-flight requests - // 2. posts "loading..." message - // 3. sends XHR to download new data - - // Cancel possible prior in-flight request - this.cancel(); - - this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel")); - this._load(); - return this.onLoadDeferred; - }, - - _load: function(){ - // summary: - // Load/reload the href specified in this.href - - // display loading message - this._setContent(this.onDownloadStart(), true); - - var self = this; - var getArgs = { - preventCache: (this.preventCache || this.refreshOnShow), - url: this.href, - handleAs: "text" - }; - if(dojo.isObject(this.ioArgs)){ - dojo.mixin(getArgs, this.ioArgs); - } - - var hand = (this._xhrDfd = (this.ioMethod || dojo.xhrGet)(getArgs)); - - hand.addCallback(function(html){ - try{ - self._isDownloaded = true; - self._setContent(html, false); - self.onDownloadEnd(); - }catch(err){ - self._onError('Content', err); // onContentError - } - delete self._xhrDfd; - return html; - }); - - hand.addErrback(function(err){ - if(!hand.canceled){ - // show error message in the pane - self._onError('Download', err); // onDownloadError - } - delete self._xhrDfd; - return err; - }); - - // Remove flag saying that a load is needed - delete this._hrefChanged; - }, - - _onLoadHandler: function(data){ - // summary: - // This is called whenever new content is being loaded - this.isLoaded = true; - try{ - this.onLoadDeferred.callback(data); - this.onLoad(data); - }catch(e){ - console.error('Error '+this.widgetId+' running custom onLoad code: ' + e.message); - } - }, - - _onUnloadHandler: function(){ - // summary: - // This is called whenever the content is being unloaded - this.isLoaded = false; - try{ - this.onUnload(); - }catch(e){ - console.error('Error '+this.widgetId+' running custom onUnload code: ' + e.message); - } - }, - - destroyDescendants: function(){ - // summary: - // Destroy all the widgets inside the ContentPane and empty containerNode - - // Make sure we call onUnload (but only when the ContentPane has real content) - if(this.isLoaded){ - this._onUnloadHandler(); - } - - // Even if this.isLoaded == false there might still be a "Loading..." message - // to erase, so continue... - - // For historical reasons we need to delete all widgets under this.containerNode, - // even ones that the user has created manually. - var setter = this._contentSetter; - dojo.forEach(this.getChildren(), function(widget){ - if(widget.destroyRecursive){ - widget.destroyRecursive(); - } - }); - if(setter){ - // Most of the widgets in setter.parseResults have already been destroyed, but - // things like Menu that have been moved to <body> haven't yet - dojo.forEach(setter.parseResults, function(widget){ - if(widget.destroyRecursive && widget.domNode && widget.domNode.parentNode == dojo.body()){ - widget.destroyRecursive(); - } - }); - delete setter.parseResults; - } - - // And then clear away all the DOM nodes - dojo.html._emptyNode(this.containerNode); - - // Delete any state information we have about current contents - delete this._singleChild; - }, - - _setContent: function(cont, isFakeContent){ - // summary: - // Insert the content into the container node - - // first get rid of child widgets - this.destroyDescendants(); - - // dojo.html.set will take care of the rest of the details - // we provide an override for the error handling to ensure the widget gets the errors - // configure the setter instance with only the relevant widget instance properties - // NOTE: unless we hook into attr, or provide property setters for each property, - // we need to re-configure the ContentSetter with each use - var setter = this._contentSetter; - if(! (setter && setter instanceof dojo.html._ContentSetter)){ - setter = this._contentSetter = new dojo.html._ContentSetter({ - node: this.containerNode, - _onError: dojo.hitch(this, this._onError), - onContentError: dojo.hitch(this, function(e){ - // fires if a domfault occurs when we are appending this.errorMessage - // like for instance if domNode is a UL and we try append a DIV - var errMess = this.onContentError(e); - try{ - this.containerNode.innerHTML = errMess; - }catch(e){ - console.error('Fatal '+this.id+' could not change content due to '+e.message, e); - } - })/*, - _onError */ - }); - }; - - var setterParams = dojo.mixin({ - cleanContent: this.cleanContent, - extractContent: this.extractContent, - parseContent: this.parseOnLoad, - dir: this.dir, - lang: this.lang - }, this._contentSetterParams || {}); - - dojo.mixin(setter, setterParams); - - setter.set( (dojo.isObject(cont) && cont.domNode) ? cont.domNode : cont ); - - // setter params must be pulled afresh from the ContentPane each time - delete this._contentSetterParams; - - if(!isFakeContent){ - // Startup each top level child widget (and they will start their children, recursively) - dojo.forEach(this.getChildren(), function(child){ - // The parser has already called startup on all widgets *without* a getParent() method - if(!this.parseOnLoad || child.getParent){ - child.startup(); - } - }, this); - - // Call resize() on each of my child layout widgets, - // or resize() on my single child layout widget... - // either now (if I'm currently visible) - // or when I become visible - this._scheduleLayout(); - - this._onLoadHandler(cont); - } - }, - - _onError: function(type, err, consoleText){ - this.onLoadDeferred.errback(err); - - // shows user the string that is returned by on[type]Error - // overide on[type]Error and return your own string to customize - var errText = this['on' + type + 'Error'].call(this, err); - if(consoleText){ - console.error(consoleText, err); - }else if(errText){// a empty string won't change current content - this._setContent(errText, true); - } - }, - - _scheduleLayout: function(){ - // summary: - // Call resize() on each of my child layout widgets, either now - // (if I'm currently visible) or when I become visible - if(this._isShown()){ - this._layoutChildren(); - }else{ - this._needLayout = true; - } - }, - - _layoutChildren: function(){ - // summary: - // Since I am a Container widget, each of my children expects me to - // call resize() or layout() on them. - // description: - // Should be called on initialization and also whenever we get new content - // (from an href, or from attr('content', ...))... but deferred until - // the ContentPane is visible - - if(this.doLayout){ - this._checkIfSingleChild(); - } - - if(this._singleChild && this._singleChild.resize){ - var cb = this._contentBox || dojo.contentBox(this.containerNode); - - // note: if widget has padding this._contentBox will have l and t set, - // but don't pass them to resize() or it will doubly-offset the child - this._singleChild.resize({w: cb.w, h: cb.h}); - }else{ - // All my child widgets are independently sized (rather than matching my size), - // but I still need to call resize() on each child to make it layout. - dojo.forEach(this.getChildren(), function(widget){ - if(widget.resize){ - widget.resize(); - } - }); - } - delete this._needLayout; - }, - - // EVENT's, should be overide-able - onLoad: function(data){ - // summary: - // Event hook, is called after everything is loaded and widgetified - // tags: - // callback - }, - - onUnload: function(){ - // summary: - // Event hook, is called before old content is cleared - // tags: - // callback - }, - - onDownloadStart: function(){ - // summary: - // Called before download starts. - // description: - // The string returned by this function will be the html - // that tells the user we are loading something. - // Override with your own function if you want to change text. - // tags: - // extension - return this.loadingMessage; - }, - - onContentError: function(/*Error*/ error){ - // summary: - // Called on DOM faults, require faults etc. in content. - // - // In order to display an error message in the pane, return - // the error message from this method, as an HTML string. - // - // By default (if this method is not overriden), it returns - // nothing, so the error message is just printed to the console. - // tags: - // extension - }, - - onDownloadError: function(/*Error*/ error){ - // summary: - // Called when download error occurs. - // - // In order to display an error message in the pane, return - // the error message from this method, as an HTML string. - // - // Default behavior (if this method is not overriden) is to display - // the error message inside the pane. - // tags: - // extension - return this.errorMessage; - }, - - onDownloadEnd: function(){ - // summary: - // Called when download is finished. - // tags: - // callback - } -}); - -} - -if(!dojo._hasResource["dijit.TooltipDialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.TooltipDialog"] = true; -dojo.provide("dijit.TooltipDialog"); - - - - - - -dojo.declare( - "dijit.TooltipDialog", - [dijit.layout.ContentPane, dijit._Templated, dijit.form._FormMixin, dijit._DialogMixin], - { - // summary: - // Pops up a dialog that appears like a Tooltip - - // title: String - // Description of tooltip dialog (required for a11y) - title: "", - - // doLayout: [protected] Boolean - // Don't change this parameter from the default value. - // This ContentPane parameter doesn't make sense for TooltipDialog, since TooltipDialog - // is never a child of a layout container, nor can you specify the size of - // TooltipDialog in order to control the size of an inner widget. - doLayout: false, - - // autofocus: Boolean - // A Toggle to modify the default focus behavior of a Dialog, which - // is to focus on the first dialog element after opening the dialog. - // False will disable autofocusing. Default: true - autofocus: true, - - // baseClass: [protected] String - // The root className to use for the various states of this widget - baseClass: "dijitTooltipDialog", - - // _firstFocusItem: [private] [readonly] DomNode - // The pointer to the first focusable node in the dialog. - // Set by `dijit._DialogMixin._getFocusItems`. - _firstFocusItem: null, - - // _lastFocusItem: [private] [readonly] DomNode - // The pointer to which node has focus prior to our dialog. - // Set by `dijit._DialogMixin._getFocusItems`. - _lastFocusItem: null, - - templateString: dojo.cache("dijit", "templates/TooltipDialog.html", "<div waiRole=\"presentation\">\n\t<div class=\"dijitTooltipContainer\" waiRole=\"presentation\">\n\t\t<div class =\"dijitTooltipContents dijitTooltipFocusNode\" dojoAttachPoint=\"containerNode\" tabindex=\"-1\" waiRole=\"dialog\"></div>\n\t</div>\n\t<div class=\"dijitTooltipConnector\" waiRole=\"presentation\"></div>\n</div>\n"), - - postCreate: function(){ - this.inherited(arguments); - this.connect(this.containerNode, "onkeypress", "_onKey"); - this.containerNode.title = this.title; - }, - - orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ corner){ - // summary: - // Configure widget to be displayed in given position relative to the button. - // This is called from the dijit.popup code, and should not be called - // directly. - // tags: - // protected - var c = this._currentOrientClass; - if(c){ - dojo.removeClass(this.domNode, c); - } - c = "dijitTooltipAB"+(corner.charAt(1) == 'L'?"Left":"Right")+" dijitTooltip"+(corner.charAt(0) == 'T' ? "Below" : "Above"); - dojo.addClass(this.domNode, c); - this._currentOrientClass = c; - }, - - onOpen: function(/*Object*/ pos){ - // summary: - // Called when dialog is displayed. - // This is called from the dijit.popup code, and should not be called directly. - // tags: - // protected - - this.orient(this.domNode,pos.aroundCorner, pos.corner); - this._onShow(); // lazy load trigger - - if(this.autofocus){ - this._getFocusItems(this.containerNode); - dijit.focus(this._firstFocusItem); - } - }, - - onClose: function(){ - // summary: - // Called when dialog is hidden. - // This is called from the dijit.popup code, and should not be called directly. - // tags: - // protected - this.onHide(); - }, - - _onKey: function(/*Event*/ evt){ - // summary: - // Handler for keyboard events - // description: - // Keep keyboard focus in dialog; close dialog on escape key - // tags: - // private - - var node = evt.target; - var dk = dojo.keys; - if(evt.charOrCode === dk.TAB){ - this._getFocusItems(this.containerNode); - } - var singleFocusItem = (this._firstFocusItem == this._lastFocusItem); - if(evt.charOrCode == dk.ESCAPE){ - // Use setTimeout to avoid crash on IE, see #10396. - setTimeout(dojo.hitch(this, "onCancel"), 0); - dojo.stopEvent(evt); - }else if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === dk.TAB){ - if(!singleFocusItem){ - dijit.focus(this._lastFocusItem); // send focus to last item in dialog - } - dojo.stopEvent(evt); - }else if(node == this._lastFocusItem && evt.charOrCode === dk.TAB && !evt.shiftKey){ - if(!singleFocusItem){ - dijit.focus(this._firstFocusItem); // send focus to first item in dialog - } - dojo.stopEvent(evt); - }else if(evt.charOrCode === dk.TAB){ - // we want the browser's default tab handling to move focus - // but we don't want the tab to propagate upwards - evt.stopPropagation(); - } - } - } - ); - -} - -if(!dojo._hasResource["dijit.Dialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.Dialog"] = true; -dojo.provide("dijit.Dialog"); - - - - - - - - - - - - - - - -/*===== -dijit._underlay = function(kwArgs){ - // summary: - // A shared instance of a `dijit.DialogUnderlay` - // - // description: - // A shared instance of a `dijit.DialogUnderlay` created and - // used by `dijit.Dialog`, though never created until some Dialog - // or subclass thereof is shown. -}; -=====*/ - -dojo.declare( - "dijit._DialogBase", - [dijit._Templated, dijit.form._FormMixin, dijit._DialogMixin, dijit._CssStateMixin], - { - // summary: - // A modal dialog Widget - // - // description: - // Pops up a modal dialog window, blocking access to the screen - // and also graying out the screen Dialog is extended from - // ContentPane so it supports all the same parameters (href, etc.) - // - // example: - // | <div dojoType="dijit.Dialog" href="test.html"></div> - // - // example: - // | var foo = new dijit.Dialog({ title: "test dialog", content: "test content" }; - // | dojo.body().appendChild(foo.domNode); - // | foo.startup(); - - templateString: dojo.cache("dijit", "templates/Dialog.html", "<div class=\"dijitDialog\" tabindex=\"-1\" waiRole=\"dialog\" waiState=\"labelledby-${id}_title\">\n\t<div dojoAttachPoint=\"titleBar\" class=\"dijitDialogTitleBar\">\n\t<span dojoAttachPoint=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"></span>\n\t<span dojoAttachPoint=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" dojoAttachEvent=\"onclick: onCancel\" title=\"${buttonCancel}\">\n\t\t<span dojoAttachPoint=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t</span>\n\t</div>\n\t\t<div dojoAttachPoint=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n</div>\n"), - - baseClass: "dijitDialog", - - cssStateNodes: { - closeButtonNode: "dijitDialogCloseIcon" - }, - - attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, { - title: [ - { node: "titleNode", type: "innerHTML" }, - { node: "titleBar", type: "attribute" } - ], - "aria-describedby":"" - }), - - // open: Boolean - // True if Dialog is currently displayed on screen. - open: false, - - // duration: Integer - // The time in milliseconds it takes the dialog to fade in and out - duration: dijit.defaultDuration, - - // refocus: Boolean - // A Toggle to modify the default focus behavior of a Dialog, which - // is to re-focus the element which had focus before being opened. - // False will disable refocusing. Default: true - refocus: true, - - // autofocus: Boolean - // A Toggle to modify the default focus behavior of a Dialog, which - // is to focus on the first dialog element after opening the dialog. - // False will disable autofocusing. Default: true - autofocus: true, - - // _firstFocusItem: [private] [readonly] DomNode - // The pointer to the first focusable node in the dialog. - // Set by `dijit._DialogMixin._getFocusItems`. - _firstFocusItem: null, - - // _lastFocusItem: [private] [readonly] DomNode - // The pointer to which node has focus prior to our dialog. - // Set by `dijit._DialogMixin._getFocusItems`. - _lastFocusItem: null, - - // doLayout: [protected] Boolean - // Don't change this parameter from the default value. - // This ContentPane parameter doesn't make sense for Dialog, since Dialog - // is never a child of a layout container, nor can you specify the size of - // Dialog in order to control the size of an inner widget. - doLayout: false, - - // draggable: Boolean - // Toggles the moveable aspect of the Dialog. If true, Dialog - // can be dragged by it's title. If false it will remain centered - // in the viewport. - draggable: true, - - //aria-describedby: String - // Allows the user to add an aria-describedby attribute onto the dialog. The value should - // be the id of the container element of text that describes the dialog purpose (usually - // the first text in the dialog). - // <div dojoType="dijit.Dialog" aria-describedby="intro" .....> - // <div id="intro">Introductory text</div> - // <div>rest of dialog contents</div> - // </div> - "aria-describedby":"", - - postMixInProperties: function(){ - var _nlsResources = dojo.i18n.getLocalization("dijit", "common"); - dojo.mixin(this, _nlsResources); - this.inherited(arguments); - }, - - postCreate: function(){ - dojo.style(this.domNode, { - display: "none", - position:"absolute" - }); - dojo.body().appendChild(this.domNode); - - this.inherited(arguments); - - this.connect(this, "onExecute", "hide"); - this.connect(this, "onCancel", "hide"); - this._modalconnects = []; - }, - - onLoad: function(){ - // summary: - // Called when data has been loaded from an href. - // Unlike most other callbacks, this function can be connected to (via `dojo.connect`) - // but should *not* be overriden. - // tags: - // callback - - // when href is specified we need to reposition the dialog after the data is loaded - // and find the focusable elements - this._position(); - if(this.autofocus){ - this._getFocusItems(this.domNode); - dijit.focus(this._firstFocusItem); - } - this.inherited(arguments); - }, - - _endDrag: function(e){ - // summary: - // Called after dragging the Dialog. Saves the position of the dialog in the viewport. - // tags: - // private - if(e && e.node && e.node === this.domNode){ - this._relativePosition = dojo.position(e.node); - } - }, - - _setup: function(){ - // summary: - // Stuff we need to do before showing the Dialog for the first - // time (but we defer it until right beforehand, for - // performance reasons). - // tags: - // private - - var node = this.domNode; - - if(this.titleBar && this.draggable){ - this._moveable = (dojo.isIE == 6) ? - new dojo.dnd.TimedMoveable(node, { handle: this.titleBar }) : // prevent overload, see #5285 - new dojo.dnd.Moveable(node, { handle: this.titleBar, timeout: 0 }); - dojo.subscribe("/dnd/move/stop",this,"_endDrag"); - }else{ - dojo.addClass(node,"dijitDialogFixed"); - } - - this.underlayAttrs = { - dialogId: this.id, - "class": dojo.map(this["class"].split(/\s/), function(s){ return s+"_underlay"; }).join(" ") - }; - - this._fadeIn = dojo.fadeIn({ - node: node, - duration: this.duration, - beforeBegin: dojo.hitch(this, function(){ - var underlay = dijit._underlay; - if(!underlay){ - underlay = dijit._underlay = new dijit.DialogUnderlay(this.underlayAttrs); - }else{ - underlay.set(this.underlayAttrs); - } - - var ds = dijit._dialogStack, - zIndex = 948 + ds.length*2; - if(ds.length == 1){ // first dialog - underlay.show(); - } - dojo.style(dijit._underlay.domNode, 'zIndex', zIndex); - dojo.style(this.domNode, 'zIndex', zIndex + 1); - }), - onEnd: dojo.hitch(this, function(){ - if(this.autofocus){ - // find focusable Items each time dialog is shown since if dialog contains a widget the - // first focusable items can change - this._getFocusItems(this.domNode); - dijit.focus(this._firstFocusItem); - } - }) - }); - - this._fadeOut = dojo.fadeOut({ - node: node, - duration: this.duration, - onEnd: dojo.hitch(this, function(){ - node.style.display = "none"; - - // Restore the previous dialog in the stack, or if this is the only dialog - // then restore to original page - var ds = dijit._dialogStack; - if(ds.length == 0){ - dijit._underlay.hide(); - }else{ - dojo.style(dijit._underlay.domNode, 'zIndex', 948 + ds.length*2); - dijit._underlay.set(ds[ds.length-1].underlayAttrs); - } - - // Restore focus to wherever it was before this dialog was displayed - if(this.refocus){ - var focus = this._savedFocus; - - // If we are returning control to a previous dialog but for some reason - // that dialog didn't have a focused field, set focus to first focusable item. - // This situation could happen if two dialogs appeared at nearly the same time, - // since a dialog doesn't set it's focus until the fade-in is finished. - if(ds.length > 0){ - var pd = ds[ds.length-1]; - if(!dojo.isDescendant(focus.node, pd.domNode)){ - pd._getFocusItems(pd.domNode); - focus = pd._firstFocusItem; - } - } - - dijit.focus(focus); - } - }) - }); - }, - - uninitialize: function(){ - var wasPlaying = false; - if(this._fadeIn && this._fadeIn.status() == "playing"){ - wasPlaying = true; - this._fadeIn.stop(); - } - if(this._fadeOut && this._fadeOut.status() == "playing"){ - wasPlaying = true; - this._fadeOut.stop(); - } - - // Hide the underlay, unless the underlay widget has already been destroyed - // because we are being called during page unload (when all widgets are destroyed) - if((this.open || wasPlaying) && !dijit._underlay._destroyed){ - dijit._underlay.hide(); - } - - if(this._moveable){ - this._moveable.destroy(); - } - this.inherited(arguments); - }, - - _size: function(){ - // summary: - // If necessary, shrink dialog contents so dialog fits in viewport - // tags: - // private - - this._checkIfSingleChild(); - - // If we resized the dialog contents earlier, reset them back to original size, so - // that if the user later increases the viewport size, the dialog can display w/out a scrollbar. - // Need to do this before the dojo.marginBox(this.domNode) call below. - if(this._singleChild){ - if(this._singleChildOriginalStyle){ - this._singleChild.domNode.style.cssText = this._singleChildOriginalStyle; - } - delete this._singleChildOriginalStyle; - }else{ - dojo.style(this.containerNode, { - width:"auto", - height:"auto" - }); - } - - var mb = dojo.marginBox(this.domNode); - var viewport = dojo.window.getBox(); - if(mb.w >= viewport.w || mb.h >= viewport.h){ - // Reduce size of dialog contents so that dialog fits in viewport - - var w = Math.min(mb.w, Math.floor(viewport.w * 0.75)), - h = Math.min(mb.h, Math.floor(viewport.h * 0.75)); - - if(this._singleChild && this._singleChild.resize){ - this._singleChildOriginalStyle = this._singleChild.domNode.style.cssText; - this._singleChild.resize({w: w, h: h}); - }else{ - dojo.style(this.containerNode, { - width: w + "px", - height: h + "px", - overflow: "auto", - position: "relative" // workaround IE bug moving scrollbar or dragging dialog - }); - } - }else{ - if(this._singleChild && this._singleChild.resize){ - this._singleChild.resize(); - } - } - }, - - _position: function(){ - // summary: - // Position modal dialog in the viewport. If no relative offset - // in the viewport has been determined (by dragging, for instance), - // center the node. Otherwise, use the Dialog's stored relative offset, - // and position the node to top: left: values based on the viewport. - // tags: - // private - if(!dojo.hasClass(dojo.body(),"dojoMove")){ - var node = this.domNode, - viewport = dojo.window.getBox(), - p = this._relativePosition, - bb = p ? null : dojo._getBorderBox(node), - l = Math.floor(viewport.l + (p ? p.x : (viewport.w - bb.w) / 2)), - t = Math.floor(viewport.t + (p ? p.y : (viewport.h - bb.h) / 2)) - ; - dojo.style(node,{ - left: l + "px", - top: t + "px" - }); - } - }, - - _onKey: function(/*Event*/ evt){ - // summary: - // Handles the keyboard events for accessibility reasons - // tags: - // private - - var ds = dijit._dialogStack; - if(ds[ds.length-1] != this){ - // console.debug(this.id + ': skipping because', this, 'is not the active dialog'); - return; - } - - if(evt.charOrCode){ - var dk = dojo.keys; - var node = evt.target; - if(evt.charOrCode === dk.TAB){ - this._getFocusItems(this.domNode); - } - var singleFocusItem = (this._firstFocusItem == this._lastFocusItem); - // see if we are shift-tabbing from first focusable item on dialog - if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === dk.TAB){ - if(!singleFocusItem){ - dijit.focus(this._lastFocusItem); // send focus to last item in dialog - } - dojo.stopEvent(evt); - }else if(node == this._lastFocusItem && evt.charOrCode === dk.TAB && !evt.shiftKey){ - if(!singleFocusItem){ - dijit.focus(this._firstFocusItem); // send focus to first item in dialog - } - dojo.stopEvent(evt); - }else{ - // see if the key is for the dialog - while(node){ - if(node == this.domNode || dojo.hasClass(node, "dijitPopup")){ - if(evt.charOrCode == dk.ESCAPE){ - this.onCancel(); - }else{ - return; // just let it go - } - } - node = node.parentNode; - } - // this key is for the disabled document window - if(evt.charOrCode !== dk.TAB){ // allow tabbing into the dialog for a11y - dojo.stopEvent(evt); - // opera won't tab to a div - }else if(!dojo.isOpera){ - try{ - this._firstFocusItem.focus(); - }catch(e){ /*squelch*/ } - } - } - } - }, - - show: function(){ - // summary: - // Display the dialog - if(this.open){ return; } - - // first time we show the dialog, there's some initialization stuff to do - if(!this._alreadyInitialized){ - this._setup(); - this._alreadyInitialized=true; - } - - if(this._fadeOut.status() == "playing"){ - this._fadeOut.stop(); - } - - this._modalconnects.push(dojo.connect(window, "onscroll", this, "layout")); - this._modalconnects.push(dojo.connect(window, "onresize", this, function(){ - // IE gives spurious resize events and can actually get stuck - // in an infinite loop if we don't ignore them - var viewport = dojo.window.getBox(); - if(!this._oldViewport || - viewport.h != this._oldViewport.h || - viewport.w != this._oldViewport.w){ - this.layout(); - this._oldViewport = viewport; - } - })); - this._modalconnects.push(dojo.connect(dojo.doc.documentElement, "onkeypress", this, "_onKey")); - - dojo.style(this.domNode, { - opacity:0, - display:"" - }); - - this.open = true; - this._onShow(); // lazy load trigger - - this._size(); - this._position(); - dijit._dialogStack.push(this); - this._fadeIn.play(); - - this._savedFocus = dijit.getFocus(this); - }, - - hide: function(){ - // summary: - // Hide the dialog - - // if we haven't been initialized yet then we aren't showing and we can just return - // or if we aren't the active dialog, don't allow us to close yet - var ds = dijit._dialogStack; - if(!this._alreadyInitialized || this != ds[ds.length-1]){ - return; - } - - if(this._fadeIn.status() == "playing"){ - this._fadeIn.stop(); - } - - // throw away current active dialog from stack -- making the previous dialog or the node on the original page active - ds.pop(); - - this._fadeOut.play(); - - if(this._scrollConnected){ - this._scrollConnected = false; - } - dojo.forEach(this._modalconnects, dojo.disconnect); - this._modalconnects = []; - - if(this._relativePosition){ - delete this._relativePosition; - } - this.open = false; - - this.onHide(); - }, - - layout: function(){ - // summary: - // Position the Dialog and the underlay - // tags: - // private - if(this.domNode.style.display != "none"){ - if(dijit._underlay){ // avoid race condition during show() - dijit._underlay.layout(); - } - this._position(); - } - }, - - destroy: function(){ - dojo.forEach(this._modalconnects, dojo.disconnect); - if(this.refocus && this.open){ - setTimeout(dojo.hitch(dijit,"focus",this._savedFocus), 25); - } - this.inherited(arguments); - } - } -); - -dojo.declare( - "dijit.Dialog", - [dijit.layout.ContentPane, dijit._DialogBase], - {} -); - -// Stack of currenctly displayed dialogs, layered on top of each other -dijit._dialogStack = []; - -// For back-compat. TODO: remove in 2.0 - - -} - -if(!dojo._hasResource["dijit._editor.selection"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit._editor.selection"] = true; -dojo.provide("dijit._editor.selection"); - -// FIXME: -// all of these methods branch internally for IE. This is probably -// sub-optimal in terms of runtime performance. We should investigate the -// size difference for differentiating at definition time. - -dojo.mixin(dijit._editor.selection, { - getType: function(){ - // summary: - // Get the selection type (like dojo.doc.select.type in IE). - if(dojo.isIE){ - return dojo.doc.selection.type.toLowerCase(); - }else{ - var stype = "text"; - - // Check if the actual selection is a CONTROL (IMG, TABLE, HR, etc...). - var oSel; - try{ - oSel = dojo.global.getSelection(); - }catch(e){ /*squelch*/ } - - if(oSel && oSel.rangeCount == 1){ - var oRange = oSel.getRangeAt(0); - if( (oRange.startContainer == oRange.endContainer) && - ((oRange.endOffset - oRange.startOffset) == 1) && - (oRange.startContainer.nodeType != 3 /* text node*/) - ){ - stype = "control"; - } - } - return stype; //String - } - }, - - getSelectedText: function(){ - // summary: - // Return the text (no html tags) included in the current selection or null if no text is selected - if(dojo.isIE){ - if(dijit._editor.selection.getType() == 'control'){ - return null; - } - return dojo.doc.selection.createRange().text; - }else{ - var selection = dojo.global.getSelection(); - if(selection){ - return selection.toString(); //String - } - } - return ''; - }, - - getSelectedHtml: function(){ - // summary: - // Return the html text of the current selection or null if unavailable - if(dojo.isIE){ - if(dijit._editor.selection.getType() == 'control'){ - return null; - } - return dojo.doc.selection.createRange().htmlText; - }else{ - var selection = dojo.global.getSelection(); - if(selection && selection.rangeCount){ - var i; - var html = ""; - for(i = 0; i < selection.rangeCount; i++){ - //Handle selections spanning ranges, such as Opera - var frag = selection.getRangeAt(i).cloneContents(); - var div = dojo.doc.createElement("div"); - div.appendChild(frag); - html += div.innerHTML; - } - return html; //String - } - return null; - } - }, - - getSelectedElement: function(){ - // summary: - // Retrieves the selected element (if any), just in the case that - // a single element (object like and image or a table) is - // selected. - if(dijit._editor.selection.getType() == "control"){ - if(dojo.isIE){ - var range = dojo.doc.selection.createRange(); - if(range && range.item){ - return dojo.doc.selection.createRange().item(0); - } - }else{ - var selection = dojo.global.getSelection(); - return selection.anchorNode.childNodes[ selection.anchorOffset ]; - } - } - return null; - }, - - getParentElement: function(){ - // summary: - // Get the parent element of the current selection - if(dijit._editor.selection.getType() == "control"){ - var p = this.getSelectedElement(); - if(p){ return p.parentNode; } - }else{ - if(dojo.isIE){ - var r = dojo.doc.selection.createRange(); - r.collapse(true); - return r.parentElement(); - }else{ - var selection = dojo.global.getSelection(); - if(selection){ - var node = selection.anchorNode; - while(node && (node.nodeType != 1)){ // not an element - node = node.parentNode; - } - return node; - } - } - } - return null; - }, - - hasAncestorElement: function(/*String*/tagName /* ... */){ - // summary: - // Check whether current selection has a parent element which is - // of type tagName (or one of the other specified tagName) - // tagName: String - // The tag name to determine if it has an ancestor of. - return this.getAncestorElement.apply(this, arguments) != null; //Boolean - }, - - getAncestorElement: function(/*String*/tagName /* ... */){ - // summary: - // Return the parent element of the current selection which is of - // type tagName (or one of the other specified tagName) - // tagName: String - // The tag name to determine if it has an ancestor of. - var node = this.getSelectedElement() || this.getParentElement(); - return this.getParentOfType(node, arguments); //DOMNode - }, - - isTag: function(/*DomNode*/ node, /*String[]*/ tags){ - // summary: - // Function to determine if a node is one of an array of tags. - // node: - // The node to inspect. - // tags: - // An array of tag name strings to check to see if the node matches. - if(node && node.tagName){ - var _nlc = node.tagName.toLowerCase(); - for(var i=0; i<tags.length; i++){ - var _tlc = String(tags[i]).toLowerCase(); - if(_nlc == _tlc){ - return _tlc; // String - } - } - } - return ""; - }, - - getParentOfType: function(/*DomNode*/ node, /*String[]*/ tags){ - // summary: - // Function to locate a parent node that matches one of a set of tags - // node: - // The node to inspect. - // tags: - // An array of tag name strings to check to see if the node matches. - while(node){ - if(this.isTag(node, tags).length){ - return node; // DOMNode - } - node = node.parentNode; - } - return null; - }, - - collapse: function(/*Boolean*/beginning){ - // summary: - // Function to collapse (clear), the current selection - // beginning: Boolean - // Boolean to indicate whether to collapse the cursor to the beginning of the selection or end. - if(window.getSelection){ - var selection = dojo.global.getSelection(); - if(selection.removeAllRanges){ // Mozilla - if(beginning){ - selection.collapseToStart(); - }else{ - selection.collapseToEnd(); - } - }else{ // Safari - // pulled from WebCore/ecma/kjs_window.cpp, line 2536 - selection.collapse(beginning); - } - }else if(dojo.isIE){ // IE - var range = dojo.doc.selection.createRange(); - range.collapse(beginning); - range.select(); - } - }, - - remove: function(){ - // summary: - // Function to delete the currently selected content from the document. - var sel = dojo.doc.selection; - if(dojo.isIE){ - if(sel.type.toLowerCase() != "none"){ - sel.clear(); - } - return sel; //Selection - }else{ - sel = dojo.global.getSelection(); - sel.deleteFromDocument(); - return sel; //Selection - } - }, - - selectElementChildren: function(/*DomNode*/element,/*Boolean?*/nochangefocus){ - // summary: - // clear previous selection and select the content of the node - // (excluding the node itself) - // element: DOMNode - // The element you wish to select the children content of. - // nochangefocus: Boolean - // Boolean to indicate if the foxus should change or not. - var win = dojo.global; - var doc = dojo.doc; - var range; - element = dojo.byId(element); - if(doc.selection && dojo.isIE && dojo.body().createTextRange){ // IE - range = element.ownerDocument.body.createTextRange(); - range.moveToElementText(element); - if(!nochangefocus){ - try{ - range.select(); // IE throws an exception here if the widget is hidden. See #5439 - }catch(e){ /* squelch */} - } - }else if(win.getSelection){ - var selection = dojo.global.getSelection(); - if(dojo.isOpera){ - //Opera's selectAllChildren doesn't seem to work right - //against <body> nodes and possibly others ... so - //we use the W3C range API - if(selection.rangeCount){ - range = selection.getRangeAt(0); - }else{ - range = doc.createRange(); - } - range.setStart(element, 0); - range.setEnd(element,(element.nodeType == 3)?element.length:element.childNodes.length); - selection.addRange(range); - }else{ - selection.selectAllChildren(element); - } - } - }, - - selectElement: function(/*DomNode*/element,/*Boolean?*/nochangefocus){ - // summary: - // clear previous selection and select element (including all its children) - // element: DOMNode - // The element to select. - // nochangefocus: Boolean - // Boolean indicating if the focus should be changed. IE only. - var range; - var doc = dojo.doc; - var win = dojo.global; - element = dojo.byId(element); - if(dojo.isIE && dojo.body().createTextRange){ - try{ - range = dojo.body().createControlRange(); - range.addElement(element); - if(!nochangefocus){ - range.select(); - } - }catch(e){ - this.selectElementChildren(element,nochangefocus); - } - }else if(dojo.global.getSelection){ - var selection = win.getSelection(); - range = doc.createRange(); - if(selection.removeAllRanges){ // Mozilla - // FIXME: does this work on Safari? - if(dojo.isOpera){ - //Opera works if you use the current range on - //the selection if present. - if(selection.getRangeAt(0)){ - range = selection.getRangeAt(0); - } - } - range.selectNode(element); - selection.removeAllRanges(); - selection.addRange(range); - } - } - }, - - inSelection: function(node){ - // summary: - // This function determines if 'node' is - // in the current selection. - // tags: - // public - if(node){ - var newRange; - var doc = dojo.doc; - var range; - - if(dojo.global.getSelection){ - //WC3 - var sel = dojo.global.getSelection(); - if(sel && sel.rangeCount > 0){ - range = sel.getRangeAt(0); - } - if(range && range.compareBoundaryPoints && doc.createRange){ - try{ - newRange = doc.createRange(); - newRange.setStart(node, 0); - if(range.compareBoundaryPoints(range.START_TO_END, newRange) === 1){ - return true; - } - }catch(e){ /* squelch */} - } - }else if(doc.selection){ - // Probably IE, so we can't use the range object as the pseudo - // range doesn't implement the boundry checking, we have to - // use IE specific crud. - range = doc.selection.createRange(); - try{ - newRange = node.ownerDocument.body.createControlRange(); - if(newRange){ - newRange.addElement(node); - } - }catch(e1){ - try{ - newRange = node.ownerDocument.body.createTextRange(); - newRange.moveToElementText(node); - }catch(e2){/* squelch */} - } - if(range && newRange){ - // We can finally compare similar to W3C - if(range.compareEndPoints("EndToStart", newRange) === 1){ - return true; - } - } - } - } - return false; // boolean - } - -}); - -} - -if(!dojo._hasResource["dijit._editor.range"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit._editor.range"] = true; -dojo.provide("dijit._editor.range"); - -dijit.range={}; - -dijit.range.getIndex=function(/*DomNode*/node, /*DomNode*/parent){ -// dojo.profile.start("dijit.range.getIndex"); - var ret=[], retR=[]; - var stop = parent; - var onode = node; - - var pnode, n; - while(node != stop){ - var i = 0; - pnode = node.parentNode; - while((n=pnode.childNodes[i++])){ - if(n === node){ - --i; - break; - } - } - //if(i>=pnode.childNodes.length){ - //dojo.debug("Error finding index of a node in dijit.range.getIndex"); - //} - ret.unshift(i); - retR.unshift(i-pnode.childNodes.length); - node = pnode; - } - - //normalized() can not be called so often to prevent - //invalidating selection/range, so we have to detect - //here that any text nodes in a row - if(ret.length > 0 && onode.nodeType == 3){ - n = onode.previousSibling; - while(n && n.nodeType == 3){ - ret[ret.length-1]--; - n = n.previousSibling; - } - n = onode.nextSibling; - while(n && n.nodeType == 3){ - retR[retR.length-1]++; - n = n.nextSibling; - } - } -// dojo.profile.end("dijit.range.getIndex"); - return {o: ret, r:retR}; -} - -dijit.range.getNode = function(/*Array*/index, /*DomNode*/parent){ - if(!dojo.isArray(index) || index.length == 0){ - return parent; - } - var node = parent; -// if(!node)debugger - dojo.every(index, function(i){ - if(i >= 0 && i < node.childNodes.length){ - node = node.childNodes[i]; - }else{ - node = null; - //console.debug('Error: can not find node with index',index,'under parent node',parent ); - return false; //terminate dojo.every - } - return true; //carry on the every loop - }); - - return node; -} - -dijit.range.getCommonAncestor = function(n1,n2,root){ - root = root||n1.ownerDocument.body; - var getAncestors = function(n){ - var as=[]; - while(n){ - as.unshift(n); - if(n !== root){ - n = n.parentNode; - }else{ - break; - } - } - return as; - }; - var n1as = getAncestors(n1); - var n2as = getAncestors(n2); - - var m = Math.min(n1as.length,n2as.length); - var com = n1as[0]; //at least, one element should be in the array: the root (BODY by default) - for(var i=1;i<m;i++){ - if(n1as[i] === n2as[i]){ - com = n1as[i] - }else{ - break; - } - } - return com; -} - -dijit.range.getAncestor = function(/*DomNode*/node, /*RegEx?*/regex, /*DomNode?*/root){ - root = root || node.ownerDocument.body; - while(node && node !== root){ - var name = node.nodeName.toUpperCase() ; - if(regex.test(name)){ - return node; - } - - node = node.parentNode; - } - return null; -} - -dijit.range.BlockTagNames = /^(?:P|DIV|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI|DT|DE)$/; -dijit.range.getBlockAncestor = function(/*DomNode*/node, /*RegEx?*/regex, /*DomNode?*/root){ - root = root || node.ownerDocument.body; - regex = regex || dijit.range.BlockTagNames; - var block=null, blockContainer; - while(node && node !== root){ - var name = node.nodeName.toUpperCase() ; - if(!block && regex.test(name)){ - block = node; - } - if(!blockContainer && (/^(?:BODY|TD|TH|CAPTION)$/).test(name)){ - blockContainer = node; - } - - node = node.parentNode; - } - return {blockNode:block, blockContainer:blockContainer || node.ownerDocument.body}; -} - -dijit.range.atBeginningOfContainer = function(/*DomNode*/container, /*DomNode*/node, /*Int*/offset){ - var atBeginning = false; - var offsetAtBeginning = (offset == 0); - if(!offsetAtBeginning && node.nodeType == 3){ //if this is a text node, check whether the left part is all space - if(/^[\s\xA0]+$/.test(node.nodeValue.substr(0,offset))){ - offsetAtBeginning = true; - } - } - if(offsetAtBeginning){ - var cnode = node; - atBeginning = true; - while(cnode && cnode !== container){ - if(cnode.previousSibling){ - atBeginning = false; - break; - } - cnode = cnode.parentNode; - } - } - return atBeginning; -} - -dijit.range.atEndOfContainer = function(/*DomNode*/container, /*DomNode*/node, /*Int*/offset){ - var atEnd = false; - var offsetAtEnd = (offset == (node.length || node.childNodes.length)); - if(!offsetAtEnd && node.nodeType == 3){ //if this is a text node, check whether the right part is all space - if(/^[\s\xA0]+$/.test(node.nodeValue.substr(offset))){ - offsetAtEnd = true; - } - } - if(offsetAtEnd){ - var cnode = node; - atEnd = true; - while(cnode && cnode !== container){ - if(cnode.nextSibling){ - atEnd = false; - break; - } - cnode = cnode.parentNode; - } - } - return atEnd; -} - -dijit.range.adjacentNoneTextNode=function(startnode, next){ - var node = startnode; - var len = (0-startnode.length) || 0; - var prop = next?'nextSibling':'previousSibling'; - while(node){ - if(node.nodeType!=3){ - break; - } - len += node.length - node = node[prop]; - } - return [node,len]; -} - -dijit.range._w3c = Boolean(window['getSelection']); -dijit.range.create = function(/*Window?*/win){ - if(dijit.range._w3c){ - return (win || dojo.global).document.createRange(); - }else{//IE - return new dijit.range.W3CRange; - } -} - -dijit.range.getSelection = function(/*Window*/win, /*Boolean?*/ignoreUpdate){ - if(dijit.range._w3c){ - return win.getSelection(); - }else{//IE - var s = new dijit.range.ie.selection(win); - if(!ignoreUpdate){ - s._getCurrentSelection(); - } - return s; - } -} - -if(!dijit.range._w3c){ - dijit.range.ie={ - cachedSelection: {}, - selection: function(win){ - this._ranges = []; - this.addRange = function(r, /*boolean*/internal){ - this._ranges.push(r); - if(!internal){ - r._select(); - } - this.rangeCount = this._ranges.length; - }; - this.removeAllRanges = function(){ - //don't detach, the range may be used later -// for(var i=0;i<this._ranges.length;i++){ -// this._ranges[i].detach(); -// } - this._ranges = []; - this.rangeCount = 0; - }; - var _initCurrentRange = function(){ - var r = win.document.selection.createRange(); - var type=win.document.selection.type.toUpperCase(); - if(type == "CONTROL"){ - //TODO: multiple range selection(?) - return new dijit.range.W3CRange(dijit.range.ie.decomposeControlRange(r)); - }else{ - return new dijit.range.W3CRange(dijit.range.ie.decomposeTextRange(r)); - } - }; - this.getRangeAt = function(i){ - return this._ranges[i]; - }; - this._getCurrentSelection = function(){ - this.removeAllRanges(); - var r=_initCurrentRange(); - if(r){ - this.addRange(r, true); - } - }; - }, - decomposeControlRange: function(range){ - var firstnode = range.item(0), lastnode = range.item(range.length-1); - var startContainer = firstnode.parentNode, endContainer = lastnode.parentNode; - var startOffset = dijit.range.getIndex(firstnode, startContainer).o; - var endOffset = dijit.range.getIndex(lastnode, endContainer).o+1; - return [startContainer, startOffset,endContainer, endOffset]; - }, - getEndPoint: function(range, end){ - var atmrange = range.duplicate(); - atmrange.collapse(!end); - var cmpstr = 'EndTo' + (end?'End':'Start'); - var parentNode = atmrange.parentElement(); - - var startnode, startOffset, lastNode; - if(parentNode.childNodes.length>0){ - dojo.every(parentNode.childNodes, function(node,i){ - var calOffset; - if(node.nodeType != 3){ - atmrange.moveToElementText(node); - - if(atmrange.compareEndPoints(cmpstr,range) > 0){ - //startnode = node.previousSibling; - if(lastNode && lastNode.nodeType == 3){ - //where shall we put the start? in the text node or after? - startnode = lastNode; - calOffset = true; - }else{ - startnode = parentNode; - startOffset = i; - return false; - } - }else{ - if(i == parentNode.childNodes.length-1){ - startnode = parentNode; - startOffset = parentNode.childNodes.length; - return false; - } - } - }else{ - if(i == parentNode.childNodes.length-1){//at the end of this node - startnode = node; - calOffset = true; - } - } - // try{ - if(calOffset && startnode){ - var prevnode = dijit.range.adjacentNoneTextNode(startnode)[0]; - if(prevnode){ - startnode = prevnode.nextSibling; - }else{ - startnode = parentNode.firstChild; //firstChild must be a text node - } - var prevnodeobj = dijit.range.adjacentNoneTextNode(startnode); - prevnode = prevnodeobj[0]; - var lenoffset = prevnodeobj[1]; - if(prevnode){ - atmrange.moveToElementText(prevnode); - atmrange.collapse(false); - }else{ - atmrange.moveToElementText(parentNode); - } - atmrange.setEndPoint(cmpstr, range); - startOffset = atmrange.text.length-lenoffset; - - return false; - } - // }catch(e){ debugger } - lastNode = node; - return true; - }); - }else{ - startnode = parentNode; - startOffset = 0; - } - - //if at the end of startnode and we are dealing with start container, then - //move the startnode to nextSibling if it is a text node - //TODO: do this for end container? - if(!end && startnode.nodeType == 1 && startOffset == startnode.childNodes.length){ - var nextnode=startnode.nextSibling; - if(nextnode && nextnode.nodeType == 3){ - startnode = nextnode; - startOffset = 0; - } - } - return [startnode, startOffset]; - }, - setEndPoint: function(range, container, offset){ - //text node - var atmrange = range.duplicate(), node, len; - if(container.nodeType!=3){ //normal node - if(offset > 0){ - node = container.childNodes[offset-1]; - if(node){ - if(node.nodeType == 3){ - container = node; - offset = node.length; - //pass through - }else{ - if(node.nextSibling && node.nextSibling.nodeType == 3){ - container=node.nextSibling; - offset=0; - //pass through - }else{ - atmrange.moveToElementText(node.nextSibling?node:container); - var parent = node.parentNode; - var tempNode = parent.insertBefore(node.ownerDocument.createTextNode(' '), node.nextSibling); - atmrange.collapse(false); - parent.removeChild(tempNode); - } - } - } - }else{ - atmrange.moveToElementText(container); - atmrange.collapse(true); - } - } - if(container.nodeType == 3){ - var prevnodeobj = dijit.range.adjacentNoneTextNode(container); - var prevnode = prevnodeobj[0]; - len = prevnodeobj[1]; - if(prevnode){ - atmrange.moveToElementText(prevnode); - atmrange.collapse(false); - //if contentEditable is not inherit, the above collapse won't make the end point - //in the correctly position: it always has a -1 offset, so compensate it - if(prevnode.contentEditable!='inherit'){ - len++; - } - }else{ - atmrange.moveToElementText(container.parentNode); - atmrange.collapse(true); - } - - offset += len; - if(offset>0){ - if(atmrange.move('character',offset) != offset){ - console.error('Error when moving!'); - } - } - } - - return atmrange; - }, - decomposeTextRange: function(range){ - var tmpary = dijit.range.ie.getEndPoint(range); - var startContainer = tmpary[0], startOffset = tmpary[1]; - var endContainer = tmpary[0], endOffset = tmpary[1]; - - if(range.htmlText.length){ - if(range.htmlText == range.text){ //in the same text node - endOffset = startOffset+range.text.length; - }else{ - tmpary = dijit.range.ie.getEndPoint(range,true); - endContainer = tmpary[0], endOffset = tmpary[1]; -// if(startContainer.tagName == "BODY"){ -// startContainer = startContainer.firstChild; -// } - } - } - return [startContainer, startOffset, endContainer, endOffset]; - }, - setRange: function(range, startContainer, - startOffset, endContainer, endOffset, collapsed){ - var start=dijit.range.ie.setEndPoint(range, startContainer, startOffset); - - range.setEndPoint('StartToStart',start); - if(!collapsed){ - var end=dijit.range.ie.setEndPoint(range, endContainer, endOffset); - } - range.setEndPoint('EndToEnd',end || start); - - return range; - } - } - -dojo.declare("dijit.range.W3CRange",null, { - constructor: function(){ - if(arguments.length>0){ - this.setStart(arguments[0][0],arguments[0][1]); - this.setEnd(arguments[0][2],arguments[0][3]); - }else{ - this.commonAncestorContainer = null; - this.startContainer = null; - this.startOffset = 0; - this.endContainer = null; - this.endOffset = 0; - this.collapsed = true; - } - }, - _updateInternal: function(){ - if(this.startContainer !== this.endContainer){ - this.commonAncestorContainer = dijit.range.getCommonAncestor(this.startContainer, this.endContainer); - }else{ - this.commonAncestorContainer = this.startContainer; - } - this.collapsed = (this.startContainer === this.endContainer) && (this.startOffset == this.endOffset); - }, - setStart: function(node, offset){ - offset=parseInt(offset); - if(this.startContainer === node && this.startOffset == offset){ - return; - } - delete this._cachedBookmark; - - this.startContainer = node; - this.startOffset = offset; - if(!this.endContainer){ - this.setEnd(node, offset); - }else{ - this._updateInternal(); - } - }, - setEnd: function(node, offset){ - offset=parseInt(offset); - if(this.endContainer === node && this.endOffset == offset){ - return; - } - delete this._cachedBookmark; - - this.endContainer = node; - this.endOffset = offset; - if(!this.startContainer){ - this.setStart(node, offset); - }else{ - this._updateInternal(); - } - }, - setStartAfter: function(node, offset){ - this._setPoint('setStart', node, offset, 1); - }, - setStartBefore: function(node, offset){ - this._setPoint('setStart', node, offset, 0); - }, - setEndAfter: function(node, offset){ - this._setPoint('setEnd', node, offset, 1); - }, - setEndBefore: function(node, offset){ - this._setPoint('setEnd', node, offset, 0); - }, - _setPoint: function(what, node, offset, ext){ - var index = dijit.range.getIndex(node, node.parentNode).o; - this[what](node.parentNode, index.pop()+ext); - }, - _getIERange: function(){ - var r = (this._body || this.endContainer.ownerDocument.body).createTextRange(); - dijit.range.ie.setRange(r, this.startContainer, this.startOffset, this.endContainer, this.endOffset, this.collapsed); - return r; - }, - getBookmark: function(body){ - this._getIERange(); - return this._cachedBookmark; - }, - _select: function(){ - var r = this._getIERange(); - r.select(); - }, - deleteContents: function(){ - var r = this._getIERange(); - r.pasteHTML(''); - this.endContainer = this.startContainer; - this.endOffset = this.startOffset; - this.collapsed = true; - }, - cloneRange: function(){ - var r = new dijit.range.W3CRange([this.startContainer,this.startOffset, - this.endContainer,this.endOffset]); - r._body = this._body; - return r; - }, - detach: function(){ - this._body = null; - this.commonAncestorContainer = null; - this.startContainer = null; - this.startOffset = 0; - this.endContainer = null; - this.endOffset = 0; - this.collapsed = true; -} -}); -} //if(!dijit.range._w3c) - -} - -if(!dojo._hasResource["dijit._editor.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit._editor.html"] = true; -dojo.provide("dijit._editor.html"); - -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, ">").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 </span> without an open tag <span>, as - // a new tag with tagName of /span. Corrupts output HTML, remove - // them. Other browsers don't prefix tags that way, so will - // never show up. - return ""; - } - output = '<' + lName; - - //store the list of attributes and sort it to have the - //attributes appear in the dictionary order - var attrarray = []; - var attr; - if(dojo.isIE && 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] + '="' + - (dojo.isString(attr[1]) ? dijit._editor.escapeXml(attr[1], true) : attr[1]) + '"'; - } - if(lName === "script"){ - // Browsers handle script tags differently in how you get content, - // but innerHTML always seems to work, so insert its content that way - // Yes, it's bad to allow script tags in the editor code, but some people - // seem to want to do it, so we need to at least return them right. - // other plugins/filters can strip them. - output += '>' + node.innerHTML +'</' + lName + '>'; - }else{ - if(node.childNodes.length){ - output += '>' + dijit._editor.getChildrenHtml(node)+'</' + lName +'>'; - }else{ - switch(lName){ - case 'br': - case 'hr': - case 'img': - case 'input': - case 'base': - case 'meta': - case 'area': - case 'basefont': - // These should all be singly closed - output += ' />'; - break; - default: - // Assume XML style separate closure for everything else. - output += '></' + lName + '>'; - } - } - } - break; - case 4: // cdata - case 3: // text - // FIXME: - output = dijit._editor.escapeXml(node.nodeValue, true); - break; - case 8: //comment - // FIXME: - output = '<!--' + dijit._editor.escapeXml(node.nodeValue, true) + '-->'; - break; - default: - output = "<!-- Element not recognized - Type: " + node.nodeType + " Name: " + node.nodeName + "-->"; - } - return output; -}; - -dijit._editor.getChildrenHtml = function(/* DomNode */dom){ - // summary: - // Returns the html content of a DomNode and children - var out = ""; - if(!dom){ return out; } - var nodes = dom["childNodes"] || dom; - - //IE issue. - //If we have an actual node we can check parent relationships on for IE, - //We should check, as IE sometimes builds invalid DOMS. If no parent, we can't check - //And should just process it and hope for the best. - var checkParent = !dojo.isIE || 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 -}; - -} - -if(!dojo._hasResource["dijit._editor.RichText"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit._editor.RichText"] = true; -dojo.provide("dijit._editor.RichText"); - - - - - - - -// used to restore content when user leaves this page then comes back -// but do not try doing dojo.doc.write if we are using xd loading. -// dojo.doc.write will only work if RichText.js is included in the dojo.js -// file. If it is included in dojo.js and you want to allow rich text saving -// for back/forward actions, then set dojo.config.allowXdRichTextSave = true. -if(!dojo.config["useXDomain"] || dojo.config["allowXdRichTextSave"]){ - if(dojo._postLoad){ - (function(){ - var savetextarea = dojo.doc.createElement('textarea'); - savetextarea.id = dijit._scopeName + "._editor.RichText.savedContent"; - dojo.style(savetextarea, { - display:'none', - position:'absolute', - top:"-100px", - height:"3px", - width:"3px" - }); - dojo.body().appendChild(savetextarea); - })(); - }else{ - //dojo.body() is not available before onLoad is fired - try{ - dojo.doc.write('<textarea id="' + dijit._scopeName + '._editor.RichText.savedContent" ' + - 'style="display:none;position:absolute;top:-100px;left:-100px;height:3px;width:3px;overflow:hidden;"></textarea>'); - }catch(e){ } - } -} - -dojo.declare("dijit._editor.RichText", [dijit._Widget, dijit._CssStateMixin], { - constructor: function(params){ - // summary: - // dijit._editor.RichText is the core of dijit.Editor, which provides basic - // WYSIWYG editing features. - // - // description: - // dijit._editor.RichText is the core of dijit.Editor, which provides basic - // WYSIWYG editing features. It also encapsulates the differences - // of different js engines for various browsers. Do not use this widget - // with an HTML <TEXTAREA> tag, since the browser unescapes XML escape characters, - // like <. This can have unexpected behavior and lead to security issues - // such as scripting attacks. - // - // tags: - // private - - // contentPreFilters: Function(String)[] - // Pre content filter function register array. - // these filters will be executed before the actual - // editing area gets the html content. - this.contentPreFilters = []; - - // contentPostFilters: Function(String)[] - // post content filter function register array. - // These will be used on the resulting html - // from contentDomPostFilters. The resulting - // content is the final html (returned by getValue()). - this.contentPostFilters = []; - - // contentDomPreFilters: Function(DomNode)[] - // Pre content dom filter function register array. - // These filters are applied after the result from - // contentPreFilters are set to the editing area. - this.contentDomPreFilters = []; - - // contentDomPostFilters: Function(DomNode)[] - // Post content dom filter function register array. - // These filters are executed on the editing area dom. - // The result from these will be passed to contentPostFilters. - this.contentDomPostFilters = []; - - // editingAreaStyleSheets: dojo._URL[] - // array to store all the stylesheets applied to the editing area - this.editingAreaStyleSheets = []; - - // Make a copy of this.events before we start writing into it, otherwise we - // will modify the prototype which leads to bad things on pages w/multiple editors - this.events = [].concat(this.events); - - this._keyHandlers = {}; - this.contentPreFilters.push(dojo.hitch(this, "_preFixUrlAttributes")); - if(dojo.isMoz){ - this.contentPreFilters.push(this._normalizeFontStyle); - this.contentPostFilters.push(this._removeMozBogus); - } - if(dojo.isWebKit){ - // Try to clean up WebKit bogus artifacts. The inserted classes - // made by WebKit sometimes messes things up. - this.contentPreFilters.push(this._removeWebkitBogus); - this.contentPostFilters.push(this._removeWebkitBogus); - } - if(dojo.isIE){ - // IE generates <strong> and <em> but we want to normalize to <b> and <i> - this.contentPostFilters.push(this._normalizeFontStyle); - } - //this.contentDomPostFilters.push(this._postDomFixUrlAttributes); - - if(params && dojo.isString(params.value)){ - this.value = params.value; - } - - this.onLoadDeferred = new dojo.Deferred(); - }, - - baseClass: "dijitEditor", - - // inheritWidth: Boolean - // whether to inherit the parent's width or simply use 100% - inheritWidth: false, - - // focusOnLoad: [deprecated] Boolean - // Focus into this widget when the page is loaded - focusOnLoad: false, - - // name: String? - // Specifies the name of a (hidden) <textarea> node on the page that's used to save - // the editor content on page leave. Used to restore editor contents after navigating - // to a new page and then hitting the back button. - name: "", - - // styleSheets: [const] String - // semicolon (";") separated list of css files for the editing area - styleSheets: "", - - // _content: [private] String - // temporary content storage - _content: "", - - // height: String - // Set height to fix the editor at a specific height, with scrolling. - // By default, this is 300px. If you want to have the editor always - // resizes to accommodate the content, use AlwaysShowToolbar plugin - // and set height="". If this editor is used within a layout widget, - // set height="100%". - height: "300px", - - // minHeight: String - // The minimum height that the editor should have. - minHeight: "1em", - - // isClosed: [private] Boolean - isClosed: true, - - // isLoaded: [private] Boolean - isLoaded: false, - - // _SEPARATOR: [private] String - // Used to concat contents from multiple editors into a single string, - // so they can be saved into a single <textarea> node. See "name" attribute. - _SEPARATOR: "@@**%%__RICHTEXTBOUNDRY__%%**@@", - - // onLoadDeferred: [protected] dojo.Deferred - // Deferred which is fired when the editor finishes loading - onLoadDeferred: null, - - // isTabIndent: Boolean - // Make tab key and shift-tab indent and outdent rather than navigating. - // Caution: sing this makes web pages inaccessible to users unable to use a mouse. - isTabIndent: false, - - // disableSpellCheck: [const] Boolean - // When true, disables the browser's native spell checking, if supported. - // Works only in Firefox. - disableSpellCheck: false, - - postCreate: function(){ - if("textarea" == this.domNode.tagName.toLowerCase()){ - console.warn("RichText should not be used with the TEXTAREA tag. See dijit._editor.RichText docs."); - } - - this.inherited(arguments); - - dojo.publish(dijit._scopeName + "._editor.RichText::init", [this]); - this.open(); - this.setupDefaultShortcuts(); - }, - - setupDefaultShortcuts: function(){ - // summary: - // Add some default key handlers - // description: - // Overwrite this to setup your own handlers. The default - // implementation does not use Editor commands, but directly - // executes the builtin commands within the underlying browser - // support. - // tags: - // protected - var exec = dojo.hitch(this, function(cmd, arg){ - return function(){ - return !this.execCommand(cmd,arg); - }; - }); - - var ctrlKeyHandlers = { - b: exec("bold"), - i: exec("italic"), - u: exec("underline"), - a: exec("selectall"), - s: function(){ this.save(true); }, - m: function(){ this.isTabIndent = !this.isTabIndent; }, - - "1": exec("formatblock", "h1"), - "2": exec("formatblock", "h2"), - "3": exec("formatblock", "h3"), - "4": exec("formatblock", "h4"), - - "\\": exec("insertunorderedlist") - }; - - if(!dojo.isIE){ - ctrlKeyHandlers.Z = exec("redo"); //FIXME: undo? - } - - for(var key in ctrlKeyHandlers){ - this.addKeyHandler(key, true, false, ctrlKeyHandlers[key]); - } - }, - - // events: [private] String[] - // events which should be connected to the underlying editing area - events: ["onKeyPress", "onKeyDown", "onKeyUp", "onClick"], - - // captureEvents: [deprecated] String[] - // Events which should be connected to the underlying editing - // area, events in this array will be addListener with - // capture=true. - // TODO: looking at the code I don't see any distinction between events and captureEvents, - // so get rid of this for 2.0 if not sooner - captureEvents: [], - - _editorCommandsLocalized: false, - _localizeEditorCommands: function(){ - // summary: - // When IE is running in a non-English locale, the API actually changes, - // so that we have to say (for example) danraku instead of p (for paragraph). - // Handle that here. - // tags: - // private - if(this._editorCommandsLocalized){ - return; - } - this._editorCommandsLocalized = true; - - //in IE, names for blockformat is locale dependent, so we cache the values here - - //if the normal way fails, we try the hard way to get the list - - //do not use _cacheLocalBlockFormatNames here, as it will - //trigger security warning in IE7 - - //put p after div, so if IE returns Normal, we show it as paragraph - //We can distinguish p and div if IE returns Normal, however, in order to detect that, - //we have to call this.document.selection.createRange().parentElement() or such, which - //could slow things down. Leave it as it is for now - var formats = ['div', 'p', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul', 'address']; - var localhtml = "", format, i=0; - while((format=formats[i++])){ - //append a <br> after each element to separate the elements more reliably - if(format.charAt(1) != 'l'){ - localhtml += "<"+format+"><span>content</span></"+format+"><br/>"; - }else{ - localhtml += "<"+format+"><li>content</li></"+format+"><br/>"; - } - } - //queryCommandValue returns empty if we hide editNode, so move it out of screen temporary - var div = dojo.doc.createElement('div'); - dojo.style(div, { - position: "absolute", - top: "-2000px" - }); - dojo.doc.body.appendChild(div); - div.innerHTML = localhtml; - var node = div.firstChild; - while(node){ - dijit._editor.selection.selectElement(node.firstChild); - dojo.withGlobal(this.window, "selectElement", dijit._editor.selection, [node.firstChild]); - var nativename = node.tagName.toLowerCase(); - this._local2NativeFormatNames[nativename] = document.queryCommandValue("formatblock"); - //this.queryCommandValue("formatblock"); - this._native2LocalFormatNames[this._local2NativeFormatNames[nativename]] = nativename; - node = node.nextSibling.nextSibling; - } - dojo.body().removeChild(div); - }, - - open: function(/*DomNode?*/ element){ - // summary: - // Transforms the node referenced in this.domNode into a rich text editing - // node. - // description: - // Sets up the editing area asynchronously. This will result in - // the creation and replacement with an iframe. - // - // A dojo.Deferred object is created at this.onLoadDeferred, and - // users may attach to it to be informed when the rich-text area - // initialization is finalized. - // tags: - // private - - if(!this.onLoadDeferred || this.onLoadDeferred.fired >= 0){ - this.onLoadDeferred = new dojo.Deferred(); - } - - if(!this.isClosed){ this.close(); } - dojo.publish(dijit._scopeName + "._editor.RichText::open", [ this ]); - - this._content = ""; - if(arguments.length == 1 && element.nodeName){ // else unchanged - this.domNode = element; - } - - var dn = this.domNode; - - // "html" will hold the innerHTML of the srcNodeRef and will be used to - // initialize the editor. - var html; - - if(dojo.isString(this.value)){ - // Allow setting the editor content programmatically instead of - // relying on the initial content being contained within the target - // domNode. - html = this.value; - delete this.value; - dn.innerHTML = ""; - }else if(dn.nodeName && dn.nodeName.toLowerCase() == "textarea"){ - // if we were created from a textarea, then we need to create a - // new editing harness node. - var ta = (this.textarea = dn); - this.name = ta.name; - html = ta.value; - dn = this.domNode = dojo.doc.createElement("div"); - dn.setAttribute('widgetId', this.id); - ta.removeAttribute('widgetId'); - dn.cssText = ta.cssText; - dn.className += " " + ta.className; - dojo.place(dn, ta, "before"); - var tmpFunc = dojo.hitch(this, function(){ - //some browsers refuse to submit display=none textarea, so - //move the textarea off screen instead - dojo.style(ta, { - display: "block", - position: "absolute", - top: "-1000px" - }); - - if(dojo.isIE){ //nasty IE bug: abnormal formatting if overflow is not hidden - var s = ta.style; - this.__overflow = s.overflow; - s.overflow = "hidden"; - } - }); - if(dojo.isIE){ - setTimeout(tmpFunc, 10); - }else{ - tmpFunc(); - } - - if(ta.form){ - dojo.connect(ta.form, "onsubmit", this, function(){ - // FIXME: should we be calling close() here instead? - ta.value = this.getValue(); - }); - } - }else{ - html = dijit._editor.getChildrenHtml(dn); - dn.innerHTML = ""; - } - - var content = dojo.contentBox(dn); - this._oldHeight = content.h; - this._oldWidth = content.w; - - this.savedContent = html; - - // If we're a list item we have to put in a blank line to force the - // bullet to nicely align at the top of text - if(dn.nodeName && dn.nodeName == "LI"){ - dn.innerHTML = " <br>"; - } - - // Construct the editor div structure. - this.header = dn.ownerDocument.createElement("div"); - dn.appendChild(this.header); - this.editingArea = dn.ownerDocument.createElement("div"); - dn.appendChild(this.editingArea); - this.footer = dn.ownerDocument.createElement("div"); - dn.appendChild(this.footer); - - // User has pressed back/forward button so we lost the text in the editor, but it's saved - // in a hidden <textarea> (which contains the data for all the editors on this page), - // so get editor value from there - if(this.name !== "" && (!dojo.config["useXDomain"] || dojo.config["allowXdRichTextSave"])){ - var saveTextarea = dojo.byId(dijit._scopeName + "._editor.RichText.savedContent"); - if(saveTextarea.value !== ""){ - var datas = saveTextarea.value.split(this._SEPARATOR), i=0, dat; - while((dat=datas[i++])){ - var data = dat.split(":"); - if(data[0] == this.name){ - html = data[1]; - datas.splice(i, 1); // TODO: this has no effect - break; - } - } - } - - // TODO: this is troublesome if this editor has been destroyed, should have global handler. - // TODO: need to clear <textarea> in global handler - dojo.addOnUnload(dojo.hitch(this, "_saveContent")); - } - - this.isClosed = false; - - var ifr = (this.editorObject = this.iframe = dojo.doc.createElement('iframe')); - ifr.id = this.id+"_iframe"; - this._iframeSrc = this._getIframeDocTxt(); - ifr.style.border = "none"; - ifr.style.width = "100%"; - if(this._layoutMode){ - // iframe should be 100% height, thus getting it's height from surrounding - // <div> (which has the correct height set by Editor) - ifr.style.height = "100%"; - }else{ - if(dojo.isIE >= 7){ - if(this.height){ - ifr.style.height = this.height; - } - if(this.minHeight){ - ifr.style.minHeight = this.minHeight; - } - }else{ - ifr.style.height = this.height ? this.height : this.minHeight; - } - } - ifr.frameBorder = 0; - ifr._loadFunc = dojo.hitch( this, function(win){ - this.window = win; - this.document = this.window.document; - - if(dojo.isIE){ - this._localizeEditorCommands(); - } - - // Do final setup and set initial contents of editor - this.onLoad(html); - }); - - // Set the iframe's initial (blank) content. - var s = 'javascript:parent.' + dijit._scopeName + '.byId("'+this.id+'")._iframeSrc'; - ifr.setAttribute('src', s); - this.editingArea.appendChild(ifr); - - // TODO: this is a guess at the default line-height, kinda works - if(dn.nodeName == "LI"){ - dn.lastChild.style.marginTop = "-1.2em"; - } - - dojo.addClass(this.domNode, this.baseClass); - }, - - //static cache variables shared among all instance of this class - _local2NativeFormatNames: {}, - _native2LocalFormatNames: {}, - - _getIframeDocTxt: function(){ - // summary: - // Generates the boilerplate text of the document inside the iframe (ie, <html><head>...</head><body/></html>). - // Editor content (if not blank) should be added afterwards. - // tags: - // private - var _cs = dojo.getComputedStyle(this.domNode); - - // The contents inside of <body>. The real contents are set later via a call to setValue(). - var html = ""; - var setBodyId = true; - if(dojo.isIE || (!this.height && !dojo.isMoz)){ - // In auto-expand mode, need a wrapper div for AlwaysShowToolbar plugin to correctly - // expand/contract the editor as the content changes. - html = "<div id='dijitEditorBody'></div>"; - setBodyId = false; - }else if(dojo.isMoz){ - // workaround bug where can't select then delete text (until user types something - // into the editor)... and/or issue where typing doesn't erase selected text - this._cursorToStart = true; - html = " "; - } - - var font = [ _cs.fontWeight, _cs.fontSize, _cs.fontFamily ].join(" "); - - // line height is tricky - applying a units value will mess things up. - // if we can't get a non-units value, bail out. - var lineHeight = _cs.lineHeight; - if(lineHeight.indexOf("px") >= 0){ - lineHeight = parseFloat(lineHeight)/parseFloat(_cs.fontSize); - // console.debug(lineHeight); - }else if(lineHeight.indexOf("em")>=0){ - lineHeight = parseFloat(lineHeight); - }else{ - // If we can't get a non-units value, just default - // it to the CSS spec default of 'normal'. Seems to - // work better, esp on IE, than '1.0' - lineHeight = "normal"; - } - var userStyle = ""; - var self = this; - this.style.replace(/(^|;)\s*(line-|font-?)[^;]+/ig, function(match){ - match = match.replace(/^;/ig,"") + ';'; - var s = match.split(":")[0]; - if(s){ - s = dojo.trim(s); - s = s.toLowerCase(); - var i; - var sC = ""; - for(i = 0; i < s.length; i++){ - var c = s.charAt(i); - switch(c){ - case "-": - i++; - c = s.charAt(i).toUpperCase(); - default: - sC += c; - } - } - dojo.style(self.domNode, sC, ""); - } - userStyle += match + ';'; - }); - - - // need to find any associated label element and update iframe document title - var label=dojo.query('label[for="'+this.id+'"]'); - - return [ - this.isLeftToRight() ? "<html>\n<head>\n" : "<html dir='rtl'>\n<head>\n", - (dojo.isMoz && label.length ? "<title>" + label[0].innerHTML + "</title>\n" : ""), - "<meta http-equiv='Content-Type' content='text/html'>\n", - "<style>\n", - "\tbody,html {\n", - "\t\tbackground:transparent;\n", - "\t\tpadding: 1px 0 0 0;\n", - "\t\tmargin: -1px 0 0 0;\n", // remove extraneous vertical scrollbar on safari and firefox - - // Set the html/body sizing. Webkit always needs this, other browsers - // only set it when height is defined (not auto-expanding), otherwise - // scrollers do not appear. - ((dojo.isWebKit)?"\t\twidth: 100%;\n":""), - ((dojo.isWebKit)?"\t\theight: 100%;\n":""), - "\t}\n", - - // TODO: left positioning will cause contents to disappear out of view - // if it gets too wide for the visible area - "\tbody{\n", - "\t\ttop:0px;\n", - "\t\tleft:0px;\n", - "\t\tright:0px;\n", - "\t\tfont:", font, ";\n", - ((this.height||dojo.isOpera) ? "" : "\t\tposition: fixed;\n"), - // FIXME: IE 6 won't understand min-height? - "\t\tmin-height:", this.minHeight, ";\n", - "\t\tline-height:", lineHeight,";\n", - "\t}\n", - "\tp{ margin: 1em 0; }\n", - - // Determine how scrollers should be applied. In autoexpand mode (height = "") no scrollers on y at all. - // But in fixed height mode we want both x/y scrollers. Also, if it's using wrapping div and in auto-expand - // (Mainly IE) we need to kill the y scroller on body and html. - (!setBodyId && !this.height ? "\tbody,html {overflow-y: hidden;}\n" : ""), - "\t#dijitEditorBody{overflow-x: auto; overflow-y:" + (this.height ? "auto;" : "hidden;") + "}\n", - "\tli > ul:-moz-first-node, li > ol:-moz-first-node{ padding-top: 1.2em; }\n", - "\tli{ min-height:1.2em; }\n", - "</style>\n", - this._applyEditingAreaStyleSheets(),"\n", - "</head>\n<body ", - (setBodyId?"id='dijitEditorBody' ":""), - "onload='frameElement._loadFunc(window,document)' style='"+userStyle+"'>", html, "</body>\n</html>" - ].join(""); // String - }, - - _applyEditingAreaStyleSheets: function(){ - // summary: - // apply the specified css files in styleSheets - // tags: - // private - var files = []; - if(this.styleSheets){ - files = this.styleSheets.split(';'); - this.styleSheets = ''; - } - - //empty this.editingAreaStyleSheets here, as it will be filled in addStyleSheet - files = files.concat(this.editingAreaStyleSheets); - this.editingAreaStyleSheets = []; - - var text='', i=0, url; - while((url=files[i++])){ - var abstring = (new dojo._Url(dojo.global.location, url)).toString(); - this.editingAreaStyleSheets.push(abstring); - text += '<link rel="stylesheet" type="text/css" href="'+abstring+'"/>'; - } - return text; - }, - - addStyleSheet: function(/*dojo._Url*/ uri){ - // summary: - // add an external stylesheet for the editing area - // uri: - // A dojo.uri.Uri pointing to the url of the external css file - var url=uri.toString(); - - //if uri is relative, then convert it to absolute so that it can be resolved correctly in iframe - if(url.charAt(0) == '.' || (url.charAt(0) != '/' && !uri.host)){ - url = (new dojo._Url(dojo.global.location, url)).toString(); - } - - if(dojo.indexOf(this.editingAreaStyleSheets, url) > -1){ -// console.debug("dijit._editor.RichText.addStyleSheet: Style sheet "+url+" is already applied"); - return; - } - - this.editingAreaStyleSheets.push(url); - this.onLoadDeferred.addCallback(dojo.hitch(function(){ - if(this.document.createStyleSheet){ //IE - this.document.createStyleSheet(url); - }else{ //other browser - var head = this.document.getElementsByTagName("head")[0]; - var stylesheet = this.document.createElement("link"); - stylesheet.rel="stylesheet"; - stylesheet.type="text/css"; - stylesheet.href=url; - head.appendChild(stylesheet); - } - })); - }, - - removeStyleSheet: function(/*dojo._Url*/ uri){ - // summary: - // remove an external stylesheet for the editing area - var url=uri.toString(); - //if uri is relative, then convert it to absolute so that it can be resolved correctly in iframe - if(url.charAt(0) == '.' || (url.charAt(0) != '/' && !uri.host)){ - url = (new dojo._Url(dojo.global.location, url)).toString(); - } - var index = dojo.indexOf(this.editingAreaStyleSheets, url); - if(index == -1){ -// console.debug("dijit._editor.RichText.removeStyleSheet: Style sheet "+url+" has not been applied"); - return; - } - delete this.editingAreaStyleSheets[index]; - dojo.withGlobal(this.window,'query', dojo, ['link:[href="'+url+'"]']).orphan(); - }, - - // disabled: Boolean - // The editor is disabled; the text cannot be changed. - disabled: false, - - _mozSettingProps: {'styleWithCSS':false}, - _setDisabledAttr: function(/*Boolean*/ value){ - this.disabled = value; - if(!this.isLoaded){ return; } // this method requires init to be complete - value = !!value; - if(dojo.isIE || dojo.isWebKit || dojo.isOpera){ - var preventIEfocus = dojo.isIE && (this.isLoaded || !this.focusOnLoad); - if(preventIEfocus){ this.editNode.unselectable = "on"; } - this.editNode.contentEditable = !value; - if(preventIEfocus){ - var _this = this; - setTimeout(function(){ _this.editNode.unselectable = "off"; }, 0); - } - }else{ //moz - try{ - this.document.designMode=(value?'off':'on'); - }catch(e){ return; } // ! _disabledOK - if(!value && this._mozSettingProps){ - var ps = this._mozSettingProps; - for(var n in ps){ - if(ps.hasOwnProperty(n)){ - try{ - this.document.execCommand(n,false,ps[n]); - }catch(e2){} - } - } - } -// this.document.execCommand('contentReadOnly', false, value); -// if(value){ -// this.blur(); //to remove the blinking caret -// } - } - this._disabledOK = true; - }, - -/* Event handlers - *****************/ - - onLoad: function(/*String*/ html){ - // summary: - // Handler after the iframe finishes loading. - // html: String - // Editor contents should be set to this value - // tags: - // protected - - // TODO: rename this to _onLoad, make empty public onLoad() method, deprecate/make protected onLoadDeferred handler? - - if(!this.window.__registeredWindow){ - this.window.__registeredWindow = true; - this._iframeRegHandle = dijit.registerIframe(this.iframe); - } - if(!dojo.isIE && (this.height || dojo.isMoz)){ - this.editNode=this.document.body; - }else{ - // there's a wrapper div around the content, see _getIframeDocTxt(). - this.editNode=this.document.body.firstChild; - var _this = this; - if(dojo.isIE){ // #4996 IE wants to focus the BODY tag - var tabStop = (this.tabStop = dojo.doc.createElement('<div tabIndex=-1>')); - this.editingArea.appendChild(tabStop); - this.iframe.onfocus = function(){ _this.editNode.setActive(); }; - } - } - this.focusNode = this.editNode; // for InlineEditBox - - - var events = this.events.concat(this.captureEvents); - var ap = this.iframe ? this.document : this.editNode; - dojo.forEach(events, function(item){ - this.connect(ap, item.toLowerCase(), item); - }, this); - - if(dojo.isIE){ // IE contentEditable - this.connect(this.document, "onmousedown", "_onIEMouseDown"); // #4996 fix focus - - // give the node Layout on IE - // TODO: this may no longer be needed, since we've reverted IE to using an iframe, - // not contentEditable. Removing it would also probably remove the need for creating - // the extra <div> in _getIframeDocTxt() - this.editNode.style.zoom = 1.0; - }else{ - this.connect(this.document, "onmousedown", function(){ - // Clear the moveToStart focus, as mouse - // down will set cursor point. Required to properly - // work with selection/position driven plugins and clicks in - // the window. refs: #10678 - delete this._cursorToStart; - }); - } - - if(dojo.isWebKit){ - //WebKit sometimes doesn't fire right on selections, so the toolbar - //doesn't update right. Therefore, help it out a bit with an additional - //listener. A mouse up will typically indicate a display change, so fire this - //and get the toolbar to adapt. Reference: #9532 - this._webkitListener = this.connect(this.document, "onmouseup", "onDisplayChanged"); - } - - if(dojo.isIE){ - // Try to make sure 'hidden' elements aren't visible in edit mode (like browsers other than IE - // do). See #9103 - try{ - this.document.execCommand('RespectVisibilityInDesign', true, null); - }catch(e){/* squelch */} - } - - this.isLoaded = true; - - this.set('disabled', this.disabled); // initialize content to editable (or not) - - // Note that setValue() call will only work after isLoaded is set to true (above) - - // Set up a function to allow delaying the setValue until a callback is fired - // This ensures extensions like dijit.Editor have a way to hold the value set - // until plugins load (and do things like register filters. - var setContent = dojo.hitch(this, function(){ - this.setValue(html); - if(this.onLoadDeferred){ - this.onLoadDeferred.callback(true); - } - this.onDisplayChanged(); - if(this.focusOnLoad){ - // after the document loads, then set focus after updateInterval expires so that - // onNormalizedDisplayChanged has run to avoid input caret issues - dojo.addOnLoad(dojo.hitch(this, function(){ setTimeout(dojo.hitch(this, "focus"), this.updateInterval); })); - } - // Save off the initial content now - this.savedContent = this.getValue(true); - }); - if(this.setValueDeferred){ - this.setValueDeferred.addCallback(setContent); - }else{ - setContent(); - } - - }, - - onKeyDown: function(/* Event */ e){ - // summary: - // Handler for onkeydown event - // tags: - // protected - - // we need this event at the moment to get the events from control keys - // such as the backspace. It might be possible to add this to Dojo, so that - // keyPress events can be emulated by the keyDown and keyUp detection. - - if(e.keyCode === dojo.keys.TAB && this.isTabIndent ){ - dojo.stopEvent(e); //prevent tab from moving focus out of editor - - // FIXME: this is a poor-man's indent/outdent. It would be - // better if it added 4 " " chars in an undoable way. - // Unfortunately pasteHTML does not prove to be undoable - if(this.queryCommandEnabled((e.shiftKey ? "outdent" : "indent"))){ - this.execCommand((e.shiftKey ? "outdent" : "indent")); - } - } - if(dojo.isIE){ - if(e.keyCode == dojo.keys.TAB && !this.isTabIndent){ - if(e.shiftKey && !e.ctrlKey && !e.altKey){ - // focus the BODY so the browser will tab away from it instead - this.iframe.focus(); - }else if(!e.shiftKey && !e.ctrlKey && !e.altKey){ - // focus the BODY so the browser will tab away from it instead - this.tabStop.focus(); - } - }else if(e.keyCode === dojo.keys.BACKSPACE && this.document.selection.type === "Control"){ - // IE has a bug where if a non-text object is selected in the editor, - // hitting backspace would act as if the browser's back button was - // clicked instead of deleting the object. see #1069 - dojo.stopEvent(e); - this.execCommand("delete"); - }else if((65 <= e.keyCode && e.keyCode <= 90) || - (e.keyCode>=37 && e.keyCode<=40) // FIXME: get this from connect() instead! - ){ //arrow keys - e.charCode = e.keyCode; - this.onKeyPress(e); - } - } - return true; - }, - - onKeyUp: function(e){ - // summary: - // Handler for onkeyup event - // tags: - // callback - return; - }, - - setDisabled: function(/*Boolean*/ disabled){ - // summary: - // Deprecated, use set('disabled', ...) instead. - // tags: - // deprecated - dojo.deprecated('dijit.Editor::setDisabled is deprecated','use dijit.Editor::attr("disabled",boolean) instead', 2.0); - this.set('disabled',disabled); - }, - _setValueAttr: function(/*String*/ value){ - // summary: - // Registers that attr("value", foo) should call setValue(foo) - this.setValue(value); - }, - _setDisableSpellCheckAttr: function(/*Boolean*/ disabled){ - if(this.document){ - dojo.attr(this.document.body, "spellcheck", !disabled); - }else{ - // try again after the editor is finished loading - this.onLoadDeferred.addCallback(dojo.hitch(this, function(){ - dojo.attr(this.document.body, "spellcheck", !disabled); - })); - } - this.disableSpellCheck = disabled; - }, - - onKeyPress: function(e){ - // summary: - // Handle the various key events - // tags: - // protected - - var c = (e.keyChar && e.keyChar.toLowerCase()) || e.keyCode, - handlers = this._keyHandlers[c], - args = arguments; - - if(handlers && !e.altKey){ - dojo.some(handlers, function(h){ - // treat meta- same as ctrl-, for benefit of mac users - if(!(h.shift ^ e.shiftKey) && !(h.ctrl ^ (e.ctrlKey||e.metaKey))){ - if(!h.handler.apply(this, args)){ - e.preventDefault(); - } - return true; - } - }, this); - } - - // function call after the character has been inserted - if(!this._onKeyHitch){ - this._onKeyHitch = dojo.hitch(this, "onKeyPressed"); - } - setTimeout(this._onKeyHitch, 1); - return true; - }, - - addKeyHandler: function(/*String*/ key, /*Boolean*/ ctrl, /*Boolean*/ shift, /*Function*/ handler){ - // summary: - // Add a handler for a keyboard shortcut - // description: - // The key argument should be in lowercase if it is a letter character - // tags: - // protected - if(!dojo.isArray(this._keyHandlers[key])){ - this._keyHandlers[key] = []; - } - //TODO: would be nice to make this a hash instead of an array for quick lookups - this._keyHandlers[key].push({ - shift: shift || false, - ctrl: ctrl || false, - handler: handler - }); - }, - - onKeyPressed: function(){ - // summary: - // Handler for after the user has pressed a key, and the display has been updated. - // (Runs on a timer so that it runs after the display is updated) - // tags: - // private - this.onDisplayChanged(/*e*/); // can't pass in e - }, - - onClick: function(/*Event*/ e){ - // summary: - // Handler for when the user clicks. - // tags: - // private - - // console.info('onClick',this._tryDesignModeOn); - this.onDisplayChanged(e); - }, - - _onIEMouseDown: function(/*Event*/ e){ - // summary: - // IE only to prevent 2 clicks to focus - // tags: - // protected - - if(!this._focused && !this.disabled){ - this.focus(); - } - }, - - _onBlur: function(e){ - // summary: - // Called from focus manager when focus has moved away from this editor - // tags: - // protected - - // console.info('_onBlur') - - this.inherited(arguments); - var _c=this.getValue(true); - - if(_c!=this.savedContent){ - this.onChange(_c); - this.savedContent=_c; - } - }, - _onFocus: function(/*Event*/ e){ - // summary: - // Called from focus manager when focus has moved into this editor - // tags: - // protected - - // console.info('_onFocus') - if(!this.disabled){ - if(!this._disabledOK){ - this.set('disabled', false); - } - this.inherited(arguments); - } - }, - - // TODO: why is this needed - should we deprecate this ? - blur: function(){ - // summary: - // Remove focus from this instance. - // tags: - // deprecated - if(!dojo.isIE && this.window.document.documentElement && this.window.document.documentElement.focus){ - this.window.document.documentElement.focus(); - }else if(dojo.doc.body.focus){ - dojo.doc.body.focus(); - } - }, - - focus: function(){ - // summary: - // Move focus to this editor - if(!this.isLoaded){ - this.focusOnLoad = true; - return; - } - if(this._cursorToStart){ - delete this._cursorToStart; - if(this.editNode.childNodes){ - this.placeCursorAtStart(); // this calls focus() so return - return; - } - } - if(!dojo.isIE){ - dijit.focus(this.iframe); - }else if(this.editNode && this.editNode.focus){ - // editNode may be hidden in display:none div, lets just punt in this case - //this.editNode.focus(); -> causes IE to scroll always (strict and quirks mode) to the top the Iframe - // if we fire the event manually and let the browser handle the focusing, the latest - // cursor position is focused like in FF - this.iframe.fireEvent('onfocus', document.createEventObject()); // createEventObject only in IE - // }else{ - // TODO: should we throw here? - // console.debug("Have no idea how to focus into the editor!"); - } - }, - - // _lastUpdate: 0, - updateInterval: 200, - _updateTimer: null, - onDisplayChanged: function(/*Event*/ e){ - // summary: - // This event will be fired everytime the display context - // changes and the result needs to be reflected in the UI. - // description: - // If you don't want to have update too often, - // onNormalizedDisplayChanged should be used instead - // tags: - // private - - // var _t=new Date(); - if(this._updateTimer){ - clearTimeout(this._updateTimer); - } - if(!this._updateHandler){ - this._updateHandler = dojo.hitch(this,"onNormalizedDisplayChanged"); - } - this._updateTimer = setTimeout(this._updateHandler, this.updateInterval); - }, - onNormalizedDisplayChanged: function(){ - // summary: - // This event is fired every updateInterval ms or more - // description: - // If something needs to happen immediately after a - // user change, please use onDisplayChanged instead. - // tags: - // private - delete this._updateTimer; - }, - onChange: function(newContent){ - // summary: - // This is fired if and only if the editor loses focus and - // the content is changed. - }, - _normalizeCommand: function(/*String*/ cmd, /*Anything?*/argument){ - // summary: - // Used as the advice function by dojo.connect to map our - // normalized set of commands to those supported by the target - // browser. - // tags: - // private - - var command = cmd.toLowerCase(); - if(command == "formatblock"){ - if(dojo.isSafari && argument === undefined){ command = "heading"; } - }else if(command == "hilitecolor" && !dojo.isMoz){ - command = "backcolor"; - } - - return command; - }, - - _qcaCache: {}, - queryCommandAvailable: function(/*String*/ command){ - // summary: - // Tests whether a command is supported by the host. Clients - // SHOULD check whether a command is supported before attempting - // to use it, behaviour for unsupported commands is undefined. - // command: - // The command to test for - // tags: - // private - - // memoizing version. See _queryCommandAvailable for computing version - var ca = this._qcaCache[command]; - if(ca !== undefined){ return ca; } - return (this._qcaCache[command] = this._queryCommandAvailable(command)); - }, - - _queryCommandAvailable: function(/*String*/ command){ - // summary: - // See queryCommandAvailable(). - // tags: - // private - - var ie = 1; - var mozilla = 1 << 1; - var webkit = 1 << 2; - var opera = 1 << 3; - var webkit420 = 1 << 4; - - function isSupportedBy(browsers){ - return { - ie: Boolean(browsers & ie), - mozilla: Boolean(browsers & mozilla), - webkit: Boolean(browsers & webkit), - webkit420: Boolean(browsers & webkit420), - opera: Boolean(browsers & opera) - }; - } - - var supportedBy = null; - - switch(command.toLowerCase()){ - case "bold": case "italic": case "underline": - case "subscript": case "superscript": - case "fontname": case "fontsize": - case "forecolor": case "hilitecolor": - case "justifycenter": case "justifyfull": case "justifyleft": - case "justifyright": case "delete": case "selectall": case "toggledir": - supportedBy = isSupportedBy(mozilla | ie | webkit | opera); - break; - - case "createlink": case "unlink": case "removeformat": - case "inserthorizontalrule": case "insertimage": - case "insertorderedlist": case "insertunorderedlist": - case "indent": case "outdent": case "formatblock": - case "inserthtml": case "undo": case "redo": case "strikethrough": case "tabindent": - supportedBy = isSupportedBy(mozilla | ie | opera | webkit420); - break; - - case "blockdirltr": case "blockdirrtl": - case "dirltr": case "dirrtl": - case "inlinedirltr": case "inlinedirrtl": - supportedBy = isSupportedBy(ie); - break; - case "cut": case "copy": case "paste": - supportedBy = isSupportedBy( ie | mozilla | webkit420); - break; - - case "inserttable": - supportedBy = isSupportedBy(mozilla | ie); - break; - - case "insertcell": case "insertcol": case "insertrow": - case "deletecells": case "deletecols": case "deleterows": - case "mergecells": case "splitcell": - supportedBy = isSupportedBy(ie | mozilla); - break; - - default: return false; - } - - return (dojo.isIE && supportedBy.ie) || - (dojo.isMoz && supportedBy.mozilla) || - (dojo.isWebKit && supportedBy.webkit) || - (dojo.isWebKit > 420 && supportedBy.webkit420) || - (dojo.isOpera && supportedBy.opera); // Boolean return true if the command is supported, false otherwise - }, - - execCommand: function(/*String*/ command, argument){ - // summary: - // Executes a command in the Rich Text area - // command: - // The command to execute - // argument: - // An optional argument to the command - // tags: - // protected - var returnValue; - - //focus() is required for IE to work - //In addition, focus() makes sure after the execution of - //the command, the editor receives the focus as expected - this.focus(); - - command = this._normalizeCommand(command, argument); - - - if(argument !== undefined){ - if(command == "heading"){ - throw new Error("unimplemented"); - }else if((command == "formatblock") && dojo.isIE){ - argument = '<'+argument+'>'; - } - } - - //Check to see if we have any over-rides for commands, they will be functions on this - //widget of the form _commandImpl. If we don't, fall through to the basic native - //exec command of the browser. - var implFunc = "_" + command + "Impl"; - if(this[implFunc]){ - returnValue = this[implFunc](argument); - }else{ - argument = arguments.length > 1 ? argument : null; - if(argument || command!="createlink"){ - returnValue = this.document.execCommand(command, false, argument); - } - } - - this.onDisplayChanged(); - return returnValue; - }, - - queryCommandEnabled: function(/*String*/ command){ - // summary: - // Check whether a command is enabled or not. - // tags: - // protected - if(this.disabled || !this._disabledOK){ return false; } - command = this._normalizeCommand(command); - if(dojo.isMoz || dojo.isWebKit){ - if(command == "unlink"){ // mozilla returns true always - // console.debug(this._sCall("hasAncestorElement", ['a'])); - return this._sCall("hasAncestorElement", ["a"]); - }else if(command == "inserttable"){ - return true; - } - } - //see #4109 - if(dojo.isWebKit){ - if(command == "copy"){ - command = "cut"; - }else if(command == "paste"){ - return true; - } - } - - var elem = dojo.isIE ? this.document.selection.createRange() : this.document; - try{ - return elem.queryCommandEnabled(command); - }catch(e){ - //Squelch, occurs if editor is hidden on FF 3 (and maybe others.) - return false; - } - - }, - - queryCommandState: function(command){ - // summary: - // Check the state of a given command and returns true or false. - // tags: - // protected - - if(this.disabled || !this._disabledOK){ return false; } - command = this._normalizeCommand(command); - try{ - return this.document.queryCommandState(command); - }catch(e){ - //Squelch, occurs if editor is hidden on FF 3 (and maybe others.) - return false; - } - }, - - queryCommandValue: function(command){ - // summary: - // Check the value of a given command. This matters most for - // custom selections and complex values like font value setting. - // tags: - // protected - - if(this.disabled || !this._disabledOK){ return false; } - var r; - command = this._normalizeCommand(command); - if(dojo.isIE && command == "formatblock"){ - r = this._native2LocalFormatNames[this.document.queryCommandValue(command)]; - }else if(dojo.isMoz && command === "hilitecolor"){ - var oldValue; - try{ - oldValue = this.document.queryCommandValue("styleWithCSS"); - }catch(e){ - oldValue = false; - } - this.document.execCommand("styleWithCSS", false, true); - r = this.document.queryCommandValue(command); - this.document.execCommand("styleWithCSS", false, oldValue); - }else{ - r = this.document.queryCommandValue(command); - } - return r; - }, - - // Misc. - - _sCall: function(name, args){ - // summary: - // Run the named method of dijit._editor.selection over the - // current editor instance's window, with the passed args. - // tags: - // private - return dojo.withGlobal(this.window, name, dijit._editor.selection, args); - }, - - // FIXME: this is a TON of code duplication. Why? - - placeCursorAtStart: function(){ - // summary: - // Place the cursor at the start of the editing area. - // tags: - // private - - this.focus(); - - //see comments in placeCursorAtEnd - var isvalid=false; - if(dojo.isMoz){ - // TODO: Is this branch even necessary? - var first=this.editNode.firstChild; - while(first){ - if(first.nodeType == 3){ - if(first.nodeValue.replace(/^\s+|\s+$/g, "").length>0){ - isvalid=true; - this._sCall("selectElement", [ first ]); - break; - } - }else if(first.nodeType == 1){ - isvalid=true; - var tg = first.tagName ? first.tagName.toLowerCase() : ""; - // Collapse before childless tags. - if(/br|input|img|base|meta|area|basefont|hr|link/.test(tg)){ - this._sCall("selectElement", [ first ]); - }else{ - // Collapse inside tags with children. - this._sCall("selectElementChildren", [ first ]); - } - break; - } - first = first.nextSibling; - } - }else{ - isvalid=true; - this._sCall("selectElementChildren", [ this.editNode ]); - } - if(isvalid){ - this._sCall("collapse", [ true ]); - } - }, - - placeCursorAtEnd: function(){ - // summary: - // Place the cursor at the end of the editing area. - // tags: - // private - - this.focus(); - - //In mozilla, if last child is not a text node, we have to use - // selectElementChildren on this.editNode.lastChild otherwise the - // cursor would be placed at the end of the closing tag of - //this.editNode.lastChild - var isvalid=false; - if(dojo.isMoz){ - var last=this.editNode.lastChild; - while(last){ - if(last.nodeType == 3){ - if(last.nodeValue.replace(/^\s+|\s+$/g, "").length>0){ - isvalid=true; - this._sCall("selectElement", [ last ]); - break; - } - }else if(last.nodeType == 1){ - isvalid=true; - if(last.lastChild){ - this._sCall("selectElement", [ last.lastChild ]); - }else{ - this._sCall("selectElement", [ last ]); - } - break; - } - last = last.previousSibling; - } - }else{ - isvalid=true; - this._sCall("selectElementChildren", [ this.editNode ]); - } - if(isvalid){ - this._sCall("collapse", [ false ]); - } - }, - - getValue: function(/*Boolean?*/ nonDestructive){ - // summary: - // Return the current content of the editing area (post filters - // are applied). Users should call attr('value') instead. - // nonDestructive: - // defaults to false. Should the post-filtering be run over a copy - // of the live DOM? Most users should pass "true" here unless they - // *really* know that none of the installed filters are going to - // mess up the editing session. - // tags: - // private - if(this.textarea){ - if(this.isClosed || !this.isLoaded){ - return this.textarea.value; - } - } - - return this._postFilterContent(null, nonDestructive); - }, - _getValueAttr: function(){ - // summary: - // Hook to make attr("value") work - return this.getValue(true); - }, - - setValue: function(/*String*/ html){ - // summary: - // This function sets the content. No undo history is preserved. - // Users should use set('value', ...) instead. - // tags: - // deprecated - - // TODO: remove this and getValue() for 2.0, and move code to _setValueAttr() - - if(!this.isLoaded){ - // try again after the editor is finished loading - this.onLoadDeferred.addCallback(dojo.hitch(this, function(){ - this.setValue(html); - })); - return; - } - this._cursorToStart = true; - if(this.textarea && (this.isClosed || !this.isLoaded)){ - this.textarea.value=html; - }else{ - html = this._preFilterContent(html); - var node = this.isClosed ? this.domNode : this.editNode; - - // Use to avoid webkit problems where editor is disabled until the user clicks it - if(!html && dojo.isWebKit){ - html = " "; - } - node.innerHTML = html; - this._preDomFilterContent(node); - } - this.onDisplayChanged(); - }, - - replaceValue: function(/*String*/ html){ - // summary: - // This function set the content while trying to maintain the undo stack - // (now only works fine with Moz, this is identical to setValue in all - // other browsers) - // tags: - // protected - - if(this.isClosed){ - this.setValue(html); - }else if(this.window && this.window.getSelection && !dojo.isMoz){ // Safari - // look ma! it's a totally f'd browser! - this.setValue(html); - }else if(this.window && this.window.getSelection){ // Moz - html = this._preFilterContent(html); - this.execCommand("selectall"); - if(!html){ - this._cursorToStart = true; - html = " "; - } - this.execCommand("inserthtml", html); - this._preDomFilterContent(this.editNode); - }else if(this.document && this.document.selection){//IE - //In IE, when the first element is not a text node, say - //an <a> tag, when replacing the content of the editing - //area, the <a> tag will be around all the content - //so for now, use setValue for IE too - this.setValue(html); - } - }, - - _preFilterContent: function(/*String*/ html){ - // summary: - // Filter the input before setting the content of the editing - // area. DOM pre-filtering may happen after this - // string-based filtering takes place but as of 1.2, this is not - // guaranteed for operations such as the inserthtml command. - // tags: - // private - - var ec = html; - dojo.forEach(this.contentPreFilters, function(ef){ if(ef){ ec = ef(ec); } }); - return ec; - }, - _preDomFilterContent: function(/*DomNode*/ dom){ - // summary: - // filter the input's live DOM. All filter operations should be - // considered to be "live" and operating on the DOM that the user - // will be interacting with in their editing session. - // tags: - // private - dom = dom || this.editNode; - dojo.forEach(this.contentDomPreFilters, function(ef){ - if(ef && dojo.isFunction(ef)){ - ef(dom); - } - }, this); - }, - - _postFilterContent: function( - /*DomNode|DomNode[]|String?*/ dom, - /*Boolean?*/ nonDestructive){ - // summary: - // filter the output after getting the content of the editing area - // - // description: - // post-filtering allows plug-ins and users to specify any number - // of transforms over the editor's content, enabling many common - // use-cases such as transforming absolute to relative URLs (and - // vice-versa), ensuring conformance with a particular DTD, etc. - // The filters are registered in the contentDomPostFilters and - // contentPostFilters arrays. Each item in the - // contentDomPostFilters array is a function which takes a DOM - // Node or array of nodes as its only argument and returns the - // same. It is then passed down the chain for further filtering. - // The contentPostFilters array behaves the same way, except each - // member operates on strings. Together, the DOM and string-based - // filtering allow the full range of post-processing that should - // be necessaray to enable even the most agressive of post-editing - // conversions to take place. - // - // If nonDestructive is set to "true", the nodes are cloned before - // filtering proceeds to avoid potentially destructive transforms - // to the content which may still needed to be edited further. - // Once DOM filtering has taken place, the serialized version of - // the DOM which is passed is run through each of the - // contentPostFilters functions. - // - // dom: - // a node, set of nodes, which to filter using each of the current - // members of the contentDomPostFilters and contentPostFilters arrays. - // - // nonDestructive: - // defaults to "false". If true, ensures that filtering happens on - // a clone of the passed-in content and not the actual node - // itself. - // - // tags: - // private - - var ec; - if(!dojo.isString(dom)){ - dom = dom || this.editNode; - if(this.contentDomPostFilters.length){ - if(nonDestructive){ - dom = dojo.clone(dom); - } - dojo.forEach(this.contentDomPostFilters, function(ef){ - dom = ef(dom); - }); - } - ec = dijit._editor.getChildrenHtml(dom); - }else{ - ec = dom; - } - - if(!dojo.trim(ec.replace(/^\xA0\xA0*/, '').replace(/\xA0\xA0*$/, '')).length){ - ec = ""; - } - - // if(dojo.isIE){ - // //removing appended <P> </P> for IE - // ec = ec.replace(/(?:<p> </p>[\n\r]*)+$/i,""); - // } - dojo.forEach(this.contentPostFilters, function(ef){ - ec = ef(ec); - }); - - return ec; - }, - - _saveContent: function(/*Event*/ e){ - // summary: - // Saves the content in an onunload event if the editor has not been closed - // tags: - // private - - var saveTextarea = dojo.byId(dijit._scopeName + "._editor.RichText.savedContent"); - if(saveTextarea.value){ - saveTextarea.value += this._SEPARATOR; - } - saveTextarea.value += this.name + ":" + this.getValue(true); - }, - - - escapeXml: function(/*String*/ str, /*Boolean*/ noSingleQuotes){ - // summary: - // Adds escape sequences for special characters in XML. - // Optionally skips escapes for single quotes - // tags: - // private - - str = str.replace(/&/gm, "&").replace(/</gm, "<").replace(/>/gm, ">").replace(/"/gm, """); - if(!noSingleQuotes){ - str = str.replace(/'/gm, "'"); - } - return str; // string - }, - - getNodeHtml: function(/* DomNode */ node){ - // summary: - // Deprecated. Use dijit._editor._getNodeHtml() instead. - // tags: - // deprecated - dojo.deprecated('dijit.Editor::getNodeHtml is deprecated','use dijit._editor.getNodeHtml instead', 2); - return dijit._editor.getNodeHtml(node); // String - }, - - getNodeChildrenHtml: function(/* DomNode */ dom){ - // summary: - // Deprecated. Use dijit._editor.getChildrenHtml() instead. - // tags: - // deprecated - dojo.deprecated('dijit.Editor::getNodeChildrenHtml is deprecated','use dijit._editor.getChildrenHtml instead', 2); - return dijit._editor.getChildrenHtml(dom); - }, - - close: function(/*Boolean*/ save){ - // summary: - // Kills the editor and optionally writes back the modified contents to the - // element from which it originated. - // save: - // Whether or not to save the changes. If false, the changes are discarded. - // tags: - // private - - if(this.isClosed){return false; } - - if(!arguments.length){ save = true; } - this._content = this.getValue(); - var changed = (this.savedContent != this._content); - - // line height is squashed for iframes - // FIXME: why was this here? if (this.iframe){ this.domNode.style.lineHeight = null; } - - if(this.interval){ clearInterval(this.interval); } - - if(this._webkitListener){ - //Cleaup of WebKit fix: #9532 - this.disconnect(this._webkitListener); - delete this._webkitListener; - } - - // Guard against memory leaks on IE (see #9268) - if(dojo.isIE){ - this.iframe.onfocus = null; - } - this.iframe._loadFunc = null; - - if(this._iframeRegHandle){ - dijit.unregisterIframe(this._iframeRegHandle); - delete this._iframeRegHandle; - } - - if(this.textarea){ - var s = this.textarea.style; - s.position = ""; - s.left = s.top = ""; - if(dojo.isIE){ - s.overflow = this.__overflow; - this.__overflow = null; - } - this.textarea.value = save ? this._content : this.savedContent; - dojo.destroy(this.domNode); - this.domNode = this.textarea; - }else{ - // if(save){ - // why we treat moz differently? comment out to fix #1061 - // if(dojo.isMoz){ - // var nc = dojo.doc.createElement("span"); - // this.domNode.appendChild(nc); - // nc.innerHTML = this.editNode.innerHTML; - // }else{ - // this.domNode.innerHTML = this._content; - // } - // } - - // Note that this destroys the iframe - this.domNode.innerHTML = save ? this._content : this.savedContent; - } - delete this.iframe; - - dojo.removeClass(this.domNode, this.baseClass); - this.isClosed = true; - this.isLoaded = false; - - delete this.editNode; - delete this.focusNode; - - if(this.window && this.window._frameElement){ - this.window._frameElement = null; - } - - this.window = null; - this.document = null; - this.editingArea = null; - this.editorObject = null; - - return changed; // Boolean: whether the content has been modified - }, - - destroy: function(){ - if(!this.isClosed){ this.close(false); } - this.inherited(arguments); - }, - - _removeMozBogus: function(/* String */ html){ - // summary: - // Post filter to remove unwanted HTML attributes generated by mozilla - // tags: - // private - return html.replace(/\stype="_moz"/gi, '').replace(/\s_moz_dirty=""/gi, '').replace(/_moz_resizing="(true|false)"/gi,''); // String - }, - _removeWebkitBogus: function(/* String */ html){ - // summary: - // Post filter to remove unwanted HTML attributes generated by webkit - // tags: - // private - html = html.replace(/\sclass="webkit-block-placeholder"/gi, ''); - html = html.replace(/\sclass="apple-style-span"/gi, ''); - return html; // String - }, - _normalizeFontStyle: function(/* String */ html){ - // summary: - // Convert 'strong' and 'em' to 'b' and 'i'. - // description: - // Moz can not handle strong/em tags correctly, so to help - // mozilla and also to normalize output, convert them to 'b' and 'i'. - // - // Note the IE generates 'strong' and 'em' rather than 'b' and 'i' - // tags: - // private - return html.replace(/<(\/)?strong([ \>])/gi, '<$1b$2') - .replace(/<(\/)?em([ \>])/gi, '<$1i$2' ); // String - }, - - _preFixUrlAttributes: function(/* String */ html){ - // summary: - // Pre-filter to do fixing to href attributes on <a> and <img> tags - // tags: - // private - return html.replace(/(?:(<a(?=\s).*?\shref=)("|')(.*?)\2)|(?:(<a\s.*?href=)([^"'][^ >]+))/gi, - '$1$4$2$3$5$2 _djrealurl=$2$3$5$2') - .replace(/(?:(<img(?=\s).*?\ssrc=)("|')(.*?)\2)|(?:(<img\s.*?src=)([^"'][^ >]+))/gi, - '$1$4$2$3$5$2 _djrealurl=$2$3$5$2'); // String - }, - - /***************************************************************************** - The following functions implement HTML manipulation commands for various - browser/contentEditable implementations. The goal of them is to enforce - standard behaviors of them. - ******************************************************************************/ - - _inserthorizontalruleImpl: function(argument){ - // summary: - // This function implements the insertion of HTML 'HR' tags. - // into a point on the page. IE doesn't to it right, so - // we have to use an alternate form - // argument: - // arguments to the exec command, if any. - // tags: - // protected - if(dojo.isIE){ - return this._inserthtmlImpl("<hr>"); - } - return this.document.execCommand("inserthorizontalrule", false, argument); - }, - - _unlinkImpl: function(argument){ - // summary: - // This function implements the unlink of an 'a' tag. - // argument: - // arguments to the exec command, if any. - // tags: - // protected - if((this.queryCommandEnabled("unlink")) && (dojo.isMoz || dojo.isWebKit)){ - var a = this._sCall("getAncestorElement", [ "a" ]); - this._sCall("selectElement", [ a ]); - return this.document.execCommand("unlink", false, null); - } - return this.document.execCommand("unlink", false, argument); - }, - - _hilitecolorImpl: function(argument){ - // summary: - // This function implements the hilitecolor command - // argument: - // arguments to the exec command, if any. - // tags: - // protected - var returnValue; - if(dojo.isMoz){ - // mozilla doesn't support hilitecolor properly when useCSS is - // set to false (bugzilla #279330) - this.document.execCommand("styleWithCSS", false, true); - returnValue = this.document.execCommand("hilitecolor", false, argument); - this.document.execCommand("styleWithCSS", false, false); - }else{ - returnValue = this.document.execCommand("hilitecolor", false, argument); - } - return returnValue; - }, - - _backcolorImpl: function(argument){ - // summary: - // This function implements the backcolor command - // argument: - // arguments to the exec command, if any. - // tags: - // protected - if(dojo.isIE){ - // Tested under IE 6 XP2, no problem here, comment out - // IE weirdly collapses ranges when we exec these commands, so prevent it - // var tr = this.document.selection.createRange(); - argument = argument ? argument : null; - } - return this.document.execCommand("backcolor", false, argument); - }, - - _forecolorImpl: function(argument){ - // summary: - // This function implements the forecolor command - // argument: - // arguments to the exec command, if any. - // tags: - // protected - if(dojo.isIE){ - // Tested under IE 6 XP2, no problem here, comment out - // IE weirdly collapses ranges when we exec these commands, so prevent it - // var tr = this.document.selection.createRange(); - argument = argument? argument : null; - } - return this.document.execCommand("forecolor", false, argument); - }, - - _inserthtmlImpl: function(argument){ - // summary: - // This function implements the insertion of HTML content into - // a point on the page. - // argument: - // The content to insert, if any. - // tags: - // protected - argument = this._preFilterContent(argument); - var rv = true; - if(dojo.isIE){ - var insertRange = this.document.selection.createRange(); - if(this.document.selection.type.toUpperCase() == 'CONTROL'){ - var n=insertRange.item(0); - while(insertRange.length){ - insertRange.remove(insertRange.item(0)); - } - n.outerHTML=argument; - }else{ - insertRange.pasteHTML(argument); - } - insertRange.select(); - //insertRange.collapse(true); - }else if(dojo.isMoz && !argument.length){ - //mozilla can not inserthtml an empty html to delete current selection - //so we delete the selection instead in this case - this._sCall("remove"); // FIXME - }else{ - rv = this.document.execCommand("inserthtml", false, argument); - } - return rv; - }, - - getHeaderHeight: function(){ - // summary: - // A function for obtaining the height of the header node - return this._getNodeChildrenHeight(this.header); // Number - }, - - getFooterHeight: function(){ - // summary: - // A function for obtaining the height of the footer node - return this._getNodeChildrenHeight(this.footer); // Number - }, - - _getNodeChildrenHeight: function(node){ - // summary: - // An internal function for computing the cumulative height of all child nodes of 'node' - // node: - // The node to process the children of; - var h = 0; - if(node && node.childNodes){ - // IE didn't compute it right when position was obtained on the node directly is some cases, - // so we have to walk over all the children manually. - var i; - for(i = 0; i < node.childNodes.length; i++){ - var size = dojo.position(node.childNodes[i]); - h += size.h; - } - } - return h; // Number - } -}); - -} - -if(!dojo._hasResource["dijit._KeyNavContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit._KeyNavContainer"] = true; -dojo.provide("dijit._KeyNavContainer"); - - -dojo.declare("dijit._KeyNavContainer", - dijit._Container, - { - - // summary: - // A _Container with keyboard navigation of its children. - // description: - // To use this mixin, call connectKeyNavHandlers() in - // postCreate() and call startupKeyNavChildren() in startup(). - // It provides normalized keyboard and focusing code for Container - // widgets. -/*===== - // focusedChild: [protected] Widget - // The currently focused child widget, or null if there isn't one - focusedChild: null, -=====*/ - - // tabIndex: Integer - // Tab index of the container; same as HTML tabIndex attribute. - // Note then when user tabs into the container, focus is immediately - // moved to the first item in the container. - tabIndex: "0", - - _keyNavCodes: {}, - - connectKeyNavHandlers: function(/*dojo.keys[]*/ prevKeyCodes, /*dojo.keys[]*/ nextKeyCodes){ - // summary: - // Call in postCreate() to attach the keyboard handlers - // to the container. - // preKeyCodes: dojo.keys[] - // Key codes for navigating to the previous child. - // nextKeyCodes: dojo.keys[] - // Key codes for navigating to the next child. - // tags: - // protected - - var keyCodes = (this._keyNavCodes = {}); - var prev = dojo.hitch(this, this.focusPrev); - var next = dojo.hitch(this, this.focusNext); - dojo.forEach(prevKeyCodes, function(code){ keyCodes[code] = prev; }); - dojo.forEach(nextKeyCodes, function(code){ keyCodes[code] = next; }); - this.connect(this.domNode, "onkeypress", "_onContainerKeypress"); - this.connect(this.domNode, "onfocus", "_onContainerFocus"); - }, - - startupKeyNavChildren: function(){ - // summary: - // Call in startup() to set child tabindexes to -1 - // tags: - // protected - dojo.forEach(this.getChildren(), dojo.hitch(this, "_startupChild")); - }, - - addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){ - // summary: - // Add a child to our _Container - dijit._KeyNavContainer.superclass.addChild.apply(this, arguments); - this._startupChild(widget); - }, - - focus: function(){ - // summary: - // Default focus() implementation: focus the first child. - this.focusFirstChild(); - }, - - focusFirstChild: function(){ - // summary: - // Focus the first focusable child in the container. - // tags: - // protected - var child = this._getFirstFocusableChild(); - if(child){ // edge case: Menu could be empty or hidden - this.focusChild(child); - } - }, - - focusNext: function(){ - // summary: - // Focus the next widget - // tags: - // protected - var child = this._getNextFocusableChild(this.focusedChild, 1); - this.focusChild(child); - }, - - focusPrev: function(){ - // summary: - // Focus the last focusable node in the previous widget - // (ex: go to the ComboButton icon section rather than button section) - // tags: - // protected - var child = this._getNextFocusableChild(this.focusedChild, -1); - this.focusChild(child, true); - }, - - focusChild: function(/*dijit._Widget*/ widget, /*Boolean*/ last){ - // summary: - // Focus widget. - // widget: - // Reference to container's child widget - // last: - // If true and if widget has multiple focusable nodes, focus the - // last one instead of the first one - // tags: - // protected - - if(this.focusedChild && widget !== this.focusedChild){ - this._onChildBlur(this.focusedChild); - } - widget.focus(last ? "end" : "start"); - this.focusedChild = widget; - }, - - _startupChild: function(/*dijit._Widget*/ widget){ - // summary: - // Setup for each child widget - // description: - // Sets tabIndex=-1 on each child, so that the tab key will - // leave the container rather than visiting each child. - // tags: - // private - - widget.set("tabIndex", "-1"); - - this.connect(widget, "_onFocus", function(){ - // Set valid tabIndex so tabbing away from widget goes to right place, see #10272 - widget.set("tabIndex", this.tabIndex); - }); - this.connect(widget, "_onBlur", function(){ - widget.set("tabIndex", "-1"); - }); - }, - - _onContainerFocus: function(evt){ - // summary: - // Handler for when the container gets focus - // description: - // Initially the container itself has a tabIndex, but when it gets - // focus, switch focus to first child... - // tags: - // private - - // Note that we can't use _onFocus() because switching focus from the - // _onFocus() handler confuses the focus.js code - // (because it causes _onFocusNode() to be called recursively) - - // focus bubbles on Firefox, - // so just make sure that focus has really gone to the container - if(evt.target !== this.domNode){ return; } - - this.focusFirstChild(); - - // and then set the container's tabIndex to -1, - // (don't remove as that breaks Safari 4) - // so that tab or shift-tab will go to the fields after/before - // the container, rather than the container itself - dojo.attr(this.domNode, "tabIndex", "-1"); - }, - - _onBlur: function(evt){ - // When focus is moved away the container, and its descendant (popup) widgets, - // then restore the container's tabIndex so that user can tab to it again. - // Note that using _onBlur() so that this doesn't happen when focus is shifted - // to one of my child widgets (typically a popup) - if(this.tabIndex){ - dojo.attr(this.domNode, "tabIndex", this.tabIndex); - } - this.inherited(arguments); - }, - - _onContainerKeypress: function(evt){ - // summary: - // When a key is pressed, if it's an arrow key etc. then - // it's handled here. - // tags: - // private - if(evt.ctrlKey || evt.altKey){ return; } - var func = this._keyNavCodes[evt.charOrCode]; - if(func){ - func(); - dojo.stopEvent(evt); - } - }, - - _onChildBlur: function(/*dijit._Widget*/ widget){ - // summary: - // Called when focus leaves a child widget to go - // to a sibling widget. - // tags: - // protected - }, - - _getFirstFocusableChild: function(){ - // summary: - // Returns first child that can be focused - return this._getNextFocusableChild(null, 1); // dijit._Widget - }, - - _getNextFocusableChild: function(child, dir){ - // summary: - // Returns the next or previous focusable child, compared - // to "child" - // child: Widget - // The current widget - // dir: Integer - // * 1 = after - // * -1 = before - if(child){ - child = this._getSiblingOfChild(child, dir); - } - var children = this.getChildren(); - for(var i=0; i < children.length; i++){ - if(!child){ - child = children[(dir>0) ? 0 : (children.length-1)]; - } - if(child.isFocusable()){ - return child; // dijit._Widget - } - child = this._getSiblingOfChild(child, dir); - } - // no focusable child found - return null; // dijit._Widget - } - } -); - -} - -if(!dojo._hasResource["dijit.ToolbarSeparator"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.ToolbarSeparator"] = true; -dojo.provide("dijit.ToolbarSeparator"); - - - - -dojo.declare("dijit.ToolbarSeparator", - [ dijit._Widget, dijit._Templated ], - { - // summary: - // A spacer between two `dijit.Toolbar` items - templateString: '<div class="dijitToolbarSeparator dijitInline" waiRole="presentation"></div>', - postCreate: function(){ dojo.setSelectable(this.domNode, false); }, - isFocusable: function(){ - // summary: - // This widget isn't focusable, so pass along that fact. - // tags: - // protected - return false; - } - - }); - - - -} - -if(!dojo._hasResource["dijit.Toolbar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.Toolbar"] = true; -dojo.provide("dijit.Toolbar"); - - - - - -dojo.declare("dijit.Toolbar", - [dijit._Widget, dijit._Templated, dijit._KeyNavContainer], - { - // summary: - // A Toolbar widget, used to hold things like `dijit.Editor` buttons - - templateString: - '<div class="dijit" waiRole="toolbar" tabIndex="${tabIndex}" dojoAttachPoint="containerNode">' + - // '<table style="table-layout: fixed" class="dijitReset dijitToolbarTable">' + // factor out style - // '<tr class="dijitReset" dojoAttachPoint="containerNode"></tr>'+ - // '</table>' + - '</div>', - - baseClass: "dijitToolbar", - - postCreate: function(){ - this.connectKeyNavHandlers( - this.isLeftToRight() ? [dojo.keys.LEFT_ARROW] : [dojo.keys.RIGHT_ARROW], - this.isLeftToRight() ? [dojo.keys.RIGHT_ARROW] : [dojo.keys.LEFT_ARROW] - ); - this.inherited(arguments); - }, - - startup: function(){ - if(this._started){ return; } - - this.startupKeyNavChildren(); - - this.inherited(arguments); - } -} -); - -// For back-compat, remove for 2.0 - - -} - -if(!dojo._hasResource["dijit._HasDropDown"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit._HasDropDown"] = true; -dojo.provide("dijit._HasDropDown"); - - - - -dojo.declare("dijit._HasDropDown", - null, - { - // 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 dojoAttachPoint 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 dojoAttachPoint 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 dojoAttachPoint 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 dojoAttachPoint 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. Set to 0 for no max height. - // any dropdown taller than this will have scrollbars - 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; } - - this._docHandler = this.connect(dojo.doc, "onmouseup", "_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 is a simple menu and the mouse is over the menu, we execute it, otherwise, we focus our - // dropDown node. If the event is missing, then we are not - // a mouseup event. - // - // This is useful for the common mouse movement pattern - // with native browser <select> nodes: - // 1. mouse down on the select node (probably on the arrow) - // 2. move mouse to a menu item while holding down the mouse button - // 3. mouse up. this selects the menu item as though the user had clicked it. - if(e && this._docHandler){ - this.disconnect(this._docHandler); - } - var dropDown = this.dropDown, overMenu = false; - - if(e && this._opened){ - // This code deals with the corner-case when the drop down covers the original widget, - // because it's so large. In that case mouse-up shouldn't select a value from the menu. - // Find out if our target is somewhere in our dropdown widget, - // but not over our _buttonNode (the clickable node) - var c = dojo.position(this._buttonNode, true); - if(!(e.pageX >= c.x && e.pageX <= c.x + c.w) || - !(e.pageY >= c.y && e.pageY <= c.y + c.h)){ - var t = e.target; - while(t && !overMenu){ - if(dojo.hasClass(t, "dijitPopup")){ - overMenu = true; - }else{ - t = t.parentNode; - } - } - if(overMenu){ - t = e.target; - if(dropDown.onItemClick){ - var menuItem; - while(t && !(menuItem = dijit.byNode(t))){ - t = t.parentNode; - } - if(menuItem && menuItem.onClick && menuItem.getParent){ - menuItem.getParent().onItemClick(menuItem, e); - } - } - return; - } - } - } - if(this._opened && dropDown.focus){ - // Focus the dropdown widget - do it on a delay so that we - // don't steal our own focus. - window.setTimeout(dojo.hitch(dropDown, "focus"), 1); - } - }, - - _onDropDownClick: function(/*Event*/ e){ - // the drop down was already opened on mousedown/keydown; just need to call stopEvent() - if(this._stopClickEvents){ - dojo.stopEvent(e); - } - }, - - _setupDropdown: function(){ - // summary: - // set up nodes and connect our mouse and keypress events - this._buttonNode = this._buttonNode || this.focusNode || this.domNode; - this._popupStateNode = this._popupStateNode || this.focusNode || this._buttonNode; - this._aroundNode = this._aroundNode || this.domNode; - this.connect(this._buttonNode, "onmousedown", "_onDropDownMouseDown"); - this.connect(this._buttonNode, "onclick", "_onDropDownClick"); - this.connect(this._buttonNode, "onkeydown", "_onDropDownKeydown"); - this.connect(this._buttonNode, "onkeyup", "_onKey"); - - // If we have a _setStateClass function (which happens when - // we are a form widget), then we need to connect our open/close - // functions to it - if(this._setStateClass){ - this.connect(this, "openDropDown", "_setStateClass"); - this.connect(this, "closeDropDown", "_setStateClass"); - } - - // 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"; - dojo.addClass(this._arrowWrapperNode || this._buttonNode, "dijit" + defaultPos + "ArrowButton"); - }, - - postCreate: function(){ - this._setupDropdown(); - this.inherited(arguments); - }, - - destroyDescendants: function(){ - if(this.dropDown){ - // Destroy the drop down, unless it's already been destroyed. This can happen because - // the drop down is a direct child of <body> even though it's logically my child. - if(!this.dropDown._destroyed){ - this.dropDown.destroyRecursive(); - } - delete this.dropDown; - } - this.inherited(arguments); - }, - - _onDropDownKeydown: function(/*Event*/ e){ - if(e.keyCode == dojo.keys.DOWN_ARROW || e.keyCode == dojo.keys.ENTER || e.keyCode == dojo.keys.SPACE){ - e.preventDefault(); // stop IE screen jump - } - }, - - _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; - if(d && this._opened && d.handleKey){ - if(d.handleKey(e) === false){ return; } - } - if(d && this._opened && e.keyCode == dojo.keys.ESCAPE){ - this.toggleDropDown(); - }else if(d && !this._opened && - (e.keyCode == dojo.keys.DOWN_ARROW || e.keyCode == dojo.keys.ENTER || e.keyCode == dojo.keys.SPACE)){ - this.toggleDropDown(); - if(d.focus){ - setTimeout(dojo.hitch(d, "focus"), 1); - } - } - }, - - _onBlur: function(){ - // summary: - // Called magically when focus has shifted away from this widget and it's dropdown - - this.closeDropDown(); - // don't focus on button. the user has explicitly focused on something else. - this.inherited(arguments); - }, - - isLoaded: function(){ - // summary: - // Returns whether or not the dropdown is loaded. This can - // be overridden in order to force a call to loadDropDown(). - // tags: - // protected - - return true; - }, - - loadDropDown: function(/* Function */ loadCallback){ - // summary: - // Loads the data for the dropdown, and at some point, calls - // the given callback - // tags: - // protected - - loadCallback(); - }, - - toggleDropDown: function(){ - // summary: - // Toggle the drop-down widget; if it is up, close it, if not, open it - // tags: - // protected - - if(this.disabled || this.readOnly){ return; } - this.focus(); - var dropDown = this.dropDown; - if(!dropDown){ return; } - if(!this._opened){ - // If we aren't loaded, load it first so there isn't a flicker - if(!this.isLoaded()){ - this.loadDropDown(dojo.hitch(this, "openDropDown")); - return; - }else{ - this.openDropDown(); - } - }else{ - this.closeDropDown(); - } - }, - - openDropDown: function(){ - // summary: - // Opens the dropdown for this widget - it returns the - // return value of dijit.popup.open - // tags: - // protected - - var dropDown = this.dropDown; - var ddNode = dropDown.domNode; - var 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){ - dijit.popup.moveOffScreen(ddNode); - 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 = ""; - } - dojo.style(ddNode, myStyle); - - // Get size of drop down, and determine if vertical scroll bar needed - var mb = dojo.marginBox(ddNode); - var overHeight = (this.maxHeight && mb.h > this.maxHeight); - dojo.style(ddNode, { - overflowX: "hidden", - overflowY: overHeight ? "auto" : "hidden" - }); - if(overHeight){ - mb.h = this.maxHeight; - if("w" in mb){ - mb.w += 16; // room for vertical scrollbar - } - }else{ - delete mb.h; - } - delete mb.t; - delete mb.l; - - // Adjust dropdown width to match or be larger than my width - if(this.forceWidth){ - mb.w = this.domNode.offsetWidth; - }else if(this.autoWidth){ - mb.w = Math.max(mb.w, this.domNode.offsetWidth); - }else{ - delete mb.w; - } - - // And finally, resize the dropdown to calculated height and width - if(dojo.isFunction(dropDown.resize)){ - dropDown.resize(mb); - }else{ - dojo.marginBox(ddNode, mb); - } - } - - var retVal = dijit.popup.open({ - parent: this, - popup: dropDown, - around: this._aroundNode, - orient: dijit.getPopupAroundAlignment((this.dropDownPosition && this.dropDownPosition.length) ? this.dropDownPosition : ["below"],this.isLeftToRight()), - onExecute: function(){ - self.closeDropDown(true); - }, - onCancel: function(){ - self.closeDropDown(true); - }, - onClose: function(){ - dojo.attr(self._popupStateNode, "popupActive", false); - dojo.removeClass(self._popupStateNode, "dijitHasDropDownOpen"); - self._opened = false; - self.state = ""; - } - }); - dojo.attr(this._popupStateNode, "popupActive", "true"); - dojo.addClass(self._popupStateNode, "dijitHasDropDownOpen"); - this._opened=true; - this.state="Opened"; - // 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 - // tags: - // protected - - if(this._opened){ - if(focus){ this.focus(); } - dijit.popup.close(this.dropDown); - this._opened = false; - this.state = ""; - } - } - - } -); - -} - -if(!dojo._hasResource["dijit.form.Button"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.Button"] = true; -dojo.provide("dijit.form.Button"); - - - - - -dojo.declare("dijit.form.Button", - dijit.form._FormWidget, - { - // summary: - // Basically the same thing as a normal HTML button, but with special styling. - // description: - // Buttons can display a label, an icon, or both. - // A label should always be specified (through innerHTML) or the label - // attribute. It can be hidden via showLabel=false. - // example: - // | <button dojoType="dijit.form.Button" onClick="...">Hello world</button> - // - // example: - // | var button1 = new dijit.form.Button({label: "hello world", onClick: foo}); - // | dojo.body().appendChild(button1.domNode); - - // label: HTML String - // Text to display in button. - // If the label is hidden (showLabel=false) then and no title has - // been specified, then label is also set as title attribute of icon. - label: "", - - // showLabel: Boolean - // Set this to true to hide the label text and display only the icon. - // (If showLabel=false then iconClass must be specified.) - // Especially useful for toolbars. - // If showLabel=true, the label will become the title (a.k.a. tooltip/hint) of the icon. - // - // The exception case is for computers in high-contrast mode, where the label - // will still be displayed, since the icon doesn't appear. - showLabel: true, - - // iconClass: String - // Class to apply to DOMNode in button to make it display an icon - iconClass: "", - - // type: String - // Defines the type of button. "button", "submit", or "reset". - type: "button", - - baseClass: "dijitButton", - - templateString: dojo.cache("dijit.form", "templates/Button.html", "<span class=\"dijit dijitReset dijitInline\"\n\t><span class=\"dijitReset dijitInline dijitButtonNode\"\n\t\tdojoAttachEvent=\"ondijitclick:_onButtonClick\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdojoAttachPoint=\"titleNode,focusNode\"\n\t\t\twaiRole=\"button\" waiState=\"labelledby-${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\" dojoAttachPoint=\"iconNode\"></span\n\t\t\t><span class=\"dijitReset dijitToggleButtonIconChar\">●</span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t\tdojoAttachPoint=\"containerNode\"\n\t\t\t></span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\"\n\t\tdojoAttachPoint=\"valueNode\"\n/></span>\n"), - - attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, { - value: "valueNode", - iconClass: { node: "iconNode", type: "class" } - }), - - - _onClick: function(/*Event*/ e){ - // summary: - // Internal function to handle click actions - if(this.disabled){ - return false; - } - this._clicked(); // widget click actions - return this.onClick(e); // user click actions - }, - - _onButtonClick: function(/*Event*/ e){ - // summary: - // Handler when the user activates the button portion. - if(this._onClick(e) === false){ // returning nothing is same as true - e.preventDefault(); // needed for checkbox - }else if(this.type == "submit" && !(this.valueNode||this.focusNode).form){ // see if a nonform widget needs to be signalled - for(var node=this.domNode; node.parentNode/*#5935*/; node=node.parentNode){ - var widget=dijit.byNode(node); - if(widget && typeof widget._onSubmit == "function"){ - widget._onSubmit(e); - break; - } - } - }else if(this.valueNode){ - this.valueNode.click(); - e.preventDefault(); // cancel BUTTON click and continue with hidden INPUT click - } - }, - - _fillContent: function(/*DomNode*/ source){ - // Overrides _Templated._fillContent(). - // If button label is specified as srcNodeRef.innerHTML rather than - // this.params.label, handle it here. - if(source && (!this.params || !("label" in this.params))){ - this.set('label', source.innerHTML); - } - }, - - postCreate: function(){ - dojo.setSelectable(this.focusNode, false); - this.inherited(arguments); - }, - - _setShowLabelAttr: function(val){ - if(this.containerNode){ - dojo.toggleClass(this.containerNode, "dijitDisplayNone", !val); - } - this.showLabel = val; - }, - - onClick: function(/*Event*/ e){ - // summary: - // Callback for when button is clicked. - // If type="submit", return true to perform submit, or false to cancel it. - // type: - // callback - return true; // Boolean - }, - - _clicked: function(/*Event*/ e){ - // summary: - // Internal overridable function for when the button is clicked - }, - - setLabel: function(/*String*/ content){ - // summary: - // Deprecated. Use set('label', ...) instead. - dojo.deprecated("dijit.form.Button.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0"); - this.set("label", content); - }, - - _setLabelAttr: function(/*String*/ content){ - // summary: - // Hook for attr('label', ...) to work. - // description: - // Set the label (text) of the button; takes an HTML string. - this.containerNode.innerHTML = this.label = content; - if(this.showLabel == false && !this.params.title){ - this.titleNode.title = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || ''); - } - } -}); - - -dojo.declare("dijit.form.DropDownButton", [dijit.form.Button, dijit._Container, dijit._HasDropDown], { - // summary: - // A button with a drop down - // - // example: - // | <button dojoType="dijit.form.DropDownButton" label="Hello world"> - // | <div dojotype="dijit.Menu">...</div> - // | </button> - // - // example: - // | var button1 = new dijit.form.DropDownButton({ label: "hi", dropDown: new dijit.Menu(...) }); - // | dojo.body().appendChild(button1); - // - - baseClass : "dijitDropDownButton", - - templateString: dojo.cache("dijit.form", "templates/DropDownButton.html", "<span class=\"dijit dijitReset dijitInline\"\n\t><span class='dijitReset dijitInline dijitButtonNode'\n\t\tdojoAttachEvent=\"ondijitclick:_onButtonClick\" dojoAttachPoint=\"_buttonNode\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdojoAttachPoint=\"focusNode,titleNode,_arrowWrapperNode\"\n\t\t\twaiRole=\"button\" waiState=\"haspopup-true,labelledby-${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\"\n\t\t\t\tdojoAttachPoint=\"iconNode\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tdojoAttachPoint=\"containerNode,_popupStateNode\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonInner\"></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonChar\">▼</span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\"\n\t\tdojoAttachPoint=\"valueNode\"\n/></span>\n"), - - _fillContent: function(){ - // Overrides Button._fillContent(). - // - // My inner HTML contains both the button contents and a drop down widget, like - // <DropDownButton> <span>push me</span> <Menu> ... </Menu> </DropDownButton> - // The first node is assumed to be the button content. The widget is the popup. - - if(this.srcNodeRef){ // programatically created buttons might not define srcNodeRef - //FIXME: figure out how to filter out the widget and use all remaining nodes as button - // content, not just nodes[0] - var nodes = dojo.query("*", this.srcNodeRef); - dijit.form.DropDownButton.superclass._fillContent.call(this, nodes[0]); - - // save pointer to srcNode so we can grab the drop down widget after it's instantiated - this.dropDownContainer = this.srcNodeRef; - } - }, - - startup: function(){ - if(this._started){ return; } - - // the child widget from srcNodeRef is the dropdown widget. Insert it in the page DOM, - // make it invisible, and store a reference to pass to the popup code. - if(!this.dropDown){ - var dropDownNode = dojo.query("[widgetId]", this.dropDownContainer)[0]; - this.dropDown = dijit.byNode(dropDownNode); - delete this.dropDownContainer; - } - dijit.popup.moveOffScreen(this.dropDown.domNode); - - this.inherited(arguments); - }, - - isLoaded: function(){ - // Returns whether or not we are loaded - if our dropdown has an href, - // then we want to check that. - var dropDown = this.dropDown; - return (!dropDown.href || dropDown.isLoaded); - }, - - loadDropDown: function(){ - // Loads our dropdown - var dropDown = this.dropDown; - if(!dropDown){ return; } - if(!this.isLoaded()){ - var handler = dojo.connect(dropDown, "onLoad", this, function(){ - dojo.disconnect(handler); - this.openDropDown(); - }); - dropDown.refresh(); - }else{ - this.openDropDown(); - } - }, - - isFocusable: function(){ - // Overridden so that focus is handled by the _HasDropDown mixin, not by - // the _FormWidget mixin. - return this.inherited(arguments) && !this._mouseDown; - } -}); - -dojo.declare("dijit.form.ComboButton", dijit.form.DropDownButton, { - // summary: - // A combination button and drop-down button. - // Users can click one side to "press" the button, or click an arrow - // icon to display the drop down. - // - // example: - // | <button dojoType="dijit.form.ComboButton" onClick="..."> - // | <span>Hello world</span> - // | <div dojoType="dijit.Menu">...</div> - // | </button> - // - // example: - // | var button1 = new dijit.form.ComboButton({label: "hello world", onClick: foo, dropDown: "myMenu"}); - // | dojo.body().appendChild(button1.domNode); - // - - templateString: dojo.cache("dijit.form", "templates/ComboButton.html", "<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tcellspacing='0' cellpadding='0' waiRole=\"presentation\"\n\t><tbody waiRole=\"presentation\"><tr waiRole=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonNode\" dojoAttachPoint=\"buttonNode\" dojoAttachEvent=\"ondijitclick:_onButtonClick,onkeypress:_onButtonKeyPress\"\n\t\t><div id=\"${id}_button\" class=\"dijitReset dijitButtonContents\"\n\t\t\tdojoAttachPoint=\"titleNode\"\n\t\t\twaiRole=\"button\" waiState=\"labelledby-${id}_label\"\n\t\t\t><div class=\"dijitReset dijitInline dijitIcon\" dojoAttachPoint=\"iconNode\" waiRole=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitInline dijitButtonText\" id=\"${id}_label\" dojoAttachPoint=\"containerNode\" waiRole=\"presentation\"></div\n\t\t></div\n\t\t></td\n\t\t><td id=\"${id}_arrow\" class='dijitReset dijitRight dijitButtonNode dijitArrowButton'\n\t\t\tdojoAttachPoint=\"_popupStateNode,focusNode,_buttonNode\"\n\t\t\tdojoAttachEvent=\"onkeypress:_onArrowKeyPress\"\n\t\t\ttitle=\"${optionsTitle}\"\n\t\t\twaiRole=\"button\" waiState=\"haspopup-true\"\n\t\t\t><div class=\"dijitReset dijitArrowButtonInner\" waiRole=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitArrowButtonChar\" waiRole=\"presentation\">▼</div\n\t\t></td\n\t\t><td style=\"display:none !important;\"\n\t\t\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" dojoAttachPoint=\"valueNode\"\n\t\t/></td></tr></tbody\n></table>\n"), - - attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap), { - id: "", - tabIndex: ["focusNode", "titleNode"], - title: "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 == dojo.keys[this.isLeftToRight() ? "RIGHT_ARROW" : "LEFT_ARROW"]){ - dijit.focus(this._popupStateNode); - dojo.stopEvent(evt); - } - }, - - _onArrowKeyPress: function(/*Event*/ evt){ - // summary: - // Handler for left arrow key when focus is on right part of button - if(evt.charOrCode == dojo.keys[this.isLeftToRight() ? "LEFT_ARROW" : "RIGHT_ARROW"]){ - dijit.focus(this.titleNode); - dojo.stopEvent(evt); - } - }, - - focus: function(/*String*/ position){ - // summary: - // Focuses this widget to according to position, if specified, - // otherwise on arrow node - // position: - // "start" or "end" - - dijit.focus(position == "start" ? this.titleNode : this._popupStateNode); - } -}); - -dojo.declare("dijit.form.ToggleButton", dijit.form.Button, { - // summary: - // A button that can be in two states (checked or not). - // Can be base class for things like tabs or checkbox or radio buttons - - baseClass: "dijitToggleButton", - - // checked: Boolean - // Corresponds to the native HTML <input> element's attribute. - // In markup, specified as "checked='checked'" or just "checked". - // True if the button is depressed, or the checkbox is checked, - // or the radio button is selected, etc. - checked: false, - - attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap), { - checked:"focusNode" - }), - - _clicked: function(/*Event*/ evt){ - this.set('checked', !this.checked); - }, - - _setCheckedAttr: function(/*Boolean*/ value, /* Boolean? */ priorityChange){ - this.checked = value; - dojo.attr(this.focusNode || this.domNode, "checked", value); - dijit.setWaiState(this.focusNode || this.domNode, "pressed", value); - this._handleOnChange(value, priorityChange); - }, - - setChecked: function(/*Boolean*/ checked){ - // summary: - // Deprecated. Use set('checked', true/false) instead. - dojo.deprecated("setChecked("+checked+") is deprecated. Use set('checked',"+checked+") instead.", "", "2.0"); - this.set('checked', checked); - }, - - reset: function(){ - // summary: - // Reset the widget's value to what it was at initialization time - - this._hasBeenBlurred = false; - - // set checked state to original setting - this.set('checked', this.params.checked || false); - } -}); - -} - -if(!dojo._hasResource["dijit._editor._Plugin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit._editor._Plugin"] = true; -dojo.provide("dijit._editor._Plugin"); - - - -dojo.declare("dijit._editor._Plugin", null, { - // summary - // Base class for a "plugin" to the editor, which is usually - // a single button on the Toolbar and some associated code - - constructor: function(/*Object?*/args, /*DomNode?*/node){ - this.params = args || {}; - dojo.mixin(this, this.params); - this._connects=[]; - }, - - // editor: [const] dijit.Editor - // Points to the parent editor - editor: null, - - // iconClassPrefix: [const] String - // The CSS class name for the button node is formed from `iconClassPrefix` and `command` - iconClassPrefix: "dijitEditorIcon", - - // button: dijit._Widget? - // Pointer to `dijit.form.Button` or other widget (ex: `dijit.form.FilteringSelect`) - // that is added to the toolbar to control this plugin. - // If not specified, will be created on initialization according to `buttonClass` - button: null, - - // command: String - // String like "insertUnorderedList", "outdent", "justifyCenter", etc. that represents an editor command. - // Passed to editor.execCommand() if `useDefaultCommand` is true. - command: "", - - // useDefaultCommand: Boolean - // If true, this plugin executes by calling Editor.execCommand() with the argument specified in `command`. - useDefaultCommand: true, - - // buttonClass: Widget Class - // Class of widget (ex: dijit.form.Button or dijit.form.FilteringSelect) - // that is added to the toolbar to control this plugin. - // This is used to instantiate the button, unless `button` itself is specified directly. - buttonClass: dijit.form.Button, - - getLabel: function(/*String*/key){ - // summary: - // Returns the label to use for the button - // tags: - // private - return this.editor.commands[key]; // String - }, - - _initButton: function(){ - // summary: - // Initialize the button or other widget that will control this plugin. - // This code only works for plugins controlling built-in commands in the editor. - // tags: - // protected extension - if(this.command.length){ - var label = this.getLabel(this.command), - editor = this.editor, - className = this.iconClassPrefix+" "+this.iconClassPrefix + this.command.charAt(0).toUpperCase() + this.command.substr(1); - if(!this.button){ - var props = dojo.mixin({ - label: label, - dir: editor.dir, - lang: editor.lang, - showLabel: false, - iconClass: className, - dropDown: this.dropDown, - tabIndex: "-1" - }, this.params || {}); - this.button = new this.buttonClass(props); - } - } - }, - - destroy: function(){ - // summary: - // Destroy this plugin - - dojo.forEach(this._connects, dojo.disconnect); - if(this.dropDown){ - this.dropDown.destroyRecursive(); - } - }, - - connect: function(o, f, tf){ - // summary: - // Make a dojo.connect() that is automatically disconnected when this plugin is destroyed. - // Similar to `dijit._Widget.connect`. - // tags: - // protected - this._connects.push(dojo.connect(o, f, this, tf)); - }, - - updateState: function(){ - // summary: - // Change state of the plugin to respond to events in the editor. - // description: - // This is called on meaningful events in the editor, such as change of selection - // or caret position (but not simple typing of alphanumeric keys). It gives the - // plugin a chance to update the CSS of its button. - // - // For example, the "bold" plugin will highlight/unhighlight the bold button depending on whether the - // characters next to the caret are bold or not. - // - // Only makes sense when `useDefaultCommand` is true, as it calls Editor.queryCommandEnabled(`command`). - var e = this.editor, - c = this.command, - checked, enabled; - if(!e || !e.isLoaded || !c.length){ return; } - if(this.button){ - try{ - enabled = e.queryCommandEnabled(c); - if(this.enabled !== enabled){ - this.enabled = enabled; - this.button.set('disabled', !enabled); - } - if(typeof this.button.checked == 'boolean'){ - checked = e.queryCommandState(c); - if(this.checked !== checked){ - this.checked = checked; - this.button.set('checked', e.queryCommandState(c)); - } - } - }catch(e){ - console.log(e); // FIXME: we shouldn't have debug statements in our code. Log as an error? - } - } - }, - - setEditor: function(/*dijit.Editor*/ editor){ - // summary: - // Tell the plugin which Editor it is associated with. - - // TODO: refactor code to just pass editor to constructor. - - // FIXME: detach from previous editor!! - this.editor = editor; - - // FIXME: prevent creating this if we don't need to (i.e., editor can't handle our command) - this._initButton(); - - // Processing for buttons that execute by calling editor.execCommand() - if(this.button && this.useDefaultCommand){ - if(this.editor.queryCommandAvailable(this.command)){ - this.connect(this.button, "onClick", - dojo.hitch(this.editor, "execCommand", this.command, this.commandArg) - ); - }else{ - // hide button because editor doesn't support command (due to browser limitations) - this.button.domNode.style.display = "none"; - } - } - - this.connect(this.editor, "onNormalizedDisplayChanged", "updateState"); - }, - - setToolbar: function(/*dijit.Toolbar*/ toolbar){ - // summary: - // Tell the plugin to add it's controller widget (often a button) - // to the toolbar. Does nothing if there is no controller widget. - - // TODO: refactor code to just pass toolbar to constructor. - - if(this.button){ - toolbar.addChild(this.button); - } - // console.debug("adding", this.button, "to:", toolbar); - } -}); - -} - -if(!dojo._hasResource["dijit._editor.plugins.EnterKeyHandling"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit._editor.plugins.EnterKeyHandling"] = true; -dojo.provide("dijit._editor.plugins.EnterKeyHandling"); - - - -dojo.declare("dijit._editor.plugins.EnterKeyHandling", dijit._editor._Plugin, { - // summary: - // This plugin tries to make all browsers behave consistently w.r.t - // displaying paragraphs, specifically dealing with when the user presses - // the ENTER key. - // - // It deals mainly with how the text appears on the screen (specifically - // address the double-spaced line problem on IE), but also has some code - // to normalize what attr('value') returns. - // - // description: - // This plugin has three modes: - // - // * blockModeForEnter=BR - // * blockModeForEnter=DIV - // * blockModeForEnter=P - // - // In blockModeForEnter=P, the ENTER key semantically means "start a new - // paragraph", whereas shift-ENTER means "new line in the current paragraph". - // For example: - // - // | first paragraph <shift-ENTER> - // | second line of first paragraph <ENTER> - // | - // | second paragraph - // - // In the other two modes, the ENTER key means to go to a new line in the - // current paragraph, and users [visually] create a new paragraph by pressing ENTER twice. - // For example, if the user enters text into an editor like this: - // - // | one <ENTER> - // | two <ENTER> - // | three <ENTER> - // | <ENTER> - // | four <ENTER> - // | five <ENTER> - // | six <ENTER> - // - // It will appear on the screen as two paragraphs of three lines each. - // - // blockNodeForEnter=BR - // -------------------- - // On IE, typing the above keystrokes in the editor will internally produce DOM of: - // - // | <p>one</p> - // | <p>two</p> - // | <p>three</p> - // | <p></p> - // | <p>four</p> - // | <p>five</p> - // | <p>six</p> - // - // However, blockNodeForEnter=BR makes the Editor on IE display like other browsers, by - // changing the CSS for the <p> node to not have top/bottom margins, - // thus eliminating the double-spaced appearance. - // - // Also, attr('value') when used w/blockNodeForEnter=br on IE will return: - // - // | <p> one <br> two <br> three </p> - // | <p> four <br> five <br> six </p> - // - // This output normalization implemented by a filter when the - // editor writes out it's data, to convert consecutive <p> - // nodes into a single <p> node with internal <br> separators. - // - // There's also a pre-filter to mirror the post-filter. - // It converts a single <p> with <br> line breaks - // into separate <p> nodes, and creates empty <p> nodes for spacing - // between paragraphs. - // - // On FF typing the above keystrokes will internally generate: - // - // | one <br> two <br> three <br> <br> four <br> five <br> six <br> - // - // And on Safari it will generate: - // - // | "one" - // | <div>two</div> - // | <div>three</div> - // | <div><br></div> - // | <div>four</div> - // | <div>five</div> - // | <div>six</div> - // - // Thus, Safari and FF already look correct although semantically their content is a bit strange. - // On Safari or Firefox blockNodeForEnter=BR uses the builtin editor command "insertBrOnReturn", - // but that doesn't seem to do anything. - // Thus, attr('value') on safari/FF returns the browser-specific HTML listed above, - // rather than the semantically meaningful value that IE returns: <p>one<br>two</p> <p>three<br>four</p>. - // - // (Note: originally based on http://bugs.dojotoolkit.org/ticket/2859) - // - // blockNodeForEnter=P - // ------------------- - // Plugin will monitor keystrokes and update the editor's content on the fly, - // so that the ENTER key will create a new <p> on FF and Safari (it already - // works that way by default on IE). - // - // blockNodeForEnter=DIV - // --------------------- - // Follows the same code path as blockNodeForEnter=P but inserting a <div> - // on ENTER key. Although it produces strange internal DOM, like this: - // - // | <div>paragraph one</div> - // | <div>paragraph one, line 2</div> - // | <div> </div> - // | <div>paragraph two</div> - // - // it does provide a consistent look on all browsers, and the on-the-fly DOM updating - // can be useful for collaborative editing. - - // 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. - // - // See class description for more details. - blockNodeForEnter: 'BR', - - constructor: function(args){ - if(args){ - dojo.mixin(this,args); - } - }, - - setEditor: function(editor){ - // Overrides _Plugin.setEditor(). - this.editor = editor; - if(this.blockNodeForEnter == 'BR'){ - if(dojo.isIE){ - editor.contentDomPreFilters.push(dojo.hitch(this, "regularPsToSingleLinePs")); - editor.contentDomPostFilters.push(dojo.hitch(this, "singleLinePsToRegularPs")); - editor.onLoadDeferred.addCallback(dojo.hitch(this, "_fixNewLineBehaviorForIE")); - }else{ - editor.onLoadDeferred.addCallback(dojo.hitch(this,function(d){ - try{ - this.editor.document.execCommand("insertBrOnReturn", false, true); - }catch(e){} - return d; - })); - } - }else if(this.blockNodeForEnter){ - // add enter key handler - // FIXME: need to port to the new event code!! - dojo['require']('dijit._editor.range'); - var h = dojo.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(e){ - // summary: - // Handler for keypress events. - // tags: - // private - if(this._checkListLater){ - if(dojo.withGlobal(this.editor.window, 'isCollapsed', dijit)){ - var liparent=dojo.withGlobal(this.editor.window, 'getAncestorElement', dijit._editor.selection, ['LI']); - if(!liparent){ - // circulate the undo detection code by calling RichText::execCommand directly - dijit._editor.RichText.prototype.execCommand.call(this.editor, 'formatblock',this.blockNodeForEnter); - // set the innerHTML of the new block node - var block = dojo.withGlobal(this.editor.window, 'getAncestorElement', dijit._editor.selection, [this.blockNodeForEnter]); - if(block){ - block.innerHTML=this.bogusHtmlContent; - if(dojo.isIE){ - // 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(dojo.isMoz){ - 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 = dijit.range.create(this.editor.window); - newrange.setStart(liparent.firstChild,0); - var selection = dijit.range.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 blockModeForEnter 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, doc=this.editor.document,br; - if(e.shiftKey){ // shift+enter always generates <br> - var parent = dojo.withGlobal(this.editor.window, "getParentElement", dijit._editor.selection); - var header = dijit.range.getAncestor(parent,this.blockNodes); - if(header){ - if(!e.shiftKey && header.tagName == 'LI'){ - return true; // let browser handle - } - selection = dijit.range.getSelection(this.editor.window); - range = selection.getRangeAt(0); - if(!range.collapsed){ - range.deleteContents(); - selection = dijit.range.getSelection(this.editor.window); - range = selection.getRangeAt(0); - } - if(dijit.range.atBeginningOfContainer(header, range.startContainer, range.startOffset)){ - if(e.shiftKey){ - br=doc.createElement('br'); - newrange = dijit.range.create(this.editor.window); - header.insertBefore(br,header.firstChild); - newrange.setStartBefore(br.nextSibling); - selection.removeAllRanges(); - selection.addRange(newrange); - }else{ - dojo.place(br, header, "before"); - } - }else if(dijit.range.atEndOfContainer(header, range.startContainer, range.startOffset)){ - newrange = dijit.range.create(this.editor.window); - br=doc.createElement('br'); - if(e.shiftKey){ - header.appendChild(br); - header.appendChild(doc.createTextNode('\xA0')); - newrange.setStart(header.lastChild,0); - }else{ - dojo.place(br, header, "after"); - newrange.setStartAfter(header); - } - - selection.removeAllRanges(); - selection.addRange(newrange); - }else{ - return true; // let browser handle - } - }else{ - // don't change this: do not call this.execCommand, as that may have other logic in subclass - dijit._editor.RichText.prototype.execCommand.call(this.editor, 'inserthtml', '<br>'); - } - return false; - } - var _letBrowserHandle = true; - - // first remove selection - selection = dijit.range.getSelection(this.editor.window); - range = selection.getRangeAt(0); - if(!range.collapsed){ - range.deleteContents(); - selection = dijit.range.getSelection(this.editor.window); - range = selection.getRangeAt(0); - } - - var block = dijit.range.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(dojo.isMoz){ - // press enter in middle of P may leave a trailing <br/>, let's remove it later - this._pressedEnterInBlock = blockNode; - } - // if this li only contains spaces, set the content to empty so the browser will outdent this item - if(/^(\s| |\xA0|<span\b[^>]*\bclass=['"]Apple-style-span['"][^>]*>(\s| |\xA0)<\/span>)?(<br>)?$/.test(blockNode.innerHTML)){ - // empty LI node - blockNode.innerHTML = ''; - if(dojo.isWebKit){ // WebKit tosses the range when innerHTML is reset - newrange = dijit.range.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{ - dijit._editor.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:dojo.withGlobal(this.editor.window, "getAncestorElement", dijit._editor.selection, [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 = dijit.range.getSelection(this.editor.window); - range = selection.getRangeAt(0); - } - - var newblock = doc.createElement(this.blockNodeForEnter); - newblock.innerHTML=this.bogusHtmlContent; - this.removeTrailingBr(block.blockNode); - if(dijit.range.atEndOfContainer(block.blockNode, range.endContainer, range.endOffset)){ - if(block.blockNode === block.blockContainer){ - block.blockNode.appendChild(newblock); - }else{ - dojo.place(newblock, block.blockNode, "after"); - } - _letBrowserHandle = false; - // lets move caret to the newly created block - newrange = dijit.range.create(this.editor.window); - newrange.setStart(newblock, 0); - selection.removeAllRanges(); - selection.addRange(newrange); - if(this.editor.height){ - dojo.window.scrollIntoView(newblock); - } - }else if(dijit.range.atBeginningOfContainer(block.blockNode, - range.startContainer, range.startOffset)){ - dojo.place(newblock, block.blockNode, block.blockNode === block.blockContainer ? "first" : "before"); - if(newblock.nextSibling && this.editor.height){ - // position input caret - mostly WebKit needs this - newrange = dijit.range.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 - dojo.window.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{ - dojo.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. - var rs = range.startContainer; - if(rs && rs.nodeType == 3){ - // Text node, we have to split it. - var nodeToMove, tNode; - var txt = rs.nodeValue; - var startNode = doc.createTextNode(txt.substring(0, range.startOffset)); - var endNode = doc.createTextNode(txt.substring(range.startOffset, txt.length)); - - // Place the split, then remove original nodes. - dojo.place(startNode, rs, "before"); - dojo.place(endNode, rs, "after"); - dojo.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; - } - } - } - - nodeToMove = endNode; - while(nodeToMove){ - tNode = nodeToMove.nextSibling; - newTg.appendChild(nodeToMove); - nodeToMove = tNode; - } - dojo.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 = ""; - } - while(nodeToMove){ - tNode = nodeToMove.nextSibling; - newblock.appendChild(nodeToMove); - nodeToMove = tNode; - } - } - - //lets move caret to the newly created block - newrange = dijit.range.create(this.editor.window); - newrange.setStart(newblock, 0); - selection.removeAllRanges(); - selection.addRange(newrange); - if(this.editor.height){ - dijit.scrollIntoView(newblock); - } - if(dojo.isMoz){ - // press enter in middle of P may leave a trailing <br/>, let's remove it later - this._pressedEnterInBlock = block.blockNode; - } - } - return _letBrowserHandle; - }, - - removeTrailingBr: function(container){ - // summary: - // If last child of container is a <br>, then remove it. - // tags: - // private - var para = /P|DIV|LI/i.test(container.tagName) ? - container : dijit._editor.selection.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'){ - - dojo.destroy(para.lastChild); - } - } - if(!para.childNodes.length){ - para.innerHTML=this.bogusHtmlContent; - } - }, - _fixNewLineBehaviorForIE: function(d){ - // summary: - // Insert CSS so <p> nodes don't have spacing around them, - // thus hiding the fact that ENTER key on IE is creating new - // paragraphs - - // cannot use !important since there may be custom user styling; - var doc = this.editor.document; - if(doc.__INSERTED_EDITIOR_NEWLINE_CSS === undefined){ - var style = dojo.create("style", {type: "text/css"}, doc.getElementsByTagName("head")[0]); - style.styleSheet.cssText = "p{margin:0;}"; // cannot use !important since there may be custom user styling; - this.editor.document.__INSERTED_EDITIOR_NEWLINE_CSS = true; - } - return d; - }, - regularPsToSingleLinePs: function(element, noWhiteSpaceInEmptyP){ - // summary: - // Converts a <p> node containing <br>'s into multiple <p> nodes. - // description: - // See singleLinePsToRegularPs(). This method does the - // opposite thing, and is used as a pre-filter when loading the - // editor, to mirror the effects of the post-filter at end of edit. - // tags: - // private - function wrapLinesInPs(el){ - // move "lines" of top-level text nodes into ps - function wrapNodes(nodes){ - // nodes are assumed to all be siblings - var newP = nodes[0].ownerDocument.createElement('p'); // FIXME: not very idiomatic - nodes[0].parentNode.insertBefore(newP, nodes[0]); - dojo.forEach(nodes, function(node){ - newP.appendChild(node); - }); - } - - var currentNodeIndex = 0; - var nodesInLine = []; - var currentNode; - while(currentNodeIndex < el.childNodes.length){ - currentNode = el.childNodes[currentNodeIndex]; - if( currentNode.nodeType==3 || // text node - (currentNode.nodeType==1 && currentNode.nodeName!='BR' && dojo.style(currentNode, "display")!="block") - ){ - nodesInLine.push(currentNode); - }else{ - // hit line delimiter; process nodesInLine if there are any - var nextCurrentNode = currentNode.nextSibling; - if(nodesInLine.length){ - wrapNodes(nodesInLine); - currentNodeIndex = (currentNodeIndex+1)-nodesInLine.length; - if(currentNode.nodeName=="BR"){ - dojo.destroy(currentNode); - } - } - nodesInLine = []; - } - currentNodeIndex++; - } - if(nodesInLine.length){ wrapNodes(nodesInLine); } - } - - function splitP(el){ - // split a paragraph into seperate paragraphs at BRs - var currentNode = null; - var trailingNodes = []; - var lastNodeIndex = el.childNodes.length-1; - for(var i=lastNodeIndex; i>=0; i--){ - currentNode = el.childNodes[i]; - if(currentNode.nodeName=="BR"){ - var newP = currentNode.ownerDocument.createElement('p'); - dojo.place(newP, el, "after"); - if(trailingNodes.length==0 && i != lastNodeIndex){ - newP.innerHTML = " " - } - dojo.forEach(trailingNodes, function(node){ - newP.appendChild(node); - }); - dojo.destroy(currentNode); - trailingNodes = []; - }else{ - trailingNodes.unshift(currentNode); - } - } - } - - var pList = []; - var ps = element.getElementsByTagName('p'); - dojo.forEach(ps, function(p){ pList.push(p); }); - dojo.forEach(pList, function(p){ - var prevSib = p.previousSibling; - if( (prevSib) && (prevSib.nodeType == 1) && - (prevSib.nodeName == 'P' || dojo.style(prevSib, 'display') != 'block') - ){ - var newP = p.parentNode.insertBefore(this.document.createElement('p'), p); - // this is essential to prevent IE from losing the P. - // if it's going to be innerHTML'd later we need - // to add the to _really_ force the issue - newP.innerHTML = noWhiteSpaceInEmptyP ? "" : " "; - } - splitP(p); - },this.editor); - wrapLinesInPs(element); - return element; - }, - - singleLinePsToRegularPs: function(element){ - // summary: - // Called as post-filter. - // Apparently collapses adjacent <p> nodes into a single <p> - // nodes with <br> separating each line. - // - // example: - // Given this input: - // | <p>line 1</p> - // | <p>line 2</p> - // | <ol> - // | <li>item 1 - // | <li>item 2 - // | </ol> - // | <p>line 3</p> - // | <p>line 4</p> - // - // Will convert to: - // | <p>line 1<br>line 2</p> - // | <ol> - // | <li>item 1 - // | <li>item 2 - // | </ol> - // | <p>line 3<br>line 4</p> - // - // Not sure why this situation would even come up after the pre-filter and - // the enter-key-handling code. - // - // tags: - // private - - function getParagraphParents(node){ - // summary: - // Used to get list of all nodes that contain paragraphs. - // Seems like that would just be the very top node itself, but apparently not. - var ps = node.getElementsByTagName('p'); - var parents = []; - for(var i=0; i<ps.length; i++){ - var p = ps[i]; - var knownParent = false; - for(var k=0; k < parents.length; k++){ - if(parents[k] === p.parentNode){ - knownParent = true; - break; - } - } - if(!knownParent){ - parents.push(p.parentNode); - } - } - return parents; - } - - function isParagraphDelimiter(node){ - return (!node.childNodes.length || node.innerHTML==" "); - } - - var paragraphContainers = getParagraphParents(element); - for(var i=0; i<paragraphContainers.length; i++){ - var container = paragraphContainers[i]; - var firstPInBlock = null; - var node = container.firstChild; - var deleteNode = null; - while(node){ - if(node.nodeType != 1 || node.tagName != 'P' || - (node.getAttributeNode('style') || {/*no style*/}).specified){ - firstPInBlock = null; - }else if(isParagraphDelimiter(node)){ - deleteNode = node; - firstPInBlock = null; - }else{ - if(firstPInBlock == null){ - firstPInBlock = node; - }else{ - if( (!firstPInBlock.lastChild || firstPInBlock.lastChild.nodeName != 'BR') && - (node.firstChild) && - (node.firstChild.nodeName != 'BR') - ){ - firstPInBlock.appendChild(this.editor.document.createElement('br')); - } - while(node.firstChild){ - firstPInBlock.appendChild(node.firstChild); - } - deleteNode = node; - } - } - node = node.nextSibling; - if(deleteNode){ - dojo.destroy(deleteNode); - deleteNode = null; - } - } - } - return element; - } -}); - -} - -if(!dojo._hasResource["dijit.Editor"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.Editor"] = true; -dojo.provide("dijit.Editor"); - - - - - - - - - - - - - -dojo.declare( - "dijit.Editor", - dijit._editor.RichText, - { - // summary: - // A rich text Editing widget - // - // description: - // This widget provides basic WYSIWYG editing features, based on the browser's - // underlying rich text editing capability, accompanied by a toolbar (`dijit.Toolbar`). - // A plugin model is available to extend the editor's capabilities as well as the - // the options available in the toolbar. Content generation may vary across - // browsers, and clipboard operations may have different results, to name - // a few limitations. Note: this widget should not be used with the HTML - // <TEXTAREA> tag -- see dijit._editor.RichText for details. - - // plugins: Object[] - // A list of plugin names (as strings) or instances (as objects) - // for this widget. - // - // When declared in markup, it might look like: - // | plugins="['bold',{name:'dijit._editor.plugins.FontChoice', command:'fontName', generic:true}]" - plugins: null, - - // extraPlugins: Object[] - // A list of extra plugin names which will be appended to plugins array - extraPlugins: null, - - constructor: function(){ - // summary: - // Runs on widget initialization to setup arrays etc. - // tags: - // private - - if(!dojo.isArray(this.plugins)){ - this.plugins=["undo","redo","|","cut","copy","paste","|","bold","italic","underline","strikethrough","|", - "insertOrderedList","insertUnorderedList","indent","outdent","|","justifyLeft","justifyRight","justifyCenter","justifyFull", - "dijit._editor.plugins.EnterKeyHandling" /*, "createLink"*/]; - } - - this._plugins=[]; - this._editInterval = this.editActionInterval * 1000; - - //IE will always lose focus when other element gets focus, while for FF and safari, - //when no iframe is used, focus will be lost whenever another element gets focus. - //For IE, we can connect to onBeforeDeactivate, which will be called right before - //the focus is lost, so we can obtain the selected range. For other browsers, - //no equivelent of onBeforeDeactivate, so we need to do two things to make sure - //selection is properly saved before focus is lost: 1) when user clicks another - //element in the page, in which case we listen to mousedown on the entire page and - //see whether user clicks out of a focus editor, if so, save selection (focus will - //only lost after onmousedown event is fired, so we can obtain correct caret pos.) - //2) when user tabs away from the editor, which is handled in onKeyDown below. - if(dojo.isIE){ - this.events.push("onBeforeDeactivate"); - this.events.push("onBeforeActivate"); - } - }, - - postCreate: function(){ - //for custom undo/redo, if enabled. - this._steps=this._steps.slice(0); - this._undoedSteps=this._undoedSteps.slice(0); - - if(dojo.isArray(this.extraPlugins)){ - this.plugins=this.plugins.concat(this.extraPlugins); - } - - // Set up a deferred so that the value isn't applied to the editor - // until all the plugins load, needed to avoid timing condition - // reported in #10537. - this.setValueDeferred = new dojo.Deferred(); - - this.inherited(arguments); - - this.commands = dojo.i18n.getLocalization("dijit._editor", "commands", this.lang); - - if(!this.toolbar){ - // if we haven't been assigned a toolbar, create one - this.toolbar = new dijit.Toolbar({ - dir: this.dir, - lang: this.lang - }); - this.header.appendChild(this.toolbar.domNode); - } - - dojo.forEach(this.plugins, this.addPlugin, this); - - // Okay, denote the value can now be set. - this.setValueDeferred.callback(true); - - dojo.addClass(this.iframe.parentNode, "dijitEditorIFrameContainer"); - dojo.addClass(this.iframe, "dijitEditorIFrame"); - dojo.attr(this.iframe, "allowTransparency", true); - - if(dojo.isWebKit){ - // Disable selecting the entire editor by inadvertant double-clicks. - // on buttons, title bar, etc. Otherwise clicking too fast on - // a button such as undo/redo selects the entire editor. - dojo.style(this.domNode, "KhtmlUserSelect", "none"); - } - this.toolbar.startup(); - this.onNormalizedDisplayChanged(); //update toolbar button status - }, - destroy: function(){ - dojo.forEach(this._plugins, function(p){ - if(p && p.destroy){ - p.destroy(); - } - }); - this._plugins=[]; - this.toolbar.destroyRecursive(); - delete this.toolbar; - this.inherited(arguments); - }, - addPlugin: function(/*String||Object*/plugin, /*Integer?*/index){ - // summary: - // takes a plugin name as a string or a plugin instance and - // adds it to the toolbar and associates it with this editor - // instance. The resulting plugin is added to the Editor's - // plugins array. If index is passed, it's placed in the plugins - // array at that index. No big magic, but a nice helper for - // passing in plugin names via markup. - // - // plugin: String, args object or plugin instance - // - // args: - // This object will be passed to the plugin constructor - // - // index: Integer - // Used when creating an instance from - // something already in this.plugins. Ensures that the new - // instance is assigned to this.plugins at that index. - var args=dojo.isString(plugin)?{name:plugin}:plugin; - if(!args.setEditor){ - var o={"args":args,"plugin":null,"editor":this}; - dojo.publish(dijit._scopeName + ".Editor.getPlugin",[o]); - if(!o.plugin){ - var pc = dojo.getObject(args.name); - if(pc){ - o.plugin=new pc(args); - } - } - if(!o.plugin){ - console.warn('Cannot find plugin',plugin); - return; - } - plugin=o.plugin; - } - if(arguments.length > 1){ - this._plugins[index] = plugin; - }else{ - this._plugins.push(plugin); - } - plugin.setEditor(this); - if(dojo.isFunction(plugin.setToolbar)){ - plugin.setToolbar(this.toolbar); - } - }, - //the following 3 functions are required to make the editor play nice under a layout widget, see #4070 - startup: function(){ - // summary: - // Exists to make Editor work as a child of a layout widget. - // Developers don't need to call this method. - // tags: - // protected - //console.log('startup',arguments); - }, - resize: function(size){ - // summary: - // Resize the editor to the specified size, see `dijit.layout._LayoutWidget.resize` - if(size){ - // we've been given a height/width for the entire editor (toolbar + contents), calls layout() - // to split the allocated size between the toolbar and the contents - dijit.layout._LayoutWidget.prototype.resize.apply(this, arguments); - } - /* - else{ - // do nothing, the editor is already laid out correctly. The user has probably specified - // the height parameter, which was used to set a size on the iframe - } - */ - }, - layout: function(){ - // summary: - // Called from `dijit.layout._LayoutWidget.resize`. This shouldn't be called directly - // tags: - // protected - - // Converts the iframe (or rather the <div> surrounding it) to take all the available space - // except what's needed for the header (toolbars) and footer (breadcrumbs, etc). - // A class was added to the iframe container and some themes style it, so we have to - // calc off the added margins and padding too. See tracker: #10662 - var areaHeight = (this._contentBox.h - - (this.getHeaderHeight() + this.getFooterHeight() + - dojo._getPadBorderExtents(this.iframe.parentNode).h + - dojo._getMarginExtents(this.iframe.parentNode).h)); - this.editingArea.style.height = areaHeight + "px"; - if(this.iframe){ - this.iframe.style.height="100%"; - } - this._layoutMode = true; - }, - _onIEMouseDown: function(/*Event*/ e){ - // summary: - // IE only to prevent 2 clicks to focus - // tags: - // private - var outsideClientArea; - // IE 8's componentFromPoint is broken, which is a shame since it - // was smaller code, but oh well. We have to do this brute force - // to detect if the click was scroller or not. - var b = this.document.body; - var clientWidth = b.clientWidth; - var clientHeight = b.clientHeight; - var clientLeft = b.clientLeft; - var offsetWidth = b.offsetWidth; - var offsetHeight = b.offsetHeight; - var offsetLeft = b.offsetLeft; - - //Check for vertical scroller click. - bodyDir = b.dir?b.dir.toLowerCase():"" - if(bodyDir != "rtl"){ - if(clientWidth < offsetWidth && e.x > clientWidth && e.x < offsetWidth){ - // Check the click was between width and offset width, if so, scroller - outsideClientArea = true; - } - }else{ - // RTL mode, we have to go by the left offsets. - if(e.x < clientLeft && e.x > offsetLeft){ - // Check the click was between width and offset width, if so, scroller - outsideClientArea = true; - } - } - if(!outsideClientArea){ - // Okay, might be horiz scroller, check that. - if(clientHeight < offsetHeight && e.y > clientHeight && e.y < offsetHeight){ - // Horizontal scroller. - outsideClientArea = true; - } - } - if(!outsideClientArea){ - delete this._cursorToStart; // Remove the force to cursor to start position. - delete this._savedSelection; // new mouse position overrides old selection - if(e.target.tagName == "BODY"){ - setTimeout(dojo.hitch(this, "placeCursorAtEnd"), 0); - } - this.inherited(arguments); - } - }, - onBeforeActivate: function(e){ - this._restoreSelection(); - }, - onBeforeDeactivate: function(e){ - // summary: - // Called on IE right before focus is lost. Saves the selected range. - // tags: - // private - if(this.customUndo){ - this.endEditing(true); - } - //in IE, the selection will be lost when other elements get focus, - //let's save focus before the editor is deactivated - if(e.target.tagName != "BODY"){ - this._saveSelection(); - } - //console.log('onBeforeDeactivate',this); - }, - - /* beginning of custom undo/redo support */ - - // customUndo: Boolean - // Whether we shall use custom undo/redo support instead of the native - // browser support. By default, we only enable customUndo for IE, as it - // has broken native undo/redo support. Note: the implementation does - // support other browsers which have W3C DOM2 Range API implemented. - // It was also enabled on WebKit, to fix undo/redo enablement. (#9613) - customUndo: dojo.isIE || dojo.isWebKit, - - // editActionInterval: Integer - // When using customUndo, not every keystroke will be saved as a step. - // Instead typing (including delete) will be grouped together: after - // a user stops typing for editActionInterval seconds, a step will be - // saved; if a user resume typing within editActionInterval seconds, - // the timeout will be restarted. By default, editActionInterval is 3 - // seconds. - editActionInterval: 3, - - beginEditing: function(cmd){ - // summary: - // Called to note that the user has started typing alphanumeric characters, if it's not already noted. - // Deals with saving undo; see editActionInterval parameter. - // tags: - // private - if(!this._inEditing){ - this._inEditing=true; - this._beginEditing(cmd); - } - if(this.editActionInterval>0){ - if(this._editTimer){ - clearTimeout(this._editTimer); - } - this._editTimer = setTimeout(dojo.hitch(this, this.endEditing), this._editInterval); - } - }, - _steps:[], - _undoedSteps:[], - execCommand: function(cmd){ - // summary: - // Main handler for executing any commands to the editor, like paste, bold, etc. - // Called by plugins, but not meant to be called by end users. - // tags: - // protected - if(this.customUndo && (cmd == 'undo' || cmd == 'redo')){ - return this[cmd](); - }else{ - if(this.customUndo){ - this.endEditing(); - this._beginEditing(); - } - var r; - try{ - r = this.inherited('execCommand', arguments); - if(dojo.isWebKit && cmd == 'paste' && !r){ //see #4598: safari does not support invoking paste from js - throw { code: 1011 }; // throw an object like Mozilla's error - } - }catch(e){ - //TODO: when else might we get an exception? Do we need the Mozilla test below? - if(e.code == 1011 /* Mozilla: service denied */ && /copy|cut|paste/.test(cmd)){ - // Warn user of platform limitation. Cannot programmatically access clipboard. See ticket #4136 - var sub = dojo.string.substitute, - accel = {cut:'X', copy:'C', paste:'V'}; - alert(sub(this.commands.systemShortcut, - [this.commands[cmd], sub(this.commands[dojo.isMac ? 'appleKey' : 'ctrlKey'], [accel[cmd]])])); - } - r = false; - } - if(this.customUndo){ - this._endEditing(); - } - return r; - } - }, - queryCommandEnabled: function(cmd){ - // summary: - // Returns true if specified editor command is enabled. - // Used by the plugins to know when to highlight/not highlight buttons. - // tags: - // protected - if(this.customUndo && (cmd == 'undo' || cmd == 'redo')){ - return cmd == 'undo' ? (this._steps.length > 1) : (this._undoedSteps.length > 0); - }else{ - return this.inherited('queryCommandEnabled',arguments); - } - }, - _moveToBookmark: function(b){ - // summary: - // Selects the text specified in bookmark b - // tags: - // private - var bookmark = b.mark; - var mark = b.mark; - var col = b.isCollapsed; - var r, sNode, eNode, sel; - if(mark){ - if(dojo.isIE){ - if(dojo.isArray(mark)){ - //IE CONTROL, have to use the native bookmark. - bookmark = []; - dojo.forEach(mark,function(n){ - bookmark.push(dijit.range.getNode(n,this.editNode)); - },this); - dojo.withGlobal(this.window,'moveToBookmark',dijit,[{mark: bookmark, isCollapsed: col}]); - }else{ - if(mark.startContainer && mark.endContainer){ - // Use the pseudo WC3 range API. This works better for positions - // than the IE native bookmark code. - sel = dijit.range.getSelection(this.window); - if(sel && sel.removeAllRanges){ - sel.removeAllRanges(); - r = dijit.range.create(this.window); - sNode = dijit.range.getNode(mark.startContainer,this.editNode); - eNode = dijit.range.getNode(mark.endContainer,this.editNode); - if(sNode && eNode){ - // Okay, we believe we found the position, so add it into the selection - // There are cases where it may not be found, particularly in undo/redo, when - // IE changes the underlying DOM on us (wraps text in a <p> tag or similar. - // So, in those cases, don't bother restoring selection. - r.setStart(sNode,mark.startOffset); - r.setEnd(eNode,mark.endOffset); - sel.addRange(r); - } - } - } - } - }else{//w3c range - sel = dijit.range.getSelection(this.window); - if(sel && sel.removeAllRanges){ - sel.removeAllRanges(); - r = dijit.range.create(this.window); - sNode = dijit.range.getNode(mark.startContainer,this.editNode); - eNode = dijit.range.getNode(mark.endContainer,this.editNode); - if(sNode && eNode){ - // Okay, we believe we found the position, so add it into the selection - // There are cases where it may not be found, particularly in undo/redo, when - // formatting as been done and so on, so don't restore selection then. - r.setStart(sNode,mark.startOffset); - r.setEnd(eNode,mark.endOffset); - sel.addRange(r); - } - } - } - } - }, - _changeToStep: function(from, to){ - // summary: - // Reverts editor to "to" setting, from the undo stack. - // tags: - // private - this.setValue(to.text); - var b=to.bookmark; - if(!b){ return; } - this._moveToBookmark(b); - }, - undo: function(){ - // summary: - // Handler for editor undo (ex: ctrl-z) operation - // tags: - // private - //console.log('undo'); - var ret = false; - if(!this._undoRedoActive){ - this._undoRedoActive = true; - this.endEditing(true); - var s=this._steps.pop(); - if(s && this._steps.length>0){ - this.focus(); - this._changeToStep(s,this._steps[this._steps.length-1]); - this._undoedSteps.push(s); - this.onDisplayChanged(); - delete this._undoRedoActive; - ret = true; - } - delete this._undoRedoActive; - } - return ret; - }, - redo: function(){ - // summary: - // Handler for editor redo (ex: ctrl-y) operation - // tags: - // private - //console.log('redo'); - var ret = false; - if(!this._undoRedoActive){ - this._undoRedoActive = true; - this.endEditing(true); - var s=this._undoedSteps.pop(); - if(s && this._steps.length>0){ - this.focus(); - this._changeToStep(this._steps[this._steps.length-1],s); - this._steps.push(s); - this.onDisplayChanged(); - ret = true; - } - delete this._undoRedoActive; - } - return ret; - }, - endEditing: function(ignore_caret){ - // summary: - // Called to note that the user has stopped typing alphanumeric characters, if it's not already noted. - // Deals with saving undo; see editActionInterval parameter. - // tags: - // private - if(this._editTimer){ - clearTimeout(this._editTimer); - } - if(this._inEditing){ - this._endEditing(ignore_caret); - this._inEditing=false; - } - }, - - _getBookmark: function(){ - // summary: - // Get the currently selected text - // tags: - // protected - var b=dojo.withGlobal(this.window,dijit.getBookmark); - var tmp=[]; - if(b && b.mark){ - var mark = b.mark; - if(dojo.isIE){ - // Try to use the pseudo range API on IE for better accuracy. - var sel = dijit.range.getSelection(this.window); - if(!dojo.isArray(mark)){ - if(sel){ - var range; - if(sel.rangeCount){ - range = sel.getRangeAt(0); - } - if(range){ - b.mark = range.cloneRange(); - }else{ - b.mark = dojo.withGlobal(this.window,dijit.getBookmark); - } - } - }else{ - // Control ranges (img, table, etc), handle differently. - dojo.forEach(b.mark,function(n){ - tmp.push(dijit.range.getIndex(n,this.editNode).o); - },this); - b.mark = tmp; - } - } - try{ - if(b.mark && b.mark.startContainer){ - tmp=dijit.range.getIndex(b.mark.startContainer,this.editNode).o; - b.mark={startContainer:tmp, - startOffset:b.mark.startOffset, - endContainer:b.mark.endContainer===b.mark.startContainer?tmp:dijit.range.getIndex(b.mark.endContainer,this.editNode).o, - endOffset:b.mark.endOffset}; - } - }catch(e){ - b.mark = null; - } - } - return b; - }, - _beginEditing: function(cmd){ - // summary: - // Called when the user starts typing alphanumeric characters. - // Deals with saving undo; see editActionInterval parameter. - // tags: - // private - if(this._steps.length === 0){ - // You want to use the editor content without post filtering - // to make sure selection restores right for the 'initial' state. - // and undo is called. So not using this.savedContent, as it was 'processed' - // and the line-up for selections may have been altered. - this._steps.push({'text':dijit._editor.getChildrenHtml(this.editNode),'bookmark':this._getBookmark()}); - } - }, - _endEditing: function(ignore_caret){ - // summary: - // Called when the user stops typing alphanumeric characters. - // Deals with saving undo; see editActionInterval parameter. - // tags: - // private - // Avoid filtering to make sure selections restore. - var v = dijit._editor.getChildrenHtml(this.editNode); - - this._undoedSteps=[];//clear undoed steps - this._steps.push({text: v, bookmark: this._getBookmark()}); - }, - onKeyDown: function(e){ - // summary: - // Handler for onkeydown event. - // tags: - // private - - //We need to save selection if the user TAB away from this editor - //no need to call _saveSelection for IE, as that will be taken care of in onBeforeDeactivate - if(!dojo.isIE && !this.iframe && e.keyCode == dojo.keys.TAB && !this.tabIndent){ - this._saveSelection(); - } - if(!this.customUndo){ - this.inherited(arguments); - return; - } - var k = e.keyCode, ks = dojo.keys; - if(e.ctrlKey && !e.altKey){//undo and redo only if the special right Alt + z/y are not pressed #5892 - if(k == 90 || k == 122){ //z - dojo.stopEvent(e); - this.undo(); - return; - }else if(k == 89 || k == 121){ //y - dojo.stopEvent(e); - this.redo(); - return; - } - } - this.inherited(arguments); - - switch(k){ - case ks.ENTER: - case ks.BACKSPACE: - case ks.DELETE: - this.beginEditing(); - break; - case 88: //x - case 86: //v - if(e.ctrlKey && !e.altKey && !e.metaKey){ - this.endEditing();//end current typing step if any - if(e.keyCode == 88){ - this.beginEditing('cut'); - //use timeout to trigger after the cut is complete - setTimeout(dojo.hitch(this, this.endEditing), 1); - }else{ - this.beginEditing('paste'); - //use timeout to trigger after the paste is complete - setTimeout(dojo.hitch(this, this.endEditing), 1); - } - break; - } - //pass through - default: - if(!e.ctrlKey && !e.altKey && !e.metaKey && (e.keyCode<dojo.keys.F1 || e.keyCode>dojo.keys.F15)){ - this.beginEditing(); - break; - } - //pass through - case ks.ALT: - this.endEditing(); - break; - case ks.UP_ARROW: - case ks.DOWN_ARROW: - case ks.LEFT_ARROW: - case ks.RIGHT_ARROW: - case ks.HOME: - case ks.END: - case ks.PAGE_UP: - case ks.PAGE_DOWN: - this.endEditing(true); - break; - //maybe ctrl+backspace/delete, so don't endEditing when ctrl is pressed - case ks.CTRL: - case ks.SHIFT: - case ks.TAB: - break; - } - }, - _onBlur: function(){ - // summary: - // Called from focus manager when focus has moved away from this editor - // tags: - // protected - - //this._saveSelection(); - this.inherited('_onBlur',arguments); - this.endEditing(true); - }, - _saveSelection: function(){ - // summary: - // Save the currently selected text in _savedSelection attribute - // tags: - // private - this._savedSelection=this._getBookmark(); - //console.log('save selection',this._savedSelection,this); - }, - _restoreSelection: function(){ - // summary: - // Re-select the text specified in _savedSelection attribute; - // see _saveSelection(). - // tags: - // private - if(this._savedSelection){ - // Clear off cursor to start, we're deliberately going to a selection. - delete this._cursorToStart; - // only restore the selection if the current range is collapsed - // if not collapsed, then it means the editor does not lose - // selection and there is no need to restore it - if(dojo.withGlobal(this.window,'isCollapsed',dijit)){ - this._moveToBookmark(this._savedSelection); - } - delete this._savedSelection; - } - }, - - onClick: function(){ - // summary: - // Handler for when editor is clicked - // tags: - // protected - this.endEditing(true); - this.inherited(arguments); - }, - - _setDisabledAttr: function(/*Boolean*/ value){ - if(!this.disabled && value){ - // Disable editor: disable all enabled buttons and remember that list - this._buttonEnabledPlugins = dojo.filter(this._plugins, function(p){ - if (p && p.button && !p.button.get("disabled")) { - p.button.set("disabled", true); - return true; - } - return false; - }); - }else if(this.disabled && !value){ - // Enable editor: we only want to enable the buttons that should be - // enabled (for example, the outdent button shouldn't be enabled if the current - // text can't be outdented). - dojo.forEach(this._buttonEnabledPlugins, function(p){ - p.button.attr("disabled", false); - p.updateState && p.updateState(); // just in case something changed, like caret position - }); - } - - this.inherited(arguments); - }, - - _setStateClass: function(){ - this.inherited(arguments); - - // Let theme set the editor's text color based on editor enabled/disabled state. - // We need to jump through hoops because the main document (where the theme CSS is) - // is separate from the iframe's document. - if(this.document && this.document.body){ - dojo.style(this.document.body, "color", dojo.style(this.iframe, "color")); - } - } - } -); - -// Register the "default plugins", ie, the built-in editor commands -dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){ - if(o.plugin){ return; } - var args = o.args, p; - var _p = dijit._editor._Plugin; - var name = args.name; - switch(name){ - case "undo": case "redo": case "cut": case "copy": case "paste": case "insertOrderedList": - case "insertUnorderedList": case "indent": case "outdent": case "justifyCenter": - case "justifyFull": case "justifyLeft": case "justifyRight": case "delete": - case "selectAll": case "removeFormat": case "unlink": - case "insertHorizontalRule": - p = new _p({ command: name }); - break; - - case "bold": case "italic": case "underline": case "strikethrough": - case "subscript": case "superscript": - p = new _p({ buttonClass: dijit.form.ToggleButton, command: name }); - break; - case "|": - p = new _p({ button: new dijit.ToolbarSeparator(), setEditor: function(editor) {this.editor = editor;} }); - } -// console.log('name',name,p); - o.plugin=p; -}); - -} - -if(!dojo._hasResource["dojo.regexp"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.regexp"] = true; -dojo.provide("dojo.regexp"); - -/*===== -dojo.regexp = { - // summary: Regular expressions and Builder resources -}; -=====*/ - -dojo.regexp.escapeString = function(/*String*/str, /*String?*/except){ - // summary: - // Adds escape sequences for special characters in regular expressions - // except: - // a String with special characters to be left unescaped - - return str.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(ch){ - if(except && except.indexOf(ch) != -1){ - return ch; - } - return "\\" + ch; - }); // String -} - -dojo.regexp.buildGroupRE = function(/*Object|Array*/arr, /*Function*/re, /*Boolean?*/nonCapture){ - // summary: - // Builds a regular expression that groups subexpressions - // description: - // A utility function used by some of the RE generators. The - // subexpressions are constructed by the function, re, in the second - // parameter. re builds one subexpression for each elem in the array - // a, in the first parameter. Returns a string for a regular - // expression that groups all the subexpressions. - // arr: - // A single value or an array of values. - // re: - // A function. Takes one parameter and converts it to a regular - // expression. - // nonCapture: - // If true, uses non-capturing match, otherwise matches are retained - // by regular expression. Defaults to false - - // case 1: a is a single value. - if(!(arr instanceof Array)){ - return re(arr); // String - } - - // case 2: a is an array - var b = []; - for(var i = 0; i < arr.length; i++){ - // convert each elem to a RE - b.push(re(arr[i])); - } - - // join the REs as alternatives in a RE group. - return dojo.regexp.group(b.join("|"), nonCapture); // String -} - -dojo.regexp.group = function(/*String*/expression, /*Boolean?*/nonCapture){ - // summary: - // adds group match to expression - // nonCapture: - // If true, uses non-capturing match, otherwise matches are retained - // by regular expression. - return "(" + (nonCapture ? "?:":"") + expression + ")"; // String -} - -} - -if(!dojo._hasResource["dojo.data.util.sorter"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.data.util.sorter"] = true; -dojo.provide("dojo.data.util.sorter"); - -dojo.data.util.sorter.basicComparator = function( /*anything*/ a, - /*anything*/ b){ - // summary: - // Basic comparision function that compares if an item is greater or less than another item - // description: - // returns 1 if a > b, -1 if a < b, 0 if equal. - // 'null' values (null, undefined) are treated as larger values so that they're pushed to the end of the list. - // And compared to each other, null is equivalent to undefined. - - //null is a problematic compare, so if null, we set to undefined. - //Makes the check logic simple, compact, and consistent - //And (null == undefined) === true, so the check later against null - //works for undefined and is less bytes. - var r = -1; - if(a === null){ - a = undefined; - } - if(b === null){ - b = undefined; - } - if(a == b){ - r = 0; - }else if(a > b || a == null){ - r = 1; - } - return r; //int {-1,0,1} -}; - -dojo.data.util.sorter.createSortFunction = function( /* attributes array */sortSpec, - /*dojo.data.core.Read*/ store){ - // summary: - // Helper function to generate the sorting function based off the list of sort attributes. - // description: - // The sort function creation will look for a property on the store called 'comparatorMap'. If it exists - // it will look in the mapping for comparisons function for the attributes. If one is found, it will - // use it instead of the basic comparator, which is typically used for strings, ints, booleans, and dates. - // Returns the sorting function for this particular list of attributes and sorting directions. - // - // sortSpec: array - // A JS object that array that defines out what attribute names to sort on and whether it should be descenting or asending. - // The objects should be formatted as follows: - // { - // attribute: "attributeName-string" || attribute, - // descending: true|false; // Default is false. - // } - // store: object - // The datastore object to look up item values from. - // - var sortFunctions=[]; - - function createSortFunction(attr, dir, comp, s){ - //Passing in comp and s (comparator and store), makes this - //function much faster. - return function(itemA, itemB){ - var a = s.getValue(itemA, attr); - var b = s.getValue(itemB, attr); - return dir * comp(a,b); //int - }; - } - var sortAttribute; - var map = store.comparatorMap; - var bc = dojo.data.util.sorter.basicComparator; - for(var i = 0; i < sortSpec.length; i++){ - sortAttribute = sortSpec[i]; - var attr = sortAttribute.attribute; - if(attr){ - var dir = (sortAttribute.descending) ? -1 : 1; - var comp = bc; - if(map){ - if(typeof attr !== "string" && ("toString" in attr)){ - attr = attr.toString(); - } - comp = map[attr] || bc; - } - sortFunctions.push(createSortFunction(attr, - dir, comp, store)); - } - } - return function(rowA, rowB){ - var i=0; - while(i < sortFunctions.length){ - var ret = sortFunctions[i++](rowA, rowB); - if(ret !== 0){ - return ret;//int - } - } - return 0; //int - }; // Function -}; - -} - -if(!dojo._hasResource["dojo.data.util.simpleFetch"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.data.util.simpleFetch"] = true; -dojo.provide("dojo.data.util.simpleFetch"); - - -dojo.data.util.simpleFetch.fetch = function(/* Object? */ request){ - // summary: - // The simpleFetch mixin is designed to serve as a set of function(s) that can - // be mixed into other datastore implementations to accelerate their development. - // The simpleFetch mixin should work well for any datastore that can respond to a _fetchItems() - // call by returning an array of all the found items that matched the query. The simpleFetch mixin - // is not designed to work for datastores that respond to a fetch() call by incrementally - // loading items, or sequentially loading partial batches of the result - // set. For datastores that mixin simpleFetch, simpleFetch - // implements a fetch method that automatically handles eight of the fetch() - // arguments -- onBegin, onItem, onComplete, onError, start, count, sort and scope - // The class mixing in simpleFetch should not implement fetch(), - // but should instead implement a _fetchItems() method. The _fetchItems() - // method takes three arguments, the keywordArgs object that was passed - // to fetch(), a callback function to be called when the result array is - // available, and an error callback to be called if something goes wrong. - // The _fetchItems() method should ignore any keywordArgs parameters for - // start, count, onBegin, onItem, onComplete, onError, sort, and scope. - // The _fetchItems() method needs to correctly handle any other keywordArgs - // parameters, including the query parameter and any optional parameters - // (such as includeChildren). The _fetchItems() method should create an array of - // result items and pass it to the fetchHandler along with the original request object - // -- or, the _fetchItems() method may, if it wants to, create an new request object - // with other specifics about the request that are specific to the datastore and pass - // that as the request object to the handler. - // - // For more information on this specific function, see dojo.data.api.Read.fetch() - request = request || {}; - if(!request.store){ - request.store = this; - } - var self = this; - - var _errorHandler = function(errorData, requestObject){ - if(requestObject.onError){ - var scope = requestObject.scope || dojo.global; - requestObject.onError.call(scope, errorData, requestObject); - } - }; - - var _fetchHandler = function(items, requestObject){ - var oldAbortFunction = requestObject.abort || null; - var aborted = false; - - var startIndex = requestObject.start?requestObject.start:0; - var endIndex = (requestObject.count && (requestObject.count !== Infinity))?(startIndex + requestObject.count):items.length; - - requestObject.abort = function(){ - aborted = true; - if(oldAbortFunction){ - oldAbortFunction.call(requestObject); - } - }; - - var scope = requestObject.scope || dojo.global; - if(!requestObject.store){ - requestObject.store = self; - } - if(requestObject.onBegin){ - requestObject.onBegin.call(scope, items.length, requestObject); - } - if(requestObject.sort){ - items.sort(dojo.data.util.sorter.createSortFunction(requestObject.sort, self)); - } - if(requestObject.onItem){ - for(var i = startIndex; (i < items.length) && (i < endIndex); ++i){ - var item = items[i]; - if(!aborted){ - requestObject.onItem.call(scope, item, requestObject); - } - } - } - if(requestObject.onComplete && !aborted){ - var subset = null; - if(!requestObject.onItem){ - subset = items.slice(startIndex, endIndex); - } - requestObject.onComplete.call(scope, subset, requestObject); - } - }; - this._fetchItems(request, _fetchHandler, _errorHandler); - return request; // Object -}; - -} - -if(!dojo._hasResource["dojo.data.util.filter"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.data.util.filter"] = true; -dojo.provide("dojo.data.util.filter"); - -dojo.data.util.filter.patternToRegExp = function(/*String*/pattern, /*boolean?*/ ignoreCase){ - // summary: - // Helper function to convert a simple pattern to a regular expression for matching. - // description: - // Returns a regular expression object that conforms to the defined conversion rules. - // For example: - // ca* -> /^ca.*$/ - // *ca* -> /^.*ca.*$/ - // *c\*a* -> /^.*c\*a.*$/ - // *c\*a?* -> /^.*c\*a..*$/ - // and so on. - // - // pattern: string - // A simple matching pattern to convert that follows basic rules: - // * Means match anything, so ca* means match anything starting with ca - // ? Means match single character. So, b?b will match to bob and bab, and so on. - // \ is an escape character. So for example, \* means do not treat * as a match, but literal character *. - // To use a \ as a character in the string, it must be escaped. So in the pattern it should be - // represented by \\ to be treated as an ordinary \ character instead of an escape. - // - // ignoreCase: - // An optional flag to indicate if the pattern matching should be treated as case-sensitive or not when comparing - // By default, it is assumed case sensitive. - - var rxp = "^"; - var c = null; - for(var i = 0; i < pattern.length; i++){ - c = pattern.charAt(i); - switch(c){ - case '\\': - rxp += c; - i++; - rxp += pattern.charAt(i); - break; - case '*': - rxp += ".*"; break; - case '?': - rxp += "."; break; - case '$': - case '^': - case '/': - case '+': - case '.': - case '|': - case '(': - case ')': - case '{': - case '}': - case '[': - case ']': - rxp += "\\"; //fallthrough - default: - rxp += c; - } - } - rxp += "$"; - if(ignoreCase){ - return new RegExp(rxp,"mi"); //RegExp - }else{ - return new RegExp(rxp,"m"); //RegExp - } - -}; - -} - -if(!dojo._hasResource["dijit.form.TextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.TextBox"] = true; -dojo.provide("dijit.form.TextBox"); - - - -dojo.declare( - "dijit.form.TextBox", - dijit.form._FormValueWidget, - { - // summary: - // A base class for textbox form inputs - - // trim: Boolean - // Removes leading and trailing whitespace if true. Default is false. - trim: false, - - // uppercase: Boolean - // Converts all characters to uppercase if true. Default is false. - uppercase: false, - - // lowercase: Boolean - // Converts all characters to lowercase if true. Default is false. - lowercase: false, - - // propercase: Boolean - // Converts the first character of each word to uppercase if true. - propercase: false, - - // maxLength: String - // HTML INPUT tag maxLength declaration. - maxLength: "", - - // selectOnClick: [const] Boolean - // If true, all text will be selected when focused with mouse - selectOnClick: false, - - // placeHolder: String - // Defines a hint to help users fill out the input field (as defined in HTML 5). - // This should only contain plain text (no html markup). - placeHolder: "", - - templateString: dojo.cache("dijit.form", "templates/TextBox.html", "<div class=\"dijit dijitReset dijitInline dijitLeft\" id=\"widget_${id}\" waiRole=\"presentation\"\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" dojoAttachPoint='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n"), - _singleNodeTemplate: '<input class="dijit dijitReset dijitLeft dijitInputField" dojoAttachPoint="textbox,focusNode" autocomplete="off" type="${type}" ${!nameAttrSetting} />', - - _buttonInputDisabled: dojo.isIE ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events - - baseClass: "dijitTextBox", - - attributeMap: dojo.delegate(dijit.form._FormValueWidget.prototype.attributeMap, { - maxLength: "focusNode" - }), - - postMixInProperties: function(){ - var type = this.type.toLowerCase(); - if(this.templateString.toLowerCase() == "input" || ((type == "hidden" || type == "file") && this.templateString == dijit.form.TextBox.prototype.templateString)){ - this.templateString = this._singleNodeTemplate; - } - this.inherited(arguments); - }, - - _setPlaceHolderAttr: function(v){ - this.placeHolder = v; - if(!this._phspan){ - this._attachPoints.push('_phspan'); - /* dijitInputField class gives placeHolder same padding as the input field - * parent node already has dijitInputField class but it doesn't affect this <span> - * since it's position: absolute. - */ - this._phspan = dojo.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"; - } - }, - - _getValueAttr: function(){ - // summary: - // Hook so attr('value') works as we like. - // description: - // For `dijit.form.TextBox` this basically returns the value of the <input>. - // - // For `dijit.form.MappedTextBox` subclasses, which have both - // a "displayed value" and a separate "submit value", - // This treats the "displayed value" as the master value, computing the - // submit value from it via this.parse(). - return this.parse(this.get('displayedValue'), this.constraints); - }, - - _setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){ - // summary: - // Hook so attr('value', ...) works. - // - // description: - // Sets the value of the widget to "value" which can be of - // any type as determined by the widget. - // - // value: - // The visual element value is also set to a corresponding, - // but not necessarily the same, value. - // - // formattedValue: - // If specified, used to set the visual element value, - // otherwise a computed visual value is used. - // - // priorityChange: - // If true, an onChange event is fired immediately instead of - // waiting for the next blur event. - - var filteredValue; - if(value !== undefined){ - // TODO: this is calling filter() on both the display value and the actual value. - // I added a comment to the filter() definition about this, but it should be changed. - filteredValue = this.filter(value); - if(typeof formattedValue != "string"){ - if(filteredValue !== null && ((typeof filteredValue != "number") || !isNaN(filteredValue))){ - formattedValue = this.filter(this.format(filteredValue, this.constraints)); - }else{ formattedValue = ''; } - } - } - if(formattedValue != null && formattedValue != undefined && ((typeof formattedValue) != "number" || !isNaN(formattedValue)) && this.textbox.value != formattedValue){ - this.textbox.value = formattedValue; - } - - this._updatePlaceHolder(); - - this.inherited(arguments, [filteredValue, priorityChange]); - }, - - // displayedValue: String - // For subclasses like ComboBox where the displayed value - // (ex: Kentucky) and the serialized value (ex: KY) are different, - // this represents the displayed value. - // - // Setting 'displayedValue' through attr('displayedValue', ...) - // updates 'value', and vice-versa. Otherwise 'value' is updated - // from 'displayedValue' periodically, like onBlur etc. - // - // TODO: move declaration to MappedTextBox? - // Problem is that ComboBox references displayedValue, - // for benefit of FilteringSelect. - displayedValue: "", - - getDisplayedValue: function(){ - // summary: - // Deprecated. Use set('displayedValue') instead. - // tags: - // deprecated - dojo.deprecated(this.declaredClass+"::getDisplayedValue() is deprecated. Use set('displayedValue') instead.", "", "2.0"); - return this.get('displayedValue'); - }, - - _getDisplayedValueAttr: function(){ - // summary: - // Hook so attr('displayedValue') works. - // description: - // Returns the displayed value (what the user sees on the screen), - // after filtering (ie, trimming spaces etc.). - // - // For some subclasses of TextBox (like ComboBox), the displayed value - // is different from the serialized value that's actually - // sent to the server (see dijit.form.ValidationTextBox.serialize) - - return this.filter(this.textbox.value); - }, - - setDisplayedValue: function(/*String*/value){ - // summary: - // Deprecated. Use set('displayedValue', ...) instead. - // tags: - // deprecated - dojo.deprecated(this.declaredClass+"::setDisplayedValue() is deprecated. Use set('displayedValue', ...) instead.", "", "2.0"); - this.set('displayedValue', value); - }, - - _setDisplayedValueAttr: function(/*String*/value){ - // summary: - // Hook so attr('displayedValue', ...) works. - // description: - // Sets the value of the visual element to the string "value". - // The widget value is also set to a corresponding, - // but not necessarily the same, value. - - if(value === null || value === undefined){ value = '' } - else if(typeof value != "string"){ value = String(value) } - this.textbox.value = value; - this._setValueAttr(this.get('value'), undefined, value); - }, - - format: function(/* String */ value, /* Object */ constraints){ - // summary: - // Replacable function to convert a value to a properly formatted string. - // tags: - // protected extension - return ((value == null || value == undefined) ? "" : (value.toString ? value.toString() : value)); - }, - - parse: function(/* String */ value, /* Object */ constraints){ - // summary: - // Replacable function to convert a formatted string to a value - // tags: - // protected extension - - return value; // String - }, - - _refreshState: function(){ - // summary: - // After the user types some characters, etc., this method is - // called to check the field for validity etc. The base method - // in `dijit.form.TextBox` does nothing, but subclasses override. - // tags: - // protected - }, - - _onInput: function(e){ - if(e && e.type && /key/i.test(e.type) && e.keyCode){ - switch(e.keyCode){ - case dojo.keys.SHIFT: - case dojo.keys.ALT: - case dojo.keys.CTRL: - case dojo.keys.TAB: - return; - } - } - if(this.intermediateChanges){ - var _this = this; - // the setTimeout allows the key to post to the widget input box - setTimeout(function(){ _this._handleOnChange(_this.get('value'), false); }, 0); - } - this._refreshState(); - }, - - postCreate: function(){ - // setting the value here is needed since value="" in the template causes "undefined" - // and setting in the DOM (instead of the JS object) helps with form reset actions - if(dojo.isIE){ // IE INPUT tag fontFamily has to be set directly using STYLE - var s = dojo.getComputedStyle(this.domNode); - 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; - } - } - } - } - } - this.textbox.setAttribute("value", this.textbox.value); // DOM and JS values shuld be the same - this.inherited(arguments); - if(dojo.isMoz || dojo.isOpera){ - this.connect(this.textbox, "oninput", this._onInput); - }else{ - this.connect(this.textbox, "onkeydown", this._onInput); - this.connect(this.textbox, "onkeyup", this._onInput); - this.connect(this.textbox, "onpaste", this._onInput); - this.connect(this.textbox, "oncut", this._onInput); - } - }, - - _blankValue: '', // if the textbox is blank, what value should be reported - filter: function(val){ - // summary: - // Auto-corrections (such as trimming) that are applied to textbox - // value on blur or form submit. - // description: - // For MappedTextBox subclasses, this is called twice - // - once with the display value - // - once the value as set/returned by attr('value', ...) - // and attr('value'), ex: a Number for NumberTextBox. - // - // In the latter case it does corrections like converting null to NaN. In - // the former case the NumberTextBox.filter() method calls this.inherited() - // to execute standard trimming code in TextBox.filter(). - // - // TODO: break this into two methods in 2.0 - // - // tags: - // protected extension - if(val === null){ return this._blankValue; } - if(typeof val != "string"){ return val; } - if(this.trim){ - val = dojo.trim(val); - } - if(this.uppercase){ - val = val.toUpperCase(); - } - if(this.lowercase){ - val = val.toLowerCase(); - } - if(this.propercase){ - val = val.replace(/[^\s]+/g, function(word){ - return word.substring(0,1).toUpperCase() + word.substring(1); - }); - } - return val; - }, - - _setBlurValue: function(){ - this._setValueAttr(this.get('value'), true); - }, - - _onBlur: function(e){ - if(this.disabled){ return; } - this._setBlurValue(); - this.inherited(arguments); - - if(this._selectOnClickHandle){ - this.disconnect(this._selectOnClickHandle); - } - if(this.selectOnClick && dojo.isMoz){ - this.textbox.selectionStart = this.textbox.selectionEnd = undefined; // clear selection so that the next mouse click doesn't reselect - } - - this._updatePlaceHolder(); - }, - - _onFocus: function(/*String*/ by){ - if(this.disabled || this.readOnly){ return; } - - // Select all text on focus via click if nothing already selected. - // Since mouse-up will clear the selection need to defer selection until after mouse-up. - // Don't do anything on focus by tabbing into the widget since there's no associated mouse-up event. - if(this.selectOnClick && by == "mouse"){ - this._selectOnClickHandle = this.connect(this.domNode, "onmouseup", function(){ - // Only select all text on first click; otherwise users would have no way to clear - // the selection. - this.disconnect(this._selectOnClickHandle); - - // Check if the user selected some text manually (mouse-down, mouse-move, mouse-up) - // and if not, then select all the text - var textIsNotSelected; - if(dojo.isIE){ - var range = dojo.doc.selection.createRange(); - var parent = range.parentElement(); - textIsNotSelected = parent == this.textbox && range.text.length == 0; - }else{ - textIsNotSelected = this.textbox.selectionStart == this.textbox.selectionEnd; - } - if(textIsNotSelected){ - dijit.selectInputText(this.textbox); - } - }); - } - - this._updatePlaceHolder(); - - this._refreshState(); - this.inherited(arguments); - }, - - reset: function(){ - // Overrides dijit._FormWidget.reset(). - // Additionally resets the displayed textbox value to '' - this.textbox.value = ''; - this.inherited(arguments); - } - } -); - -dijit.selectInputText = function(/*DomNode*/element, /*Number?*/ start, /*Number?*/ stop){ - // summary: - // Select text in the input element argument, from start (default 0), to stop (default end). - - // TODO: use functions in _editor/selection.js? - var _window = dojo.global; - var _document = dojo.doc; - element = dojo.byId(element); - if(isNaN(start)){ start = 0; } - if(isNaN(stop)){ stop = element.value ? element.value.length : 0; } - dijit.focus(element); - if(_document["selection"] && dojo.body()["createTextRange"]){ // IE - if(element.createTextRange){ - var range = element.createTextRange(); - with(range){ - collapse(true); - moveStart("character", -99999); // move to 0 - moveStart("character", start); // delta from 0 is the correct position - moveEnd("character", stop-start); - select(); - } - } - }else if(_window["getSelection"]){ - if(element.setSelectionRange){ - element.setSelectionRange(start, stop); - } - } -}; - -} - -if(!dojo._hasResource["dijit.Tooltip"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.Tooltip"] = true; -dojo.provide("dijit.Tooltip"); - - - - -dojo.declare( - "dijit._MasterTooltip", - [dijit._Widget, dijit._Templated], - { - // summary: - // Internal widget that holds the actual tooltip markup, - // which occurs once per page. - // Called by Tooltip widgets which are just containers to hold - // the markup - // tags: - // protected - - // duration: Integer - // Milliseconds to fade in/fade out - duration: dijit.defaultDuration, - - templateString: dojo.cache("dijit", "templates/Tooltip.html", "<div class=\"dijitTooltip dijitTooltipLeft\" id=\"dojoTooltip\">\n\t<div class=\"dijitTooltipContainer dijitTooltipContents\" dojoAttachPoint=\"containerNode\" waiRole='alert'></div>\n\t<div class=\"dijitTooltipConnector\"></div>\n</div>\n"), - - postCreate: function(){ - dojo.body().appendChild(this.domNode); - - this.bgIframe = new dijit.BackgroundIframe(this.domNode); - - // Setup fade-in and fade-out functions. - this.fadeIn = dojo.fadeIn({ node: this.domNode, duration: this.duration, onEnd: dojo.hitch(this, "_onShow") }); - this.fadeOut = dojo.fadeOut({ node: this.domNode, duration: this.duration, onEnd: dojo.hitch(this, "_onHide") }); - - }, - - show: function(/*String*/ innerHTML, /*DomNode*/ aroundNode, /*String[]?*/ position, /*Boolean*/ rtl){ - // summary: - // Display tooltip w/specified contents to right of specified node - // (To left if there's no space on the right, or if rtl == true) - - if(this.aroundNode && this.aroundNode === aroundNode){ - return; - } - - if(this.fadeOut.status() == "playing"){ - // previous tooltip is being hidden; wait until the hide completes then show new one - this._onDeck=arguments; - return; - } - this.containerNode.innerHTML=innerHTML; - - var pos = dijit.placeOnScreenAroundElement(this.domNode, aroundNode, dijit.getPopupAroundAlignment((position && position.length) ? position : dijit.Tooltip.defaultPosition, !rtl), dojo.hitch(this, "orient")); - - // show it - dojo.style(this.domNode, "opacity", 0); - this.fadeIn.play(); - this.isShowingNow = true; - this.aroundNode = aroundNode; - }, - - orient: function(/* DomNode */ node, /* String */ aroundCorner, /* String */ tooltipCorner){ - // summary: - // Private function to set CSS for tooltip node based on which position it's in. - // This is called by the dijit popup code. - // tags: - // protected - - node.className = "dijitTooltip " + - { - "BL-TL": "dijitTooltipBelow dijitTooltipABLeft", - "TL-BL": "dijitTooltipAbove dijitTooltipABLeft", - "BR-TR": "dijitTooltipBelow dijitTooltipABRight", - "TR-BR": "dijitTooltipAbove dijitTooltipABRight", - "BR-BL": "dijitTooltipRight", - "BL-BR": "dijitTooltipLeft" - }[aroundCorner + "-" + tooltipCorner]; - }, - - _onShow: function(){ - // summary: - // Called at end of fade-in operation - // tags: - // protected - if(dojo.isIE){ - // the arrow won't show up on a node w/an opacity filter - this.domNode.style.filter=""; - } - }, - - hide: function(aroundNode){ - // summary: - // Hide the tooltip - if(this._onDeck && this._onDeck[1] == aroundNode){ - // this hide request is for a show() that hasn't even started yet; - // just cancel the pending show() - this._onDeck=null; - }else if(this.aroundNode === aroundNode){ - // this hide request is for the currently displayed tooltip - this.fadeIn.stop(); - this.isShowingNow = false; - this.aroundNode = null; - this.fadeOut.play(); - }else{ - // just ignore the call, it's for a tooltip that has already been erased - } - }, - - _onHide: function(){ - // summary: - // Called at end of fade-out operation - // tags: - // protected - - this.domNode.style.cssText=""; // to position offscreen again - this.containerNode.innerHTML=""; - if(this._onDeck){ - // a show request has been queued up; do it now - this.show.apply(this, this._onDeck); - this._onDeck=null; - } - } - - } -); - -dijit.showTooltip = function(/*String*/ innerHTML, /*DomNode*/ aroundNode, /*String[]?*/ position, /*Boolean*/ rtl){ - // summary: - // Display tooltip w/specified contents in specified position. - // See description of dijit.Tooltip.defaultPosition for details on position parameter. - // If position is not specified then dijit.Tooltip.defaultPosition is used. - if(!dijit._masterTT){ dijit._masterTT = new dijit._MasterTooltip(); } - return dijit._masterTT.show(innerHTML, aroundNode, position, rtl); -}; - -dijit.hideTooltip = function(aroundNode){ - // summary: - // Hide the tooltip - if(!dijit._masterTT){ dijit._masterTT = new dijit._MasterTooltip(); } - return dijit._masterTT.hide(aroundNode); -}; - -dojo.declare( - "dijit.Tooltip", - dijit._Widget, - { - // summary: - // Pops up a tooltip (a help message) when you hover over a node. - - // label: String - // Text to display in the tooltip. - // Specified as innerHTML when creating the widget from markup. - label: "", - - // showDelay: Integer - // Number of milliseconds to wait after hovering over/focusing on the object, before - // the tooltip is displayed. - showDelay: 400, - - // connectId: [const] String[] - // Id's of domNodes to attach the tooltip to. - // When user hovers over any of the specified dom nodes, the tooltip will appear. - // - // Note: Currently connectId can only be specified on initialization, it cannot - // be changed via attr('connectId', ...) - // - // Note: in 2.0 this will be renamed to connectIds for less confusion. - connectId: [], - - // position: String[] - // See description of `dijit.Tooltip.defaultPosition` for details on position parameter. - position: [], - - constructor: function(){ - // Map id's of nodes I'm connected to to a list of the this.connect() handles - this._nodeConnectionsById = {}; - }, - - _setConnectIdAttr: function(newIds){ - for(var oldId in this._nodeConnectionsById){ - this.removeTarget(oldId); - } - dojo.forEach(dojo.isArrayLike(newIds) ? newIds : [newIds], this.addTarget, this); - }, - - _getConnectIdAttr: function(){ - var ary = []; - for(var id in this._nodeConnectionsById){ - ary.push(id); - } - return ary; - }, - - addTarget: function(/*DOMNODE || String*/ id){ - // summary: - // Attach tooltip to specified node, if it's not already connected - var node = dojo.byId(id); - if(!node){ return; } - if(node.id in this._nodeConnectionsById){ return; }//Already connected - - this._nodeConnectionsById[node.id] = [ - this.connect(node, "onmouseenter", "_onTargetMouseEnter"), - this.connect(node, "onmouseleave", "_onTargetMouseLeave"), - this.connect(node, "onfocus", "_onTargetFocus"), - this.connect(node, "onblur", "_onTargetBlur") - ]; - }, - - removeTarget: function(/*DOMNODE || String*/ node){ - // summary: - // Detach tooltip from specified node - - // map from DOMNode back to plain id string - var id = node.id || node; - - if(id in this._nodeConnectionsById){ - dojo.forEach(this._nodeConnectionsById[id], this.disconnect, this); - delete this._nodeConnectionsById[id]; - } - }, - - postCreate: function(){ - dojo.addClass(this.domNode,"dijitTooltipData"); - }, - - startup: function(){ - this.inherited(arguments); - - // If this tooltip was created in a template, or for some other reason the specified connectId[s] - // didn't exist during the widget's initialization, then connect now. - var ids = this.connectId; - dojo.forEach(dojo.isArrayLike(ids) ? ids : [ids], this.addTarget, this); - }, - - _onTargetMouseEnter: function(/*Event*/ e){ - // summary: - // Handler for mouseenter event on the target node - // tags: - // private - this._onHover(e); - }, - - _onTargetMouseLeave: function(/*Event*/ e){ - // summary: - // Handler for mouseleave event on the target node - // tags: - // private - this._onUnHover(e); - }, - - _onTargetFocus: function(/*Event*/ e){ - // summary: - // Handler for focus event on the target node - // tags: - // private - - this._focus = true; - this._onHover(e); - }, - - _onTargetBlur: function(/*Event*/ e){ - // summary: - // Handler for blur event on the target node - // tags: - // private - - this._focus = false; - this._onUnHover(e); - }, - - _onHover: function(/*Event*/ e){ - // summary: - // Despite the name of this method, it actually handles both hover and focus - // events on the target node, setting a timer to show the tooltip. - // tags: - // private - if(!this._showTimer){ - var target = e.target; - this._showTimer = setTimeout(dojo.hitch(this, function(){this.open(target)}), this.showDelay); - } - }, - - _onUnHover: function(/*Event*/ e){ - // summary: - // Despite the name of this method, it actually handles both mouseleave and blur - // events on the target node, hiding the tooltip. - // tags: - // private - - // keep a tooltip open if the associated element still has focus (even though the - // mouse moved away) - if(this._focus){ return; } - - if(this._showTimer){ - clearTimeout(this._showTimer); - delete this._showTimer; - } - this.close(); - }, - - open: function(/*DomNode*/ target){ - // summary: - // Display the tooltip; usually not called directly. - // tags: - // private - - if(this._showTimer){ - clearTimeout(this._showTimer); - delete this._showTimer; - } - dijit.showTooltip(this.label || this.domNode.innerHTML, target, this.position, !this.isLeftToRight()); - - this._connectNode = target; - this.onShow(target, this.position); - }, - - close: function(){ - // summary: - // Hide the tooltip or cancel timer for show of tooltip - // tags: - // private - - if(this._connectNode){ - // if tooltip is currently shown - dijit.hideTooltip(this._connectNode); - delete this._connectNode; - this.onHide(); - } - if(this._showTimer){ - // if tooltip is scheduled to be shown (after a brief delay) - clearTimeout(this._showTimer); - delete this._showTimer; - } - }, - - onShow: function(target, position){ - // summary: - // Called when the tooltip is shown - // tags: - // callback - }, - - onHide: function(){ - // summary: - // Called when the tooltip is hidden - // tags: - // callback - }, - - uninitialize: function(){ - this.close(); - this.inherited(arguments); - } - } -); - -// dijit.Tooltip.defaultPosition: String[] -// This variable controls the position of tooltips, if the position is not specified to -// the Tooltip widget or *TextBox widget itself. It's an array of strings with the following values: -// -// * before: places tooltip to the left of the target node/widget, or to the right in -// the case of RTL scripts like Hebrew and Arabic -// * after: places tooltip to the right of the target node/widget, or to the left in -// the case of RTL scripts like Hebrew and Arabic -// * above: tooltip goes above target node -// * below: tooltip goes below target node -// -// The list is positions is tried, in order, until a position is found where the tooltip fits -// within the viewport. -// -// Be careful setting this parameter. A value of "above" may work fine until the user scrolls -// the screen so that there's no room above the target node. Nodes with drop downs, like -// DropDownButton or FilteringSelect, are especially problematic, in that you need to be sure -// that the drop down and tooltip don't overlap, even when the viewport is scrolled so that there -// is only room below (or above) the target node, but not both. -dijit.Tooltip.defaultPosition = ["after", "before"]; - -} - -if(!dojo._hasResource["dijit.form.ValidationTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.ValidationTextBox"] = true; -dojo.provide("dijit.form.ValidationTextBox"); - - - - - - - - -/*===== - dijit.form.ValidationTextBox.__Constraints = function(){ - // locale: String - // locale used for validation, picks up value from this widget's lang attribute - // _flags_: anything - // various flags passed to regExpGen function - this.locale = ""; - this._flags_ = ""; - } -=====*/ - -dojo.declare( - "dijit.form.ValidationTextBox", - dijit.form.TextBox, - { - // summary: - // Base class for textbox widgets with the ability to validate content of various types and provide user feedback. - // tags: - // protected - - templateString: dojo.cache("dijit.form", "templates/ValidationTextBox.html", "<div class=\"dijit dijitReset dijitInlineTable dijitLeft\"\n\tid=\"widget_${id}\" waiRole=\"presentation\"\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"Χ \" type=\"text\" tabIndex=\"-1\" readOnly waiRole=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" dojoAttachPoint='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n"), - baseClass: "dijitTextBox dijitValidationTextBox", - - // required: Boolean - // User is required to enter data into this field. - required: false, - - // promptMessage: String - // If defined, display this hint string immediately on focus to the textbox, if empty. - // Think of this like a tooltip that tells the user what to do, not an error message - // that tells the user what they've done wrong. - // - // Message disappears when user starts typing. - promptMessage: "", - - // invalidMessage: String - // The message to display if value is invalid. - // The translated string value is read from the message file by default. - // Set to "" to use the promptMessage instead. - invalidMessage: "$_unset_$", - - // missingMessage: String - // The message to display if value is empty and the field is required. - // The translated string value is read from the message file by default. - // Set to "" to use the invalidMessage instead. - missingMessage: "$_unset_$", - - // constraints: dijit.form.ValidationTextBox.__Constraints - // user-defined object needed to pass parameters to the validator functions - constraints: {}, - - // regExp: [extension protected] String - // regular expression string used to validate the input - // Do not specify both regExp and regExpGen - regExp: ".*", - - regExpGen: function(/*dijit.form.ValidationTextBox.__Constraints*/constraints){ - // summary: - // Overridable function used to generate regExp when dependent on constraints. - // Do not specify both regExp and regExpGen. - // tags: - // extension protected - return this.regExp; // String - }, - - // state: [readonly] String - // Shows current state (ie, validation result) of input (Normal, Warning, or Error) - state: "", - - // tooltipPosition: String[] - // See description of `dijit.Tooltip.defaultPosition` for details on this parameter. - tooltipPosition: [], - - _setValueAttr: function(){ - // summary: - // Hook so attr('value', ...) works. - this.inherited(arguments); - this.validate(this._focused); - }, - - validator: function(/*anything*/value, /*dijit.form.ValidationTextBox.__Constraints*/constraints){ - // summary: - // Overridable function used to validate the text input against the regular expression. - // tags: - // protected - return (new RegExp("^(?:" + this.regExpGen(constraints) + ")"+(this.required?"":"?")+"$")).test(value) && - (!this.required || !this._isEmpty(value)) && - (this._isEmpty(value) || this.parse(value, constraints) !== undefined); // Boolean - }, - - _isValidSubset: function(){ - // summary: - // Returns true if the value is either already valid or could be made valid by appending characters. - // This is used for validation while the user [may be] still typing. - return this.textbox.value.search(this._partialre) == 0; - }, - - isValid: function(/*Boolean*/ isFocused){ - // summary: - // Tests if value is valid. - // Can override with your own routine in a subclass. - // tags: - // protected - return this.validator(this.textbox.value, this.constraints); - }, - - _isEmpty: function(value){ - // summary: - // Checks for whitespace - return /^\s*$/.test(value); // Boolean - }, - - getErrorMessage: function(/*Boolean*/ isFocused){ - // summary: - // Return an error message to show if appropriate - // tags: - // protected - return (this.required && this._isEmpty(this.textbox.value)) ? this.missingMessage : this.invalidMessage; // String - }, - - getPromptMessage: function(/*Boolean*/ isFocused){ - // summary: - // Return a hint message to show when widget is first focused - // tags: - // protected - return this.promptMessage; // String - }, - - _maskValidSubsetError: true, - validate: function(/*Boolean*/ isFocused){ - // summary: - // Called by oninit, onblur, and onkeypress. - // description: - // Show missing or invalid messages if appropriate, and highlight textbox field. - // tags: - // protected - var message = ""; - var isValid = this.disabled || this.isValid(isFocused); - if(isValid){ this._maskValidSubsetError = true; } - var isEmpty = this._isEmpty(this.textbox.value); - var isValidSubset = !isValid && !isEmpty && isFocused && this._isValidSubset(); - this.state = ((isValid || ((!this._hasBeenBlurred || isFocused) && isEmpty) || isValidSubset) && this._maskValidSubsetError) ? "" : "Error"; - if(this.state == "Error"){ this._maskValidSubsetError = isFocused; } // we want the error to show up afer a blur and refocus - this._setStateClass(); - dijit.setWaiState(this.focusNode, "invalid", isValid ? "false" : "true"); - if(isFocused){ - if(this.state == "Error"){ - message = this.getErrorMessage(true); - }else{ - message = this.getPromptMessage(true); // show the prompt whever there's no error - } - this._maskValidSubsetError = true; // since we're focused, always mask warnings - } - this.displayMessage(message); - return isValid; - }, - - // _message: String - // Currently displayed message - _message: "", - - displayMessage: function(/*String*/ message){ - // summary: - // Overridable method to display validation errors/hints. - // By default uses a tooltip. - // tags: - // extension - if(this._message == message){ return; } - this._message = message; - dijit.hideTooltip(this.domNode); - if(message){ - dijit.showTooltip(message, this.domNode, this.tooltipPosition, !this.isLeftToRight()); - } - }, - - _refreshState: function(){ - // Overrides TextBox._refreshState() - this.validate(this._focused); - this.inherited(arguments); - }, - - //////////// INITIALIZATION METHODS /////////////////////////////////////// - - constructor: function(){ - this.constraints = {}; - }, - - _setConstraintsAttr: function(/* Object */ constraints){ - if(!constraints.locale && this.lang){ - constraints.locale = this.lang; - } - this.constraints = constraints; - this._computePartialRE(); - }, - - _computePartialRE: function(){ - var p = this.regExpGen(this.constraints); - this.regExp = p; - var partialre = ""; - // parse the regexp and produce a new regexp that matches valid subsets - // if the regexp is .* then there's no use in matching subsets since everything is valid - if(p != ".*"){ this.regExp.replace(/\\.|\[\]|\[.*?[^\\]{1}\]|\{.*?\}|\(\?[=:!]|./g, - function (re){ - switch(re.charAt(0)){ - case '{': - case '+': - case '?': - case '*': - case '^': - case '$': - case '|': - case '(': - partialre += re; - break; - case ")": - partialre += "|$)"; - break; - default: - partialre += "(?:"+re+"|$)"; - break; - } - } - );} - try{ // this is needed for now since the above regexp parsing needs more test verification - "".search(partialre); - }catch(e){ // should never be here unless the original RE is bad or the parsing is bad - partialre = this.regExp; - console.warn('RegExp error in ' + this.declaredClass + ': ' + this.regExp); - } // should never be here unless the original RE is bad or the parsing is bad - this._partialre = "^(?:" + partialre + ")$"; - }, - - postMixInProperties: function(){ - this.inherited(arguments); - this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang); - if(this.invalidMessage == "$_unset_$"){ this.invalidMessage = this.messages.invalidMessage; } - if(!this.invalidMessage){ this.invalidMessage = this.promptMessage; } - if(this.missingMessage == "$_unset_$"){ this.missingMessage = this.messages.missingMessage; } - if(!this.missingMessage){ this.missingMessage = this.invalidMessage; } - this._setConstraintsAttr(this.constraints); // this needs to happen now (and later) due to codependency on _set*Attr calls attachPoints - }, - - _setDisabledAttr: function(/*Boolean*/ value){ - this.inherited(arguments); // call FormValueWidget._setDisabledAttr() - this._refreshState(); - }, - - _setRequiredAttr: function(/*Boolean*/ value){ - this.required = value; - dijit.setWaiState(this.focusNode, "required", value); - this._refreshState(); - }, - - reset:function(){ - // Overrides dijit.form.TextBox.reset() by also - // hiding errors about partial matches - this._maskValidSubsetError = true; - this.inherited(arguments); - }, - - _onBlur: function(){ - this.displayMessage(''); - this.inherited(arguments); - } - } -); - -dojo.declare( - "dijit.form.MappedTextBox", - dijit.form.ValidationTextBox, - { - // summary: - // A dijit.form.ValidationTextBox subclass which provides a base class for widgets that have - // a visible formatted display value, and a serializable - // value in a hidden input field which is actually sent to the server. - // description: - // The visible display may - // be locale-dependent and interactive. The value sent to the server is stored in a hidden - // input field which uses the `name` attribute declared by the original widget. That value sent - // to the server is defined by the dijit.form.MappedTextBox.serialize method and is typically - // locale-neutral. - // tags: - // protected - - postMixInProperties: function(){ - this.inherited(arguments); - - // we want the name attribute to go to the hidden <input>, not the displayed <input>, - // so override _FormWidget.postMixInProperties() setting of nameAttrSetting - this.nameAttrSetting = ""; - }, - - serialize: function(/*anything*/val, /*Object?*/options){ - // summary: - // Overridable function used to convert the attr('value') result to a canonical - // (non-localized) string. For example, will print dates in ISO format, and - // numbers the same way as they are represented in javascript. - // tags: - // protected extension - return val.toString ? val.toString() : ""; // String - }, - - toString: function(){ - // summary: - // Returns widget as a printable string using the widget's value - // tags: - // protected - var val = this.filter(this.get('value')); // call filter in case value is nonstring and filter has been customized - return val != null ? (typeof val == "string" ? val : this.serialize(val, this.constraints)) : ""; // String - }, - - validate: function(){ - // Overrides `dijit.form.TextBox.validate` - this.valueNode.value = this.toString(); - return this.inherited(arguments); - }, - - buildRendering: function(){ - // Overrides `dijit._Templated.buildRendering` - - this.inherited(arguments); - - // Create a hidden <input> node with the serialized value used for submit - // (as opposed to the displayed value). - // Passing in name as markup rather than calling dojo.create() with an attrs argument - // to make dojo.query(input[name=...]) work on IE. (see #8660) - this.valueNode = dojo.place("<input type='hidden'" + (this.name ? " name='" + this.name + "'" : "") + ">", this.textbox, "after"); - }, - - reset:function(){ - // Overrides `dijit.form.ValidationTextBox.reset` to - // reset the hidden textbox value to '' - this.valueNode.value = ''; - this.inherited(arguments); - } - } -); - -/*===== - dijit.form.RangeBoundTextBox.__Constraints = function(){ - // min: Number - // Minimum signed value. Default is -Infinity - // max: Number - // Maximum signed value. Default is +Infinity - this.min = min; - this.max = max; - } -=====*/ - -dojo.declare( - "dijit.form.RangeBoundTextBox", - dijit.form.MappedTextBox, - { - // summary: - // Base class for textbox form widgets which defines a range of valid values. - - // rangeMessage: String - // The message to display if value is out-of-range - rangeMessage: "", - - /*===== - // constraints: dijit.form.RangeBoundTextBox.__Constraints - constraints: {}, - ======*/ - - rangeCheck: function(/*Number*/ primitive, /*dijit.form.RangeBoundTextBox.__Constraints*/ constraints){ - // summary: - // Overridable function used to validate the range of the numeric input value. - // tags: - // protected - return ("min" in constraints? (this.compare(primitive,constraints.min) >= 0) : true) && - ("max" in constraints? (this.compare(primitive,constraints.max) <= 0) : true); // Boolean - }, - - isInRange: function(/*Boolean*/ isFocused){ - // summary: - // Tests if the value is in the min/max range specified in constraints - // tags: - // protected - return this.rangeCheck(this.get('value'), this.constraints); - }, - - _isDefinitelyOutOfRange: function(){ - // summary: - // Returns true if the value is out of range and will remain - // out of range even if the user types more characters - var val = this.get('value'); - var isTooLittle = false; - var isTooMuch = false; - if("min" in this.constraints){ - var min = this.constraints.min; - min = this.compare(val, ((typeof min == "number") && min >= 0 && val !=0) ? 0 : min); - isTooLittle = (typeof min == "number") && min < 0; - } - if("max" in this.constraints){ - var max = this.constraints.max; - max = this.compare(val, ((typeof max != "number") || max > 0) ? max : 0); - isTooMuch = (typeof max == "number") && max > 0; - } - return isTooLittle || isTooMuch; - }, - - _isValidSubset: function(){ - // summary: - // Overrides `dijit.form.ValidationTextBox._isValidSubset`. - // Returns true if the input is syntactically valid, and either within - // range or could be made in range by more typing. - return this.inherited(arguments) && !this._isDefinitelyOutOfRange(); - }, - - isValid: function(/*Boolean*/ isFocused){ - // Overrides dijit.form.ValidationTextBox.isValid to check that the value is also in range. - return this.inherited(arguments) && - ((this._isEmpty(this.textbox.value) && !this.required) || this.isInRange(isFocused)); // Boolean - }, - - getErrorMessage: function(/*Boolean*/ isFocused){ - // Overrides dijit.form.ValidationTextBox.getErrorMessage to print "out of range" message if appropriate - var v = this.get('value'); - if(v !== null && v !== '' && v !== undefined && (typeof v != "number" || !isNaN(v)) && !this.isInRange(isFocused)){ // don't check isInRange w/o a real value - return this.rangeMessage; // String - } - return this.inherited(arguments); - }, - - postMixInProperties: function(){ - this.inherited(arguments); - if(!this.rangeMessage){ - this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang); - this.rangeMessage = this.messages.rangeMessage; - } - }, - - _setConstraintsAttr: function(/* Object */ constraints){ - this.inherited(arguments); - if(this.focusNode){ // not set when called from postMixInProperties - if(this.constraints.min !== undefined){ - dijit.setWaiState(this.focusNode, "valuemin", this.constraints.min); - }else{ - dijit.removeWaiState(this.focusNode, "valuemin"); - } - if(this.constraints.max !== undefined){ - dijit.setWaiState(this.focusNode, "valuemax", this.constraints.max); - }else{ - dijit.removeWaiState(this.focusNode, "valuemax"); - } - } - }, - - _setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange){ - // summary: - // Hook so attr('value', ...) works. - - dijit.setWaiState(this.focusNode, "valuenow", value); - this.inherited(arguments); - } - } -); - -} - -if(!dojo._hasResource["dijit.form.ComboBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.ComboBox"] = true; -dojo.provide("dijit.form.ComboBox"); - - - - - - - - - - - - -dojo.declare( - "dijit.form.ComboBoxMixin", - null, - { - // summary: - // Implements the base functionality for `dijit.form.ComboBox`/`dijit.form.FilteringSelect` - // description: - // All widgets that mix in dijit.form.ComboBoxMixin must extend `dijit.form._FormValueWidget`. - // tags: - // protected - - // item: Object - // This is the item returned by the dojo.data.store implementation that - // provides the data for this ComboBox, it's the currently selected item. - item: null, - - // pageSize: Integer - // Argument to data provider. - // Specifies number of search results per page (before hitting "next" button) - pageSize: Infinity, - - // store: Object - // Reference to data provider object used by this ComboBox - store: null, - - // fetchProperties: Object - // Mixin to the dojo.data store's fetch. - // For example, to set the sort order of the ComboBox menu, pass: - // | { sort: {attribute:"name",descending: true} } - // To override the default queryOptions so that deep=false, do: - // | { queryOptions: {ignoreCase: true, deep: false} } - fetchProperties:{}, - - // query: Object - // A query that can be passed to 'store' to initially filter the items, - // before doing further filtering based on `searchAttr` and the key. - // Any reference to the `searchAttr` is ignored. - query: {}, - - // autoComplete: Boolean - // If user types in a partial string, and then tab out of the `<input>` box, - // automatically copy the first entry displayed in the drop down list to - // the `<input>` field - autoComplete: true, - - // highlightMatch: String - // One of: "first", "all" or "none". - // - // If the ComboBox/FilteringSelect opens with the search results and the searched - // string can be found, it will be highlighted. If set to "all" - // then will probably want to change `queryExpr` parameter to '*${0}*' - // - // Highlighting is only performed when `labelType` is "text", so as to not - // interfere with any HTML markup an HTML label might contain. - highlightMatch: "first", - - // searchDelay: Integer - // Delay in milliseconds between when user types something and we start - // searching based on that value - searchDelay: 100, - - // searchAttr: String - // Search for items in the data store where this attribute (in the item) - // matches what the user typed - searchAttr: "name", - - // labelAttr: String? - // The entries in the drop down list come from this attribute in the - // dojo.data items. - // If not specified, the searchAttr attribute is used instead. - labelAttr: "", - - // labelType: String - // Specifies how to interpret the labelAttr in the data store items. - // Can be "html" or "text". - labelType: "text", - - // queryExpr: String - // This specifies what query ComboBox/FilteringSelect sends to the data store, - // based on what the user has typed. Changing this expression will modify - // whether the drop down shows only exact matches, a "starting with" match, - // etc. Use it in conjunction with highlightMatch. - // dojo.data query expression pattern. - // `${0}` will be substituted for the user text. - // `*` is used for wildcards. - // `${0}*` means "starts with", `*${0}*` means "contains", `${0}` means "is" - queryExpr: "${0}*", - - // ignoreCase: Boolean - // Set true if the ComboBox/FilteringSelect should ignore case when matching possible items - ignoreCase: true, - - // hasDownArrow: [const] Boolean - // Set this textbox to have a down arrow button, to display the drop down list. - // Defaults to true. - hasDownArrow: true, - - templateString: dojo.cache("dijit.form", "templates/ComboBox.html", "<div class=\"dijit dijitReset dijitInlineTable dijitLeft\"\n\tid=\"widget_${id}\"\n\tdojoAttachPoint=\"comboNode\" waiRole=\"combobox\"\n\t><div class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer'\n\t\tdojoAttachPoint=\"downArrowNode\" waiRole=\"presentation\"\n\t\tdojoAttachEvent=\"onmousedown:_onArrowMouseDown\"\n\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"▼ \" type=\"text\" tabIndex=\"-1\" readOnly waiRole=\"presentation\"\n\t\t\t${_buttonInputDisabled}\n\t/></div\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"Χ \" type=\"text\" tabIndex=\"-1\" readOnly waiRole=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class='dijitReset dijitInputInner' ${!nameAttrSetting} type=\"text\" autocomplete=\"off\"\n\t\t\tdojoAttachEvent=\"onkeypress:_onKeyPress,compositionend\"\n\t\t\tdojoAttachPoint=\"textbox,focusNode\" waiRole=\"textbox\" waiState=\"haspopup-true,autocomplete-list\"\n\t/></div\n></div>\n"), - - baseClass: "dijitTextBox dijitComboBox", - - // Set classes like dijitDownArrowButtonHover depending on - // mouse action over button node - cssStateNodes: { - "downArrowNode": "dijitDownArrowButton" - }, - - _getCaretPos: function(/*DomNode*/ element){ - // khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22 - var pos = 0; - if(typeof(element.selectionStart) == "number"){ - // FIXME: this is totally borked on Moz < 1.3. Any recourse? - pos = element.selectionStart; - }else if(dojo.isIE){ - // in the case of a mouse click in a popup being handled, - // then the dojo.doc.selection is not the textarea, but the popup - // var r = dojo.doc.selection.createRange(); - // hack to get IE 6 to play nice. What a POS browser. - var tr = dojo.doc.selection.createRange().duplicate(); - var ntr = element.createTextRange(); - tr.move("character",0); - ntr.move("character",0); - try{ - // If control doesnt have focus, you get an exception. - // Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes). - // There appears to be no workaround for this - googled for quite a while. - ntr.setEndPoint("EndToEnd", tr); - pos = String(ntr.text).replace(/\r/g,"").length; - }catch(e){ - // If focus has shifted, 0 is fine for caret pos. - } - } - return pos; - }, - - _setCaretPos: function(/*DomNode*/ element, /*Number*/ location){ - location = parseInt(location); - dijit.selectInputText(element, location, location); - }, - - _setDisabledAttr: function(/*Boolean*/ value){ - // Additional code to set disabled state of ComboBox node. - // Overrides _FormValueWidget._setDisabledAttr() or ValidationTextBox._setDisabledAttr(). - this.inherited(arguments); - dijit.setWaiState(this.comboNode, "disabled", value); - }, - - _abortQuery: function(){ - // stop in-progress query - if(this.searchTimer){ - clearTimeout(this.searchTimer); - this.searchTimer = null; - } - if(this._fetchHandle){ - if(this._fetchHandle.abort){ this._fetchHandle.abort(); } - this._fetchHandle = null; - } - }, - - _onInput: function(/*Event*/ evt){ - // summary: - // Handles paste events - if(!this.searchTimer && (evt.type == 'paste'/*IE|WebKit*/ || evt.type == 'input'/*Firefox*/) && this._lastInput != this.textbox.value){ - this.searchTimer = setTimeout(dojo.hitch(this, function(){ - this._onKeyPress({charOrCode: 229}); // fake IME key to cause a search - }), 100); // long delay that will probably be preempted by keyboard input - } - this.inherited(arguments); - }, - - _onKeyPress: function(/*Event*/ evt){ - // summary: - // Handles keyboard events - var key = evt.charOrCode; - // except for cutting/pasting case - ctrl + x/v - if(evt.altKey || ((evt.ctrlKey || evt.metaKey) && (key != 'x' && key != 'v')) || key == dojo.keys.SHIFT){ - return; // throw out weird key combinations and spurious events - } - var doSearch = false; - var searchFunction = "_startSearchFromInput"; - var pw = this._popupWidget; - var dk = dojo.keys; - var highlighted = null; - this._prev_key_backspace = false; - this._abortQuery(); - if(this._isShowingNow){ - pw.handleKey(key); - highlighted = pw.getHighlightedOption(); - } - switch(key){ - case dk.PAGE_DOWN: - case dk.DOWN_ARROW: - case dk.PAGE_UP: - case dk.UP_ARROW: - if(!this._isShowingNow){ - doSearch = true; - searchFunction = "_startSearchAll"; - }else{ - this._announceOption(highlighted); - } - dojo.stopEvent(evt); - break; - - case dk.ENTER: - // prevent submitting form if user presses enter. Also - // prevent accepting the value if either Next or Previous - // are selected - if(highlighted){ - // only stop event on prev/next - if(highlighted == pw.nextButton){ - this._nextSearch(1); - dojo.stopEvent(evt); - break; - }else if(highlighted == pw.previousButton){ - this._nextSearch(-1); - dojo.stopEvent(evt); - break; - } - }else{ - // Update 'value' (ex: KY) according to currently displayed text - this._setBlurValue(); // set value if needed - this._setCaretPos(this.focusNode, this.focusNode.value.length); // move cursor to end and cancel highlighting - } - // default case: - // prevent submit, but allow event to bubble - evt.preventDefault(); - // fall through - - case dk.TAB: - var newvalue = this.get('displayedValue'); - // if the user had More Choices selected fall into the - // _onBlur handler - if(pw && ( - newvalue == pw._messages["previousMessage"] || - newvalue == pw._messages["nextMessage"]) - ){ - break; - } - if(highlighted){ - this._selectOption(); - } - if(this._isShowingNow){ - this._lastQuery = null; // in case results come back later - this._hideResultList(); - } - break; - - case ' ': - if(highlighted){ - dojo.stopEvent(evt); - this._selectOption(); - this._hideResultList(); - }else{ - doSearch = true; - } - break; - - case dk.ESCAPE: - if(this._isShowingNow){ - dojo.stopEvent(evt); - this._hideResultList(); - } - break; - - case dk.DELETE: - case dk.BACKSPACE: - this._prev_key_backspace = true; - doSearch = true; - break; - - default: - // Non char keys (F1-F12 etc..) shouldn't open list. - // Ascii characters and IME input (Chinese, Japanese etc.) should. - // On IE and safari, IME input produces keycode == 229, and we simulate - // it on firefox by attaching to compositionend event (see compositionend method) - doSearch = typeof key == 'string' || key == 229; - } - if(doSearch){ - // need to wait a tad before start search so that the event - // bubbles through DOM and we have value visible - this.item = undefined; // undefined means item needs to be set - this.searchTimer = setTimeout(dojo.hitch(this, searchFunction),1); - } - }, - - _autoCompleteText: function(/*String*/ text){ - // summary: - // Fill in the textbox with the first item from the drop down - // list, and highlight the characters that were - // auto-completed. For example, if user typed "CA" and the - // drop down list appeared, the textbox would be changed to - // "California" and "ifornia" would be highlighted. - - var fn = this.focusNode; - - // IE7: clear selection so next highlight works all the time - dijit.selectInputText(fn, fn.value.length); - // does text autoComplete the value in the textbox? - var caseFilter = this.ignoreCase? 'toLowerCase' : 'substr'; - if(text[caseFilter](0).indexOf(this.focusNode.value[caseFilter](0)) == 0){ - var cpos = this._getCaretPos(fn); - // only try to extend if we added the last character at the end of the input - if((cpos+1) > fn.value.length){ - // only add to input node as we would overwrite Capitalisation of chars - // actually, that is ok - fn.value = text;//.substr(cpos); - // visually highlight the autocompleted characters - dijit.selectInputText(fn, cpos); - } - }else{ - // text does not autoComplete; replace the whole value and highlight - fn.value = text; - dijit.selectInputText(fn); - } - }, - - _openResultList: function(/*Object*/ results, /*Object*/ dataObject){ - this._fetchHandle = null; - if( this.disabled || - this.readOnly || - (dataObject.query[this.searchAttr] != this._lastQuery) - ){ - return; - } - this._popupWidget.clearResultList(); - if(!results.length && !this._maxOptions){ // this condition needs to match !this._isvalid set in FilteringSelect::_openResultList - this._hideResultList(); - return; - } - - - // Fill in the textbox with the first item from the drop down list, - // and highlight the characters that were auto-completed. For - // example, if user typed "CA" and the drop down list appeared, the - // textbox would be changed to "California" and "ifornia" would be - // highlighted. - - dataObject._maxOptions = this._maxOptions; - var nodes = this._popupWidget.createOptions( - results, - dataObject, - dojo.hitch(this, "_getMenuLabelFromItem") - ); - - // show our list (only if we have content, else nothing) - this._showResultList(); - - // #4091: - // tell the screen reader that the paging callback finished by - // shouting the next choice - if(dataObject.direction){ - if(1 == dataObject.direction){ - this._popupWidget.highlightFirstOption(); - }else if(-1 == dataObject.direction){ - this._popupWidget.highlightLastOption(); - } - this._announceOption(this._popupWidget.getHighlightedOption()); - }else if(this.autoComplete && !this._prev_key_backspace /*&& !dataObject.direction*/ - // when the user clicks the arrow button to show the full list, - // startSearch looks for "*". - // it does not make sense to autocomplete - // if they are just previewing the options available. - && !/^[*]+$/.test(dataObject.query[this.searchAttr])){ - this._announceOption(nodes[1]); // 1st real item - } - }, - - _showResultList: function(){ - this._hideResultList(); - // hide the tooltip - this.displayMessage(""); - - // Position the list and if it's too big to fit on the screen then - // size it to the maximum possible height - // Our dear friend IE doesnt take max-height so we need to - // calculate that on our own every time - - // TODO: want to redo this, see - // http://trac.dojotoolkit.org/ticket/3272 - // and - // http://trac.dojotoolkit.org/ticket/4108 - - - // natural size of the list has changed, so erase old - // width/height settings, which were hardcoded in a previous - // call to this function (via dojo.marginBox() call) - dojo.style(this._popupWidget.domNode, {width: "", height: ""}); - - var best = this.open(); - // #3212: - // only set auto scroll bars if necessary prevents issues with - // scroll bars appearing when they shouldn't when node is made - // wider (fractional pixels cause this) - var popupbox = dojo.marginBox(this._popupWidget.domNode); - this._popupWidget.domNode.style.overflow = - ((best.h == popupbox.h) && (best.w == popupbox.w)) ? "hidden" : "auto"; - // #4134: - // borrow TextArea scrollbar test so content isn't covered by - // scrollbar and horizontal scrollbar doesn't appear - var newwidth = best.w; - if(best.h < this._popupWidget.domNode.scrollHeight){ - newwidth += 16; - } - dojo.marginBox(this._popupWidget.domNode, { - h: best.h, - w: Math.max(newwidth, this.domNode.offsetWidth) - }); - - // If we increased the width of drop down to match the width of ComboBox.domNode, - // then need to reposition the drop down (wrapper) so (all of) the drop down still - // appears underneath the ComboBox.domNode - if(newwidth < this.domNode.offsetWidth){ - this._popupWidget.domNode.parentNode.style.left = dojo.position(this.domNode, true).x + "px"; - } - - dijit.setWaiState(this.comboNode, "expanded", "true"); - }, - - _hideResultList: function(){ - this._abortQuery(); - if(this._isShowingNow){ - dijit.popup.close(this._popupWidget); - this._isShowingNow=false; - dijit.setWaiState(this.comboNode, "expanded", "false"); - dijit.removeWaiState(this.focusNode,"activedescendant"); - } - }, - - _setBlurValue: function(){ - // if the user clicks away from the textbox OR tabs away, set the - // value to the textbox value - // #4617: - // if value is now more choices or previous choices, revert - // the value - var newvalue = this.get('displayedValue'); - var pw = this._popupWidget; - if(pw && ( - newvalue == pw._messages["previousMessage"] || - newvalue == pw._messages["nextMessage"] - ) - ){ - this._setValueAttr(this._lastValueReported, true); - }else if(typeof this.item == "undefined"){ - // Update 'value' (ex: KY) according to currently displayed text - this.item = null; - this.set('displayedValue', newvalue); - }else{ - if(this.value != this._lastValueReported){ - dijit.form._FormValueWidget.prototype._setValueAttr.call(this, this.value, true); - } - this._refreshState(); - } - }, - - _onBlur: function(){ - // summary: - // Called magically when focus has shifted away from this widget and it's drop down - this._hideResultList(); - this.inherited(arguments); - }, - - _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){ - // summary: - // Set the displayed valued in the input box, and the hidden value - // that gets submitted, based on a dojo.data store item. - // description: - // Users shouldn't call this function; they should be calling - // attr('item', value) - // tags: - // private - if(!displayedValue){ displayedValue = this.labelFunc(item, this.store); } - this.value = this._getValueField() != this.searchAttr? this.store.getIdentity(item) : displayedValue; - this.item = item; - dijit.form.ComboBox.superclass._setValueAttr.call(this, this.value, priorityChange, displayedValue); - }, - - _announceOption: function(/*Node*/ node){ - // summary: - // a11y code that puts the highlighted option in the textbox. - // This way screen readers will know what is happening in the - // menu. - - if(!node){ - return; - } - // pull the text value from the item attached to the DOM node - var newValue; - if(node == this._popupWidget.nextButton || - node == this._popupWidget.previousButton){ - newValue = node.innerHTML; - this.item = undefined; - this.value = ''; - }else{ - newValue = this.labelFunc(node.item, this.store); - this.set('item', node.item, false, newValue); - } - // get the text that the user manually entered (cut off autocompleted text) - this.focusNode.value = this.focusNode.value.substring(0, this._lastInput.length); - // set up ARIA activedescendant - dijit.setWaiState(this.focusNode, "activedescendant", dojo.attr(node, "id")); - // autocomplete the rest of the option to announce change - this._autoCompleteText(newValue); - }, - - _selectOption: function(/*Event*/ evt){ - // summary: - // Menu callback function, called when an item in the menu is selected. - if(evt){ - this._announceOption(evt.target); - } - this._hideResultList(); - this._setCaretPos(this.focusNode, this.focusNode.value.length); - dijit.form._FormValueWidget.prototype._setValueAttr.call(this, this.value, true); // set this.value and fire onChange - }, - - _onArrowMouseDown: function(evt){ - // summary: - // Callback when arrow is clicked - if(this.disabled || this.readOnly){ - return; - } - dojo.stopEvent(evt); - this.focus(); - if(this._isShowingNow){ - this._hideResultList(); - }else{ - // forces full population of results, if they click - // on the arrow it means they want to see more options - this._startSearchAll(); - } - }, - - _startSearchAll: function(){ - this._startSearch(''); - }, - - _startSearchFromInput: function(){ - this._startSearch(this.focusNode.value.replace(/([\\\*\?])/g, "\\$1")); - }, - - _getQueryString: function(/*String*/ text){ - return dojo.string.substitute(this.queryExpr, [text]); - }, - - _startSearch: function(/*String*/ key){ - if(!this._popupWidget){ - var popupId = this.id + "_popup"; - this._popupWidget = new dijit.form._ComboBoxMenu({ - onChange: dojo.hitch(this, this._selectOption), - id: popupId, - dir: this.dir - }); - dijit.removeWaiState(this.focusNode,"activedescendant"); - dijit.setWaiState(this.textbox,"owns",popupId); // associate popup with textbox - } - // create a new query to prevent accidentally querying for a hidden - // value from FilteringSelect's keyField - var query = dojo.clone(this.query); // #5970 - this._lastInput = key; // Store exactly what was entered by the user. - this._lastQuery = query[this.searchAttr] = this._getQueryString(key); - // #5970: set _lastQuery, *then* start the timeout - // otherwise, if the user types and the last query returns before the timeout, - // _lastQuery won't be set and their input gets rewritten - this.searchTimer=setTimeout(dojo.hitch(this, function(query, _this){ - this.searchTimer = null; - var fetch = { - queryOptions: { - ignoreCase: this.ignoreCase, - deep: true - }, - query: query, - onBegin: dojo.hitch(this, "_setMaxOptions"), - onComplete: dojo.hitch(this, "_openResultList"), - onError: function(errText){ - _this._fetchHandle = null; - console.error('dijit.form.ComboBox: ' + errText); - dojo.hitch(_this, "_hideResultList")(); - }, - start: 0, - count: this.pageSize - }; - dojo.mixin(fetch, _this.fetchProperties); - this._fetchHandle = _this.store.fetch(fetch); - - var nextSearch = function(dataObject, direction){ - dataObject.start += dataObject.count*direction; - // #4091: - // tell callback the direction of the paging so the screen - // reader knows which menu option to shout - dataObject.direction = direction; - this._fetchHandle = this.store.fetch(dataObject); - }; - this._nextSearch = this._popupWidget.onPage = dojo.hitch(this, nextSearch, this._fetchHandle); - }, query, this), this.searchDelay); - }, - - _setMaxOptions: function(size, request){ - this._maxOptions = size; - }, - - _getValueField: function(){ - // summmary: - // Helper for postMixInProperties() to set this.value based on data inlined into the markup. - // Returns the attribute name in the item (in dijit.form._ComboBoxDataStore) to use as the value. - return this.searchAttr; - }, - - /////////////// Event handlers ///////////////////// - - // FIXME: For 2.0, rename to "_compositionEnd" - compositionend: function(/*Event*/ evt){ - // summary: - // When inputting characters using an input method, such as - // Asian languages, it will generate this event instead of - // onKeyDown event. - // Note: this event is only triggered in FF (not in IE/safari) - // tags: - // private - - // 229 is the code produced by IE and safari while pressing keys during - // IME input mode - this._onKeyPress({charOrCode: 229}); - }, - - //////////// INITIALIZATION METHODS /////////////////////////////////////// - - constructor: function(){ - this.query={}; - this.fetchProperties={}; - }, - - postMixInProperties: function(){ - if(!this.store){ - var srcNodeRef = this.srcNodeRef; - - // if user didn't specify store, then assume there are option tags - this.store = new dijit.form._ComboBoxDataStore(srcNodeRef); - - // if there is no value set and there is an option list, set - // the value to the first value to be consistent with native - // Select - - // Firefox and Safari set value - // IE6 and Opera set selectedIndex, which is automatically set - // by the selected attribute of an option tag - // IE6 does not set value, Opera sets value = selectedIndex - if(!("value" in this.params)){ - var item = this.store.fetchSelectedItem(); - if(item){ - var valueField = this._getValueField(); - this.value = valueField != this.searchAttr? this.store.getValue(item, valueField) : this.labelFunc(item, this.store); - } - } - } - this.inherited(arguments); - }, - - postCreate: function(){ - // summary: - // Subclasses must call this method from their postCreate() methods - // tags: - // protected - - if(!this.hasDownArrow){ - this.downArrowNode.style.display = "none"; - } - - // find any associated label element and add to ComboBox node. - var label=dojo.query('label[for="'+this.id+'"]'); - if(label.length){ - label[0].id = (this.id+"_label"); - var cn=this.comboNode; - dijit.setWaiState(cn, "labelledby", label[0].id); - - } - this.inherited(arguments); - }, - - uninitialize: function(){ - if(this._popupWidget && !this._popupWidget._destroyed){ - this._hideResultList(); - this._popupWidget.destroy(); - } - this.inherited(arguments); - }, - - _getMenuLabelFromItem: function(/*Item*/ item){ - var label = this.labelAttr? this.store.getValue(item, this.labelAttr) : this.labelFunc(item, this.store); - var labelType = this.labelType; - // If labelType is not "text" we don't want to screw any markup ot whatever. - if(this.highlightMatch != "none" && this.labelType == "text" && this._lastInput){ - label = this.doHighlight(label, this._escapeHtml(this._lastInput)); - labelType = "html"; - } - return {html: labelType == "html", label: label}; - }, - - doHighlight: function(/*String*/label, /*String*/find){ - // summary: - // Highlights the string entered by the user in the menu. By default this - // highlights the first occurence found. Override this method - // to implement your custom highlighing. - // tags: - // protected - - // Add greedy when this.highlightMatch == "all" - var modifiers = "i"+(this.highlightMatch == "all"?"g":""); - var escapedLabel = this._escapeHtml(label); - find = dojo.regexp.escapeString(find); // escape regexp special chars - var ret = escapedLabel.replace(new RegExp("(^|\\s)("+ find +")", modifiers), - '$1<span class="dijitComboBoxHighlightMatch">$2</span>'); - return ret;// returns String, (almost) valid HTML (entities encoded) - }, - - _escapeHtml: function(/*string*/str){ - // TODO Should become dojo.html.entities(), when exists use instead - // summary: - // Adds escape sequences for special characters in XML: &<>"' - str = String(str).replace(/&/gm, "&").replace(/</gm, "<") - .replace(/>/gm, ">").replace(/"/gm, """); - return str; // string - }, - - open: function(){ - // summary: - // Opens the drop down menu. TODO: rename to _open. - // tags: - // private - this._isShowingNow=true; - return dijit.popup.open({ - popup: this._popupWidget, - around: this.domNode, - parent: this - }); - }, - - reset: function(){ - // Overrides the _FormWidget.reset(). - // Additionally reset the .item (to clean up). - this.item = null; - this.inherited(arguments); - }, - - labelFunc: function(/*item*/ item, /*dojo.data.store*/ store){ - // summary: - // Computes the label to display based on the dojo.data store item. - // returns: - // The label that the ComboBox should display - // tags: - // private - - // Use toString() because XMLStore returns an XMLItem whereas this - // method is expected to return a String (#9354) - return store.getValue(item, this.searchAttr).toString(); // String - } - } -); - -dojo.declare( - "dijit.form._ComboBoxMenu", - [dijit._Widget, dijit._Templated, dijit._CssStateMixin], - { - // summary: - // Focus-less menu for internal use in `dijit.form.ComboBox` - // tags: - // private - - templateString: "<ul class='dijitReset dijitMenu' dojoAttachEvent='onmousedown:_onMouseDown,onmouseup:_onMouseUp,onmouseover:_onMouseOver,onmouseout:_onMouseOut' tabIndex='-1' style='overflow: \"auto\"; overflow-x: \"hidden\";'>" - +"<li class='dijitMenuItem dijitMenuPreviousButton' dojoAttachPoint='previousButton' waiRole='option'></li>" - +"<li class='dijitMenuItem dijitMenuNextButton' dojoAttachPoint='nextButton' waiRole='option'></li>" - +"</ul>", - - // _messages: Object - // Holds "next" and "previous" text for paging buttons on drop down - _messages: null, - - baseClass: "dijitComboBoxMenu", - - postMixInProperties: function(){ - this._messages = dojo.i18n.getLocalization("dijit.form", "ComboBox", this.lang); - this.inherited(arguments); - }, - - _setValueAttr: function(/*Object*/ value){ - this.value = value; - this.onChange(value); - }, - - // stubs - onChange: function(/*Object*/ value){ - // summary: - // Notifies ComboBox/FilteringSelect that user clicked an option in the drop down menu. - // Probably should be called onSelect. - // tags: - // callback - }, - onPage: function(/*Number*/ direction){ - // summary: - // Notifies ComboBox/FilteringSelect that user clicked to advance to next/previous page. - // tags: - // callback - }, - - postCreate: function(){ - // fill in template with i18n messages - this.previousButton.innerHTML = this._messages["previousMessage"]; - this.nextButton.innerHTML = this._messages["nextMessage"]; - this.inherited(arguments); - }, - - onClose: function(){ - // summary: - // Callback from dijit.popup code to this widget, notifying it that it closed - // tags: - // private - this._blurOptionNode(); - }, - - _createOption: function(/*Object*/ item, labelFunc){ - // summary: - // Creates an option to appear on the popup menu subclassed by - // `dijit.form.FilteringSelect`. - - var labelObject = labelFunc(item); - var menuitem = dojo.doc.createElement("li"); - dijit.setWaiRole(menuitem, "option"); - if(labelObject.html){ - menuitem.innerHTML = labelObject.label; - }else{ - menuitem.appendChild( - dojo.doc.createTextNode(labelObject.label) - ); - } - // #3250: in blank options, assign a normal height - if(menuitem.innerHTML == ""){ - menuitem.innerHTML = " "; - } - menuitem.item=item; - return menuitem; - }, - - createOptions: function(results, dataObject, labelFunc){ - // summary: - // Fills in the items in the drop down list - // results: - // Array of dojo.data items - // dataObject: - // dojo.data store - // labelFunc: - // Function to produce a label in the drop down list from a dojo.data item - - //this._dataObject=dataObject; - //this._dataObject.onComplete=dojo.hitch(comboBox, comboBox._openResultList); - // display "Previous . . ." button - this.previousButton.style.display = (dataObject.start == 0) ? "none" : ""; - dojo.attr(this.previousButton, "id", this.id + "_prev"); - // create options using _createOption function defined by parent - // ComboBox (or FilteringSelect) class - // #2309: - // iterate over cache nondestructively - dojo.forEach(results, function(item, i){ - var menuitem = this._createOption(item, labelFunc); - menuitem.className = "dijitReset dijitMenuItem" + - (this.isLeftToRight() ? "" : " dijitMenuItemRtl"); - dojo.attr(menuitem, "id", this.id + i); - this.domNode.insertBefore(menuitem, this.nextButton); - }, this); - // display "Next . . ." button - var displayMore = false; - //Try to determine if we should show 'more'... - if(dataObject._maxOptions && dataObject._maxOptions != -1){ - if((dataObject.start + dataObject.count) < dataObject._maxOptions){ - displayMore = true; - }else if((dataObject.start + dataObject.count) > dataObject._maxOptions && dataObject.count == results.length){ - //Weird return from a datastore, 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(dataObject.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"; - dojo.attr(this.nextButton,"id", this.id + "_next"); - return this.domNode.childNodes; - }, - - clearResultList: function(){ - // summary: - // Clears the entries in the drop down list, but of course keeps the previous and next buttons. - while(this.domNode.childNodes.length>2){ - this.domNode.removeChild(this.domNode.childNodes[this.domNode.childNodes.length-2]); - } - }, - - _onMouseDown: function(/*Event*/ evt){ - dojo.stopEvent(evt); - }, - - _onMouseUp: function(/*Event*/ evt){ - if(evt.target === this.domNode || !this._highlighted_option){ - return; - }else if(evt.target == this.previousButton){ - this.onPage(-1); - }else if(evt.target == this.nextButton){ - this.onPage(1); - }else{ - var tgt = evt.target; - // while the clicked node is inside the div - while(!tgt.item){ - // recurse to the top - tgt = tgt.parentNode; - } - this._setValueAttr({ target: tgt }, true); - } - }, - - _onMouseOver: function(/*Event*/ evt){ - if(evt.target === this.domNode){ return; } - var tgt = evt.target; - if(!(tgt == this.previousButton || tgt == this.nextButton)){ - // while the clicked node is inside the div - while(!tgt.item){ - // recurse to the top - tgt = tgt.parentNode; - } - } - this._focusOptionNode(tgt); - }, - - _onMouseOut: function(/*Event*/ evt){ - if(evt.target === this.domNode){ return; } - this._blurOptionNode(); - }, - - _focusOptionNode: function(/*DomNode*/ node){ - // summary: - // Does the actual highlight. - if(this._highlighted_option != node){ - this._blurOptionNode(); - this._highlighted_option = node; - dojo.addClass(this._highlighted_option, "dijitMenuItemSelected"); - } - }, - - _blurOptionNode: function(){ - // summary: - // Removes highlight on highlighted option. - if(this._highlighted_option){ - dojo.removeClass(this._highlighted_option, "dijitMenuItemSelected"); - this._highlighted_option = null; - } - }, - - _highlightNextOption: function(){ - // summary: - // Highlight the item just below the current selection. - // If nothing selected, highlight first option. - - // because each press of a button clears the menu, - // the highlighted option sometimes becomes detached from the menu! - // test to see if the option has a parent to see if this is the case. - if(!this.getHighlightedOption()){ - var fc = this.domNode.firstChild; - this._focusOptionNode(fc.style.display == "none" ? fc.nextSibling : fc); - }else{ - var ns = this._highlighted_option.nextSibling; - if(ns && ns.style.display != "none"){ - this._focusOptionNode(ns); - }else{ - this.highlightFirstOption(); - } - } - // scrollIntoView is called outside of _focusOptionNode because in IE putting it inside causes the menu to scroll up on mouseover - dojo.window.scrollIntoView(this._highlighted_option); - }, - - highlightFirstOption: function(){ - // summary: - // Highlight the first real item in the list (not Previous Choices). - var first = this.domNode.firstChild; - var second = first.nextSibling; - this._focusOptionNode(second.style.display == "none" ? first : second); // remotely possible that Previous Choices is the only thing in the list - dojo.window.scrollIntoView(this._highlighted_option); - }, - - highlightLastOption: function(){ - // summary: - // Highlight the last real item in the list (not More Choices). - this._focusOptionNode(this.domNode.lastChild.previousSibling); - dojo.window.scrollIntoView(this._highlighted_option); - }, - - _highlightPrevOption: function(){ - // summary: - // Highlight the item just above the current selection. - // If nothing selected, highlight last option (if - // you select Previous and try to keep scrolling up the list). - if(!this.getHighlightedOption()){ - var lc = this.domNode.lastChild; - this._focusOptionNode(lc.style.display == "none" ? lc.previousSibling : lc); - }else{ - var ps = this._highlighted_option.previousSibling; - if(ps && ps.style.display != "none"){ - this._focusOptionNode(ps); - }else{ - this.highlightLastOption(); - } - } - dojo.window.scrollIntoView(this._highlighted_option); - }, - - _page: function(/*Boolean*/ up){ - // summary: - // Handles page-up and page-down keypresses - - var scrollamount = 0; - var oldscroll = this.domNode.scrollTop; - var height = dojo.style(this.domNode, "height"); - // if no item is highlighted, highlight the first option - if(!this.getHighlightedOption()){ - this._highlightNextOption(); - } - while(scrollamount<height){ - if(up){ - // stop at option 1 - if(!this.getHighlightedOption().previousSibling || - this._highlighted_option.previousSibling.style.display == "none"){ - break; - } - this._highlightPrevOption(); - }else{ - // stop at last option - if(!this.getHighlightedOption().nextSibling || - this._highlighted_option.nextSibling.style.display == "none"){ - break; - } - this._highlightNextOption(); - } - // going backwards - var newscroll=this.domNode.scrollTop; - scrollamount+=(newscroll-oldscroll)*(up ? -1:1); - oldscroll=newscroll; - } - }, - - pageUp: function(){ - // summary: - // Handles pageup keypress. - // TODO: just call _page directly from handleKey(). - // tags: - // private - this._page(true); - }, - - pageDown: function(){ - // summary: - // Handles pagedown keypress. - // TODO: just call _page directly from handleKey(). - // tags: - // private - this._page(false); - }, - - getHighlightedOption: function(){ - // summary: - // Returns the highlighted option. - var ho = this._highlighted_option; - return (ho && ho.parentNode) ? ho : null; - }, - - handleKey: function(key){ - switch(key){ - case dojo.keys.DOWN_ARROW: - this._highlightNextOption(); - break; - case dojo.keys.PAGE_DOWN: - this.pageDown(); - break; - case dojo.keys.UP_ARROW: - this._highlightPrevOption(); - break; - case dojo.keys.PAGE_UP: - this.pageUp(); - break; - } - } - } -); - -dojo.declare( - "dijit.form.ComboBox", - [dijit.form.ValidationTextBox, dijit.form.ComboBoxMixin], - { - // summary: - // Auto-completing text box, and base class for dijit.form.FilteringSelect. - // - // description: - // The drop down box's values are populated from an class called - // a data provider, which returns a list of values based on the characters - // that the user has typed into the input box. - // If OPTION tags are used as the data provider via markup, - // then the OPTION tag's child text node is used as the widget value - // when selected. The OPTION tag's value attribute is ignored. - // To set the default value when using OPTION tags, specify the selected - // attribute on 1 of the child OPTION tags. - // - // Some of the options to the ComboBox are actually arguments to the data - // provider. - - _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){ - // summary: - // Hook so attr('value', value) works. - // description: - // Sets the value of the select. - this.item = null; // value not looked up in store - if(!value){ value = ''; } // null translates to blank - dijit.form.ValidationTextBox.prototype._setValueAttr.call(this, value, priorityChange, displayedValue); - } - } -); - -dojo.declare("dijit.form._ComboBoxDataStore", null, { - // summary: - // Inefficient but small data store specialized for inlined `dijit.form.ComboBox` data - // - // description: - // Provides a store for inlined data like: - // - // | <select> - // | <option value="AL">Alabama</option> - // | ... - // - // Actually. just implements the subset of dojo.data.Read/Notification - // needed for ComboBox and FilteringSelect to work. - // - // Note that an item is just a pointer to the <option> DomNode. - - constructor: function( /*DomNode*/ root){ - this.root = root; - if(root.tagName != "SELECT" && root.firstChild){ - root = dojo.query("select", root); - if(root.length > 0){ // SELECT is a child of srcNodeRef - root = root[0]; - }else{ // no select, so create 1 to parent the option tags to define selectedIndex - this.root.innerHTML = "<SELECT>"+this.root.innerHTML+"</SELECT>"; - root = this.root.firstChild; - } - this.root = root; - } - dojo.query("> option", root).forEach(function(node){ - // TODO: this was added in #3858 but unclear why/if it's needed; doesn't seem to be. - // If it is needed then can we just hide the select itself instead? - //node.style.display="none"; - node.innerHTML = dojo.trim(node.innerHTML); - }); - - }, - - getValue: function( /* item */ item, - /* attribute-name-string */ attribute, - /* value? */ defaultValue){ - return (attribute == "value") ? item.value : (item.innerText || item.textContent || ''); - }, - - isItemLoaded: function(/* anything */ something){ - return true; - }, - - getFeatures: function(){ - return {"dojo.data.api.Read": true, "dojo.data.api.Identity": true}; - }, - - _fetchItems: function( /* Object */ args, - /* Function */ findCallback, - /* Function */ errorCallback){ - // summary: - // See dojo.data.util.simpleFetch.fetch() - if(!args.query){ args.query = {}; } - if(!args.query.name){ args.query.name = ""; } - if(!args.queryOptions){ args.queryOptions = {}; } - var matcher = dojo.data.util.filter.patternToRegExp(args.query.name, args.queryOptions.ignoreCase), - items = dojo.query("> option", this.root).filter(function(option){ - return (option.innerText || option.textContent || '').match(matcher); - } ); - if(args.sort){ - items.sort(dojo.data.util.sorter.createSortFunction(args.sort, this)); - } - findCallback(items, args); - }, - - close: function(/*dojo.data.api.Request || args || null */ request){ - return; - }, - - getLabel: function(/* item */ item){ - return item.innerHTML; - }, - - getIdentity: function(/* item */ item){ - return dojo.attr(item, "value"); - }, - - fetchItemByIdentity: function(/* Object */ args){ - // summary: - // Given the identity of an item, this method returns the item that has - // that identity through the onItem callback. - // Refer to dojo.data.api.Identity.fetchItemByIdentity() for more details. - // - // description: - // Given arguments like: - // - // | {identity: "CA", onItem: function(item){...} - // - // Call `onItem()` with the DOM node `<option value="CA">California</option>` - var item = dojo.query("> option[value='" + args.identity + "']", this.root)[0]; - args.onItem(item); - }, - - fetchSelectedItem: function(){ - // summary: - // Get the option marked as selected, like `<option selected>`. - // Not part of dojo.data API. - var root = this.root, - si = root.selectedIndex; - return typeof si == "number" - ? dojo.query("> option:nth-child(" + (si != -1 ? si+1 : 1) + ")", root)[0] - : null; // dojo.data.Item - } -}); -//Mix in the simple fetch implementation to this class. -dojo.extend(dijit.form._ComboBoxDataStore,dojo.data.util.simpleFetch); - -} - -if(!dojo._hasResource["dijit.form.FilteringSelect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.FilteringSelect"] = true; -dojo.provide("dijit.form.FilteringSelect"); - - - -dojo.declare( - "dijit.form.FilteringSelect", - [dijit.form.MappedTextBox, dijit.form.ComboBoxMixin], - { - // summary: - // An enhanced version of the HTML SELECT tag, populated dynamically - // - // description: - // An enhanced version of the HTML SELECT tag, populated dynamically. It works - // very nicely with very large data sets because it can load and page data as needed. - // It also resembles ComboBox, but does not allow values outside of the provided ones. - // If OPTION tags are used as the data provider via markup, then the - // OPTION tag's child text node is used as the displayed value when selected - // while the OPTION tag's value attribute is used as the widget value on form submit. - // To set the default value when using OPTION tags, specify the selected - // attribute on 1 of the child OPTION tags. - // - // Similar features: - // - There is a drop down list of possible values. - // - You can only enter a value from the drop down list. (You can't - // enter an arbitrary value.) - // - The value submitted with the form is the hidden value (ex: CA), - // not the displayed value a.k.a. label (ex: California) - // - // Enhancements over plain HTML version: - // - If you type in some text then it will filter down the list of - // possible values in the drop down list. - // - List can be specified either as a static list or via a javascript - // function (that can get the list from a server) - - _isvalid: true, - - // required: Boolean - // True (default) if user is required to enter a value into this field. - required: true, - - _lastDisplayedValue: "", - - isValid: function(){ - // Overrides ValidationTextBox.isValid() - return this._isvalid || (!this.required && this.get('displayedValue') == ""); // #5974 - }, - - _refreshState: function(){ - if(!this.searchTimer){ // state will be refreshed after results are returned - this.inherited(arguments); - } - }, - - _callbackSetLabel: function( /*Array*/ result, - /*Object*/ dataObject, - /*Boolean?*/ priorityChange){ - // summary: - // Callback function that dynamically sets the label of the - // ComboBox - - // setValue does a synchronous lookup, - // so it calls _callbackSetLabel directly, - // and so does not pass dataObject - // still need to test against _lastQuery in case it came too late - if((dataObject && dataObject.query[this.searchAttr] != this._lastQuery) || (!dataObject && result.length && this.store.getIdentity(result[0]) != this._lastQuery)){ - return; - } - if(!result.length){ - //#3268: do nothing on bad input - //#3285: change CSS to indicate error - this.valueNode.value = ""; - dijit.form.TextBox.superclass._setValueAttr.call(this, "", priorityChange || (priorityChange === undefined && !this._focused)); - this._isvalid = false; - this.validate(this._focused); - this.item = null; - }else{ - this.set('item', result[0], priorityChange); - } - }, - - _openResultList: function(/*Object*/ results, /*Object*/ dataObject){ - // Overrides ComboBox._openResultList() - - // #3285: tap into search callback to see if user's query resembles a match - if(dataObject.query[this.searchAttr] != this._lastQuery){ - return; - } - if(this.item === undefined){ // item == undefined for keyboard search - this._isvalid = results.length != 0 || this._maxOptions != 0; // result.length==0 && maxOptions != 0 implies the nextChoices item selected but then the datastore returned 0 more entries - this.validate(true); - } - dijit.form.ComboBoxMixin.prototype._openResultList.apply(this, arguments); - }, - - _getValueAttr: function(){ - // summary: - // Hook for attr('value') to work. - - // don't get the textbox value but rather the previously set hidden value. - // Use this.valueNode.value which isn't always set for other MappedTextBox widgets until blur - return this.valueNode.value; - }, - - _getValueField: function(){ - // Overrides ComboBox._getValueField() - return "value"; - }, - - _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange){ - // summary: - // Hook so attr('value', value) works. - // description: - // Sets the value of the select. - // Also sets the label to the corresponding value by reverse lookup. - if(!this._onChangeActive){ priorityChange = null; } - this._lastQuery = value; - - if(value === null || value === ''){ - this._setDisplayedValueAttr('', priorityChange); - return; - } - - //#3347: fetchItemByIdentity if no keyAttr specified - var self = this; - this.store.fetchItemByIdentity({ - identity: value, - onItem: function(item){ - self._callbackSetLabel(item? [item] : [], undefined, priorityChange); - } - }); - }, - - _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){ - // summary: - // Set the displayed valued in the input box, and the hidden value - // that gets submitted, based on a dojo.data store item. - // description: - // Users shouldn't call this function; they should be calling - // attr('item', value) - // tags: - // private - this._isvalid = true; - this.inherited(arguments); - this.valueNode.value = this.value; - this._lastDisplayedValue = this.textbox.value; - }, - - _getDisplayQueryString: function(/*String*/ text){ - return text.replace(/([\\\*\?])/g, "\\$1"); - }, - - _setDisplayedValueAttr: function(/*String*/ label, /*Boolean?*/ priorityChange){ - // summary: - // Hook so attr('displayedValue', label) works. - // description: - // Sets textbox to display label. Also performs reverse lookup - // to set the hidden value. - - // When this is called during initialization it'll ping the datastore - // for reverse lookup, and when that completes (after an XHR request) - // will call setValueAttr()... but that shouldn't trigger an onChange() - // event, even when it happens after creation has finished - if(!this._created){ - priorityChange = false; - } - - if(this.store){ - this._hideResultList(); - var query = dojo.clone(this.query); // #6196: populate query with user-specifics - // escape meta characters of dojo.data.util.filter.patternToRegExp(). - this._lastQuery = query[this.searchAttr] = this._getDisplayQueryString(label); - // if the label is not valid, the callback will never set it, - // so the last valid value will get the warning textbox set the - // textbox value now so that the impending warning will make - // sense to the user - this.textbox.value = label; - this._lastDisplayedValue = label; - var _this = this; - var fetch = { - query: query, - queryOptions: { - ignoreCase: this.ignoreCase, - deep: true - }, - onComplete: function(result, dataObject){ - _this._fetchHandle = null; - dojo.hitch(_this, "_callbackSetLabel")(result, dataObject, priorityChange); - }, - onError: function(errText){ - _this._fetchHandle = null; - console.error('dijit.form.FilteringSelect: ' + errText); - dojo.hitch(_this, "_callbackSetLabel")([], undefined, false); - } - }; - dojo.mixin(fetch, this.fetchProperties); - this._fetchHandle = this.store.fetch(fetch); - } - }, - - postMixInProperties: function(){ - this.inherited(arguments); - this._isvalid = !this.required; - }, - - undo: function(){ - this.set('displayedValue', this._lastDisplayedValue); - } - } -); - -} - -if(!dojo._hasResource["dojo.data.ItemFileReadStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.data.ItemFileReadStore"] = true; -dojo.provide("dojo.data.ItemFileReadStore"); - - - - - -dojo.declare("dojo.data.ItemFileReadStore", null,{ - // summary: - // The ItemFileReadStore implements the dojo.data.api.Read API and reads - // data from JSON files that have contents in this format -- - // { items: [ - // { name:'Kermit', color:'green', age:12, friends:['Gonzo', {_reference:{name:'Fozzie Bear'}}]}, - // { name:'Fozzie Bear', wears:['hat', 'tie']}, - // { name:'Miss Piggy', pets:'Foo-Foo'} - // ]} - // Note that it can also contain an 'identifer' property that specified which attribute on the items - // in the array of items that acts as the unique identifier for that item. - // - constructor: function(/* Object */ keywordParameters){ - // summary: constructor - // keywordParameters: {url: String} - // keywordParameters: {data: jsonObject} - // keywordParameters: {typeMap: object) - // The structure of the typeMap object is as follows: - // { - // type0: function || object, - // type1: function || object, - // ... - // typeN: function || object - // } - // Where if it is a function, it is assumed to be an object constructor that takes the - // value of _value as the initialization parameters. If it is an object, then it is assumed - // to be an object of general form: - // { - // type: function, //constructor. - // deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately. - // } - - this._arrayOfAllItems = []; - this._arrayOfTopLevelItems = []; - this._loadFinished = false; - this._jsonFileUrl = keywordParameters.url; - this._ccUrl = keywordParameters.url; - this.url = keywordParameters.url; - this._jsonData = keywordParameters.data; - this.data = null; - this._datatypeMap = keywordParameters.typeMap || {}; - if(!this._datatypeMap['Date']){ - //If no default mapping for dates, then set this as default. - //We use the dojo.date.stamp here because the ISO format is the 'dojo way' - //of generically representing dates. - this._datatypeMap['Date'] = { - type: Date, - deserialize: function(value){ - return dojo.date.stamp.fromISOString(value); - } - }; - } - this._features = {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true}; - this._itemsByIdentity = null; - this._storeRefPropName = "_S"; // Default name for the store reference to attach to every item. - this._itemNumPropName = "_0"; // Default Item Id for isItem to attach to every item. - this._rootItemPropName = "_RI"; // Default Item Id for isItem to attach to every item. - this._reverseRefMap = "_RRM"; // Default attribute for constructing a reverse reference map for use with reference integrity - this._loadInProgress = false; //Got to track the initial load to prevent duelling loads of the dataset. - this._queuedFetches = []; - if(keywordParameters.urlPreventCache !== undefined){ - this.urlPreventCache = keywordParameters.urlPreventCache?true:false; - } - if(keywordParameters.hierarchical !== undefined){ - this.hierarchical = keywordParameters.hierarchical?true:false; - } - if(keywordParameters.clearOnClose){ - this.clearOnClose = true; - } - if("failOk" in keywordParameters){ - this.failOk = keywordParameters.failOk?true:false; - } - }, - - url: "", // use "" rather than undefined for the benefit of the parser (#3539) - - //Internal var, crossCheckUrl. Used so that setting either url or _jsonFileUrl, can still trigger a reload - //when clearOnClose and close is used. - _ccUrl: "", - - data: null, // define this so that the parser can populate it - - typeMap: null, //Define so parser can populate. - - //Parameter to allow users to specify if a close call should force a reload or not. - //By default, it retains the old behavior of not clearing if close is called. But - //if set true, the store will be reset to default state. Note that by doing this, - //all item handles will become invalid and a new fetch must be issued. - clearOnClose: false, - - //Parameter to allow specifying if preventCache should be passed to the xhrGet call or not when loading data from a url. - //Note this does not mean the store calls the server on each fetch, only that the data load has preventCache set as an option. - //Added for tracker: #6072 - urlPreventCache: false, - - //Parameter for specifying that it is OK for the xhrGet call to fail silently. - failOk: false, - - //Parameter to indicate to process data from the url as hierarchical - //(data items can contain other data items in js form). Default is true - //for backwards compatibility. False means only root items are processed - //as items, all child objects outside of type-mapped objects and those in - //specific reference format, are left straight JS data objects. - hierarchical: true, - - _assertIsItem: function(/* item */ item){ - // summary: - // This function tests whether the item passed in is indeed an item in the store. - // item: - // The item to test for being contained by the store. - if(!this.isItem(item)){ - throw new Error("dojo.data.ItemFileReadStore: Invalid item argument."); - } - }, - - _assertIsAttribute: function(/* attribute-name-string */ attribute){ - // summary: - // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store. - // attribute: - // The attribute to test for being contained by the store. - if(typeof attribute !== "string"){ - throw new Error("dojo.data.ItemFileReadStore: Invalid attribute argument."); - } - }, - - getValue: function( /* item */ item, - /* attribute-name-string */ attribute, - /* value? */ defaultValue){ - // summary: - // See dojo.data.api.Read.getValue() - var values = this.getValues(item, attribute); - return (values.length > 0)?values[0]:defaultValue; // mixed - }, - - getValues: function(/* item */ item, - /* attribute-name-string */ attribute){ - // summary: - // See dojo.data.api.Read.getValues() - - this._assertIsItem(item); - this._assertIsAttribute(attribute); - // Clone it before returning. refs: #10474 - return (item[attribute] || []).slice(0); // Array - }, - - getAttributes: function(/* item */ item){ - // summary: - // See dojo.data.api.Read.getAttributes() - this._assertIsItem(item); - var attributes = []; - for(var key in item){ - // Save off only the real item attributes, not the special id marks for O(1) isItem. - if((key !== this._storeRefPropName) && (key !== this._itemNumPropName) && (key !== this._rootItemPropName) && (key !== this._reverseRefMap)){ - attributes.push(key); - } - } - return attributes; // Array - }, - - hasAttribute: function( /* item */ item, - /* attribute-name-string */ attribute){ - // summary: - // See dojo.data.api.Read.hasAttribute() - this._assertIsItem(item); - this._assertIsAttribute(attribute); - return (attribute in item); - }, - - containsValue: function(/* item */ item, - /* attribute-name-string */ attribute, - /* anything */ value){ - // summary: - // See dojo.data.api.Read.containsValue() - var regexp = undefined; - if(typeof value === "string"){ - regexp = dojo.data.util.filter.patternToRegExp(value, false); - } - return this._containsValue(item, attribute, value, regexp); //boolean. - }, - - _containsValue: function( /* item */ item, - /* attribute-name-string */ attribute, - /* anything */ value, - /* RegExp?*/ regexp){ - // summary: - // Internal function for looking at the values contained by the item. - // description: - // Internal function for looking at the values contained by the item. This - // function allows for denoting if the comparison should be case sensitive for - // strings or not (for handling filtering cases where string case should not matter) - // - // item: - // The data item to examine for attribute values. - // attribute: - // The attribute to inspect. - // value: - // The value to match. - // regexp: - // Optional regular expression generated off value if value was of string type to handle wildcarding. - // If present and attribute values are string, then it can be used for comparison instead of 'value' - return dojo.some(this.getValues(item, attribute), function(possibleValue){ - if(possibleValue !== null && !dojo.isObject(possibleValue) && regexp){ - if(possibleValue.toString().match(regexp)){ - return true; // Boolean - } - }else if(value === possibleValue){ - return true; // Boolean - } - }); - }, - - isItem: function(/* anything */ something){ - // summary: - // See dojo.data.api.Read.isItem() - if(something && something[this._storeRefPropName] === this){ - if(this._arrayOfAllItems[something[this._itemNumPropName]] === something){ - return true; - } - } - return false; // Boolean - }, - - isItemLoaded: function(/* anything */ something){ - // summary: - // See dojo.data.api.Read.isItemLoaded() - return this.isItem(something); //boolean - }, - - loadItem: function(/* object */ keywordArgs){ - // summary: - // See dojo.data.api.Read.loadItem() - this._assertIsItem(keywordArgs.item); - }, - - getFeatures: function(){ - // summary: - // See dojo.data.api.Read.getFeatures() - return this._features; //Object - }, - - getLabel: function(/* item */ item){ - // summary: - // See dojo.data.api.Read.getLabel() - if(this._labelAttr && this.isItem(item)){ - return this.getValue(item,this._labelAttr); //String - } - return undefined; //undefined - }, - - getLabelAttributes: function(/* item */ item){ - // summary: - // See dojo.data.api.Read.getLabelAttributes() - if(this._labelAttr){ - return [this._labelAttr]; //array - } - return null; //null - }, - - _fetchItems: function( /* Object */ keywordArgs, - /* Function */ findCallback, - /* Function */ errorCallback){ - // summary: - // See dojo.data.util.simpleFetch.fetch() - var self = this, - filter = function(requestArgs, arrayOfItems){ - var items = [], - i, key; - if(requestArgs.query){ - var value, - ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false; - - //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the - //same value for each item examined. Much more efficient. - var regexpList = {}; - for(key in requestArgs.query){ - value = requestArgs.query[key]; - if(typeof value === "string"){ - regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase); - }else if(value instanceof RegExp){ - regexpList[key] = value; - } - } - for(i = 0; i < arrayOfItems.length; ++i){ - var match = true; - var candidateItem = arrayOfItems[i]; - if(candidateItem === null){ - match = false; - }else{ - for(key in requestArgs.query){ - value = requestArgs.query[key]; - if(!self._containsValue(candidateItem, key, value, regexpList[key])){ - match = false; - } - } - } - if(match){ - items.push(candidateItem); - } - } - findCallback(items, requestArgs); - }else{ - // We want a copy to pass back in case the parent wishes to sort the array. - // We shouldn't allow resort of the internal list, so that multiple callers - // can get lists and sort without affecting each other. We also need to - // filter out any null values that have been left as a result of deleteItem() - // calls in ItemFileWriteStore. - for(i = 0; i < arrayOfItems.length; ++i){ - var item = arrayOfItems[i]; - if(item !== null){ - items.push(item); - } - } - findCallback(items, requestArgs); - } - }; - - if(this._loadFinished){ - filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions)); - }else{ - //Do a check on the JsonFileUrl and crosscheck it. - //If it doesn't match the cross-check, it needs to be updated - //This allows for either url or _jsonFileUrl to he changed to - //reset the store load location. Done this way for backwards - //compatibility. People use _jsonFileUrl (even though officially - //private. - if(this._jsonFileUrl !== this._ccUrl){ - dojo.deprecated("dojo.data.ItemFileReadStore: ", - "To change the url, set the url property of the store," + - " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0"); - this._ccUrl = this._jsonFileUrl; - this.url = this._jsonFileUrl; - }else if(this.url !== this._ccUrl){ - this._jsonFileUrl = this.url; - this._ccUrl = this.url; - } - - //See if there was any forced reset of data. - if(this.data != null && this._jsonData == null){ - this._jsonData = this.data; - this.data = null; - } - - if(this._jsonFileUrl){ - //If fetches come in before the loading has finished, but while - //a load is in progress, we have to defer the fetching to be - //invoked in the callback. - if(this._loadInProgress){ - this._queuedFetches.push({args: keywordArgs, filter: filter}); - }else{ - this._loadInProgress = true; - var getArgs = { - url: self._jsonFileUrl, - handleAs: "json-comment-optional", - preventCache: this.urlPreventCache, - failOk: this.failOk - }; - var getHandler = dojo.xhrGet(getArgs); - getHandler.addCallback(function(data){ - try{ - self._getItemsFromLoadedData(data); - self._loadFinished = true; - self._loadInProgress = false; - - filter(keywordArgs, self._getItemsArray(keywordArgs.queryOptions)); - self._handleQueuedFetches(); - }catch(e){ - self._loadFinished = true; - self._loadInProgress = false; - errorCallback(e, keywordArgs); - } - }); - getHandler.addErrback(function(error){ - self._loadInProgress = false; - errorCallback(error, keywordArgs); - }); - - //Wire up the cancel to abort of the request - //This call cancel on the deferred if it hasn't been called - //yet and then will chain to the simple abort of the - //simpleFetch keywordArgs - var oldAbort = null; - if(keywordArgs.abort){ - oldAbort = keywordArgs.abort; - } - keywordArgs.abort = function(){ - var df = getHandler; - if(df && df.fired === -1){ - df.cancel(); - df = null; - } - if(oldAbort){ - oldAbort.call(keywordArgs); - } - }; - } - }else if(this._jsonData){ - try{ - this._loadFinished = true; - this._getItemsFromLoadedData(this._jsonData); - this._jsonData = null; - filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions)); - }catch(e){ - errorCallback(e, keywordArgs); - } - }else{ - errorCallback(new Error("dojo.data.ItemFileReadStore: No JSON source data was provided as either URL or a nested Javascript object."), keywordArgs); - } - } - }, - - _handleQueuedFetches: function(){ - // summary: - // Internal function to execute delayed request in the store. - //Execute any deferred fetches now. - if(this._queuedFetches.length > 0){ - for(var i = 0; i < this._queuedFetches.length; i++){ - var fData = this._queuedFetches[i], - delayedQuery = fData.args, - delayedFilter = fData.filter; - if(delayedFilter){ - delayedFilter(delayedQuery, this._getItemsArray(delayedQuery.queryOptions)); - }else{ - this.fetchItemByIdentity(delayedQuery); - } - } - this._queuedFetches = []; - } - }, - - _getItemsArray: function(/*object?*/queryOptions){ - // summary: - // Internal function to determine which list of items to search over. - // queryOptions: The query options parameter, if any. - if(queryOptions && queryOptions.deep){ - return this._arrayOfAllItems; - } - return this._arrayOfTopLevelItems; - }, - - close: function(/*dojo.data.api.Request || keywordArgs || null */ request){ - // summary: - // See dojo.data.api.Read.close() - if(this.clearOnClose && - this._loadFinished && - !this._loadInProgress){ - //Reset all internalsback to default state. This will force a reload - //on next fetch. This also checks that the data or url param was set - //so that the store knows it can get data. Without one of those being set, - //the next fetch will trigger an error. - - if(((this._jsonFileUrl == "" || this._jsonFileUrl == null) && - (this.url == "" || this.url == null) - ) && this.data == null){ - console.debug("dojo.data.ItemFileReadStore: WARNING! Data reload " + - " information has not been provided." + - " Please set 'url' or 'data' to the appropriate value before" + - " the next fetch"); - } - this._arrayOfAllItems = []; - this._arrayOfTopLevelItems = []; - this._loadFinished = false; - this._itemsByIdentity = null; - this._loadInProgress = false; - this._queuedFetches = []; - } - }, - - _getItemsFromLoadedData: function(/* Object */ dataObject){ - // summary: - // Function to parse the loaded data into item format and build the internal items array. - // description: - // Function to parse the loaded data into item format and build the internal items array. - // - // dataObject: - // The JS data object containing the raw data to convery into item format. - // - // returns: array - // Array of items in store item format. - - // First, we define a couple little utility functions... - var addingArrays = false, - self = this; - - function valueIsAnItem(/* anything */ aValue){ - // summary: - // Given any sort of value that could be in the raw json data, - // return true if we should interpret the value as being an - // item itself, rather than a literal value or a reference. - // example: - // | false == valueIsAnItem("Kermit"); - // | false == valueIsAnItem(42); - // | false == valueIsAnItem(new Date()); - // | false == valueIsAnItem({_type:'Date', _value:'May 14, 1802'}); - // | false == valueIsAnItem({_reference:'Kermit'}); - // | true == valueIsAnItem({name:'Kermit', color:'green'}); - // | true == valueIsAnItem({iggy:'pop'}); - // | true == valueIsAnItem({foo:42}); - var isItem = ( - (aValue !== null) && - (typeof aValue === "object") && - (!dojo.isArray(aValue) || addingArrays) && - (!dojo.isFunction(aValue)) && - (aValue.constructor == Object || dojo.isArray(aValue)) && - (typeof aValue._reference === "undefined") && - (typeof aValue._type === "undefined") && - (typeof aValue._value === "undefined") && - self.hierarchical - ); - return isItem; - } - - function addItemAndSubItemsToArrayOfAllItems(/* Item */ anItem){ - self._arrayOfAllItems.push(anItem); - for(var attribute in anItem){ - var valueForAttribute = anItem[attribute]; - if(valueForAttribute){ - if(dojo.isArray(valueForAttribute)){ - var valueArray = valueForAttribute; - for(var k = 0; k < valueArray.length; ++k){ - var singleValue = valueArray[k]; - if(valueIsAnItem(singleValue)){ - addItemAndSubItemsToArrayOfAllItems(singleValue); - } - } - }else{ - if(valueIsAnItem(valueForAttribute)){ - addItemAndSubItemsToArrayOfAllItems(valueForAttribute); - } - } - } - } - } - - this._labelAttr = dataObject.label; - - // We need to do some transformations to convert the data structure - // that we read from the file into a format that will be convenient - // to work with in memory. - - // Step 1: Walk through the object hierarchy and build a list of all items - var i, - item; - this._arrayOfAllItems = []; - this._arrayOfTopLevelItems = dataObject.items; - - for(i = 0; i < this._arrayOfTopLevelItems.length; ++i){ - item = this._arrayOfTopLevelItems[i]; - if(dojo.isArray(item)){ - addingArrays = true; - } - addItemAndSubItemsToArrayOfAllItems(item); - item[this._rootItemPropName]=true; - } - - // Step 2: Walk through all the attribute values of all the items, - // and replace single values with arrays. For example, we change this: - // { name:'Miss Piggy', pets:'Foo-Foo'} - // into this: - // { name:['Miss Piggy'], pets:['Foo-Foo']} - // - // We also store the attribute names so we can validate our store - // reference and item id special properties for the O(1) isItem - var allAttributeNames = {}, - key; - - for(i = 0; i < this._arrayOfAllItems.length; ++i){ - item = this._arrayOfAllItems[i]; - for(key in item){ - if(key !== this._rootItemPropName){ - var value = item[key]; - if(value !== null){ - if(!dojo.isArray(value)){ - item[key] = [value]; - } - }else{ - item[key] = [null]; - } - } - allAttributeNames[key]=key; - } - } - - // Step 3: Build unique property names to use for the _storeRefPropName and _itemNumPropName - // This should go really fast, it will generally never even run the loop. - while(allAttributeNames[this._storeRefPropName]){ - this._storeRefPropName += "_"; - } - while(allAttributeNames[this._itemNumPropName]){ - this._itemNumPropName += "_"; - } - while(allAttributeNames[this._reverseRefMap]){ - this._reverseRefMap += "_"; - } - - // Step 4: Some data files specify an optional 'identifier', which is - // the name of an attribute that holds the identity of each item. - // If this data file specified an identifier attribute, then build a - // hash table of items keyed by the identity of the items. - var arrayOfValues; - - var identifier = dataObject.identifier; - if(identifier){ - this._itemsByIdentity = {}; - this._features['dojo.data.api.Identity'] = identifier; - for(i = 0; i < this._arrayOfAllItems.length; ++i){ - item = this._arrayOfAllItems[i]; - arrayOfValues = item[identifier]; - var identity = arrayOfValues[0]; - if(!this._itemsByIdentity[identity]){ - this._itemsByIdentity[identity] = item; - }else{ - if(this._jsonFileUrl){ - throw new Error("dojo.data.ItemFileReadStore: The json data as specified by: [" + this._jsonFileUrl + "] is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]"); - }else if(this._jsonData){ - throw new Error("dojo.data.ItemFileReadStore: The json data provided by the creation arguments is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]"); - } - } - } - }else{ - this._features['dojo.data.api.Identity'] = Number; - } - - // Step 5: Walk through all the items, and set each item's properties - // for _storeRefPropName and _itemNumPropName, so that store.isItem() will return true. - for(i = 0; i < this._arrayOfAllItems.length; ++i){ - item = this._arrayOfAllItems[i]; - item[this._storeRefPropName] = this; - item[this._itemNumPropName] = i; - } - - // Step 6: We walk through all the attribute values of all the items, - // looking for type/value literals and item-references. - // - // We replace item-references with pointers to items. For example, we change: - // { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] } - // into this: - // { name:['Kermit'], friends:[miss_piggy] } - // (where miss_piggy is the object representing the 'Miss Piggy' item). - // - // We replace type/value pairs with typed-literals. For example, we change: - // { name:['Nelson Mandela'], born:[{_type:'Date', _value:'July 18, 1918'}] } - // into this: - // { name:['Kermit'], born:(new Date('July 18, 1918')) } - // - // We also generate the associate map for all items for the O(1) isItem function. - for(i = 0; i < this._arrayOfAllItems.length; ++i){ - item = this._arrayOfAllItems[i]; // example: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] } - for(key in item){ - arrayOfValues = item[key]; // example: [{_reference:{name:'Miss Piggy'}}] - for(var j = 0; j < arrayOfValues.length; ++j){ - value = arrayOfValues[j]; // example: {_reference:{name:'Miss Piggy'}} - if(value !== null && typeof value == "object"){ - if(("_type" in value) && ("_value" in value)){ - var type = value._type; // examples: 'Date', 'Color', or 'ComplexNumber' - var mappingObj = this._datatypeMap[type]; // examples: Date, dojo.Color, foo.math.ComplexNumber, {type: dojo.Color, deserialize(value){ return new dojo.Color(value)}} - if(!mappingObj){ - throw new Error("dojo.data.ItemFileReadStore: in the typeMap constructor arg, no object class was specified for the datatype '" + type + "'"); - }else if(dojo.isFunction(mappingObj)){ - arrayOfValues[j] = new mappingObj(value._value); - }else if(dojo.isFunction(mappingObj.deserialize)){ - arrayOfValues[j] = mappingObj.deserialize(value._value); - }else{ - throw new Error("dojo.data.ItemFileReadStore: Value provided in typeMap was neither a constructor, nor a an object with a deserialize function"); - } - } - if(value._reference){ - var referenceDescription = value._reference; // example: {name:'Miss Piggy'} - if(!dojo.isObject(referenceDescription)){ - // example: 'Miss Piggy' - // from an item like: { name:['Kermit'], friends:[{_reference:'Miss Piggy'}]} - arrayOfValues[j] = this._getItemByIdentity(referenceDescription); - }else{ - // example: {name:'Miss Piggy'} - // from an item like: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] } - for(var k = 0; k < this._arrayOfAllItems.length; ++k){ - var candidateItem = this._arrayOfAllItems[k], - found = true; - for(var refKey in referenceDescription){ - if(candidateItem[refKey] != referenceDescription[refKey]){ - found = false; - } - } - if(found){ - arrayOfValues[j] = candidateItem; - } - } - } - if(this.referenceIntegrity){ - var refItem = arrayOfValues[j]; - if(this.isItem(refItem)){ - this._addReferenceToMap(refItem, item, key); - } - } - }else if(this.isItem(value)){ - //It's a child item (not one referenced through _reference). - //We need to treat this as a referenced item, so it can be cleaned up - //in a write store easily. - if(this.referenceIntegrity){ - this._addReferenceToMap(value, item, key); - } - } - } - } - } - } - }, - - _addReferenceToMap: function(/*item*/ refItem, /*item*/ parentItem, /*string*/ attribute){ - // summary: - // Method to add an reference map entry for an item and attribute. - // description: - // Method to add an reference map entry for an item and attribute. // - // refItem: - // The item that is referenced. - // parentItem: - // The item that holds the new reference to refItem. - // attribute: - // The attribute on parentItem that contains the new reference. - - //Stub function, does nothing. Real processing is in ItemFileWriteStore. - }, - - getIdentity: function(/* item */ item){ - // summary: - // See dojo.data.api.Identity.getIdentity() - var identifier = this._features['dojo.data.api.Identity']; - if(identifier === Number){ - return item[this._itemNumPropName]; // Number - }else{ - var arrayOfValues = item[identifier]; - if(arrayOfValues){ - return arrayOfValues[0]; // Object || String - } - } - return null; // null - }, - - fetchItemByIdentity: function(/* Object */ keywordArgs){ - // summary: - // See dojo.data.api.Identity.fetchItemByIdentity() - - // Hasn't loaded yet, we have to trigger the load. - var item, - scope; - if(!this._loadFinished){ - var self = this; - //Do a check on the JsonFileUrl and crosscheck it. - //If it doesn't match the cross-check, it needs to be updated - //This allows for either url or _jsonFileUrl to he changed to - //reset the store load location. Done this way for backwards - //compatibility. People use _jsonFileUrl (even though officially - //private. - if(this._jsonFileUrl !== this._ccUrl){ - dojo.deprecated("dojo.data.ItemFileReadStore: ", - "To change the url, set the url property of the store," + - " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0"); - this._ccUrl = this._jsonFileUrl; - this.url = this._jsonFileUrl; - }else if(this.url !== this._ccUrl){ - this._jsonFileUrl = this.url; - this._ccUrl = this.url; - } - - //See if there was any forced reset of data. - if(this.data != null && this._jsonData == null){ - this._jsonData = this.data; - this.data = null; - } - - if(this._jsonFileUrl){ - - if(this._loadInProgress){ - this._queuedFetches.push({args: keywordArgs}); - }else{ - this._loadInProgress = true; - var getArgs = { - url: self._jsonFileUrl, - handleAs: "json-comment-optional", - preventCache: this.urlPreventCache, - failOk: this.failOk - }; - var getHandler = dojo.xhrGet(getArgs); - getHandler.addCallback(function(data){ - var scope = keywordArgs.scope?keywordArgs.scope:dojo.global; - try{ - self._getItemsFromLoadedData(data); - self._loadFinished = true; - self._loadInProgress = false; - item = self._getItemByIdentity(keywordArgs.identity); - if(keywordArgs.onItem){ - keywordArgs.onItem.call(scope, item); - } - self._handleQueuedFetches(); - }catch(error){ - self._loadInProgress = false; - if(keywordArgs.onError){ - keywordArgs.onError.call(scope, error); - } - } - }); - getHandler.addErrback(function(error){ - self._loadInProgress = false; - if(keywordArgs.onError){ - var scope = keywordArgs.scope?keywordArgs.scope:dojo.global; - keywordArgs.onError.call(scope, error); - } - }); - } - - }else if(this._jsonData){ - // Passed in data, no need to xhr. - self._getItemsFromLoadedData(self._jsonData); - self._jsonData = null; - self._loadFinished = true; - item = self._getItemByIdentity(keywordArgs.identity); - if(keywordArgs.onItem){ - scope = keywordArgs.scope?keywordArgs.scope:dojo.global; - keywordArgs.onItem.call(scope, item); - } - } - }else{ - // Already loaded. We can just look it up and call back. - item = this._getItemByIdentity(keywordArgs.identity); - if(keywordArgs.onItem){ - scope = keywordArgs.scope?keywordArgs.scope:dojo.global; - keywordArgs.onItem.call(scope, item); - } - } - }, - - _getItemByIdentity: function(/* Object */ identity){ - // summary: - // Internal function to look an item up by its identity map. - var item = null; - if(this._itemsByIdentity){ - item = this._itemsByIdentity[identity]; - }else{ - item = this._arrayOfAllItems[identity]; - } - if(item === undefined){ - item = null; - } - return item; // Object - }, - - getIdentityAttributes: function(/* item */ item){ - // summary: - // See dojo.data.api.Identity.getIdentifierAttributes() - - var identifier = this._features['dojo.data.api.Identity']; - if(identifier === Number){ - // If (identifier === Number) it means getIdentity() just returns - // an integer item-number for each item. The dojo.data.api.Identity - // spec says we need to return null if the identity is not composed - // of attributes - return null; // null - }else{ - return [identifier]; // Array - } - }, - - _forceLoad: function(){ - // summary: - // Internal function to force a load of the store if it hasn't occurred yet. This is required - // for specific functions to work properly. - var self = this; - //Do a check on the JsonFileUrl and crosscheck it. - //If it doesn't match the cross-check, it needs to be updated - //This allows for either url or _jsonFileUrl to he changed to - //reset the store load location. Done this way for backwards - //compatibility. People use _jsonFileUrl (even though officially - //private. - if(this._jsonFileUrl !== this._ccUrl){ - dojo.deprecated("dojo.data.ItemFileReadStore: ", - "To change the url, set the url property of the store," + - " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0"); - this._ccUrl = this._jsonFileUrl; - this.url = this._jsonFileUrl; - }else if(this.url !== this._ccUrl){ - this._jsonFileUrl = this.url; - this._ccUrl = this.url; - } - - //See if there was any forced reset of data. - if(this.data != null && this._jsonData == null){ - this._jsonData = this.data; - this.data = null; - } - - if(this._jsonFileUrl){ - var getArgs = { - url: this._jsonFileUrl, - handleAs: "json-comment-optional", - preventCache: this.urlPreventCache, - failOk: this.failOk, - sync: true - }; - var getHandler = dojo.xhrGet(getArgs); - getHandler.addCallback(function(data){ - try{ - //Check to be sure there wasn't another load going on concurrently - //So we don't clobber data that comes in on it. If there is a load going on - //then do not save this data. It will potentially clobber current data. - //We mainly wanted to sync/wait here. - //TODO: Revisit the loading scheme of this store to improve multi-initial - //request handling. - if(self._loadInProgress !== true && !self._loadFinished){ - self._getItemsFromLoadedData(data); - self._loadFinished = true; - }else if(self._loadInProgress){ - //Okay, we hit an error state we can't recover from. A forced load occurred - //while an async load was occurring. Since we cannot block at this point, the best - //that can be managed is to throw an error. - throw new Error("dojo.data.ItemFileReadStore: Unable to perform a synchronous load, an async load is in progress."); - } - }catch(e){ - console.log(e); - throw e; - } - }); - getHandler.addErrback(function(error){ - throw error; - }); - }else if(this._jsonData){ - self._getItemsFromLoadedData(self._jsonData); - self._jsonData = null; - self._loadFinished = true; - } - } -}); -//Mix in the simple fetch implementation to this class. -dojo.extend(dojo.data.ItemFileReadStore,dojo.data.util.simpleFetch); - -} - -if(!dojo._hasResource["dijit._editor.plugins.FontChoice"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit._editor.plugins.FontChoice"] = true; -dojo.provide("dijit._editor.plugins.FontChoice"); - - - - - - - - - - -dojo.declare("dijit._editor.plugins._FontDropDown", - [dijit._Widget, dijit._Templated],{ - // 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: "", - - // widgetsInTemplate: [public] boolean - // Over-ride denoting the template has widgets to parse. - widgetsInTemplate: true, - - // plainText: [public] boolean - // Flag to indicate that the returned label should be plain text - // instead of an example. - plainText: false, - - // templateString: [public] String - // The template used to construct the labeled dropdown. - templateString: - "<span style='white-space: nowrap' class='dijit dijitReset dijitInline'>" + - "<label class='dijitLeft dijitInline' for='${selectId}'>${label}</label>" + - "<input dojoType='dijit.form.FilteringSelect' required=false labelType=html labelAttr=label searchAttr=name " + - "tabIndex='-1' id='${selectId}' dojoAttachPoint='select' value=''/>" + - "</span>", - - postMixInProperties: function(){ - // summary: - // Over-ride to misin specific properties. - this.inherited(arguments); - - this.strings = dojo.i18n.getLocalization("dijit._editor", "FontChoice"); - - // Set some substitution variables used in the template - this.label = this.strings[this.command]; - this.id = dijit.getUniqueId(this.declaredClass.replace(/\./g,"_")); - this.selectId = this.id + "_select"; - - this.inherited(arguments); - }, - - postCreate: function(){ - // summary: - // Over-ride for the default postCreate action - // This establishes the filtering selects and the like. - - // Initialize the list of items in the drop down by creating data store with items like: - // {value: 1, name: "xx-small", label: "<font size=1>xx-small</font-size>" } - var items = dojo.map(this.values, function(value){ - var name = this.strings[value] || value; - return { - label: this.getLabel(value, name), - name: name, - value: value - }; - }, this); - - this.select.store = new dojo.data.ItemFileReadStore({ - data: { - identifier: "value", - items: items - } - }); - - 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?true:false; - this.select.set('value', dojo.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 retreiving 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); - } -}); - - -dojo.declare("dijit._editor.plugins._FontNameDropDown", dijit._editor.plugins._FontDropDown, { - // summary: - // Dropdown to select a font; goes in editor toolbar. - - // generic: Boolean - // Use generic (web standard) font names - generic: false, - - // command: [public] String - // The editor 'command' implemented by this plugin. - command: "fontName", - - postMixInProperties: function(){ - // summary: - // Over-ride for the default posr mixin control - if(!this.values){ - this.values = this.generic ? - ["serif", "sans-serif", "monospace", "cursive", "fantasy"] : // CSS font-family generics - ["Arial", "Times New Roman", "Comic Sans MS", "Courier New"]; - } - this.inherited(arguments); - }, - - getLabel: function(value, name){ - // summary: - // Function used to generate the labels of the format dropdown - // will return a formatted, or plain label based on the value - // of the plainText option. - // value: String - // The 'insert value' associated with a name - // name: String - // The text name of the value - if(this.plainText){ - return name; - }else{ - return "<div style='font-family: "+value+"'>" + name + "</div>"; - } - }, - - _setValueAttr: function(value, priorityChange){ - // summary: - // Over-ride for the default action of setting the - // widget value, maps the input to known values - - priorityChange = priorityChange !== false?true: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" -// ,"????": "fantasy" TODO: IE doesn't map fantasy font-family? - }; - value = map[value] || value; - } - this.inherited(arguments, [value, priorityChange]); - } -}); - -dojo.declare("dijit._editor.plugins._FontSizeDropDown", dijit._editor.plugins._FontDropDown, { - // summary: - // Dropdown to select a font size; goes in editor toolbar. - - // command: [public] String - // The editor 'command' implemented by this plugin. - command: "fontSize", - - // values: [public] Number[] - // The HTML font size values supported by this plugin - values: [1,2,3,4,5,6,7], // sizes according to the old HTML FONT SIZE - - getLabel: function(value, name){ - // summary: - // Function used to generate the labels of the format dropdown - // will return a formatted, or plain label based on the value - // of the plainText option. - // We're stuck using the deprecated FONT tag to correspond - // with the size measurements used by the editor - // value: String - // The 'insert value' associated with a name - // name: String - // The text name of the value - if(this.plainText){ - return name; - }else{ - return "<font size=" + value + "'>" + name + "</font>"; - } - }, - - _setValueAttr: function(value, priorityChange){ - // summary: - // Over-ride for the default action of setting the - // widget value, maps the input to known values - priorityChange = priorityChange !== false?true: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]); - } -}); - - -dojo.declare("dijit._editor.plugins._FormatBlockDropDown", dijit._editor.plugins._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){ - return name; - }else{ - return "<" + value + ">" + name + "</" + value + ">"; - } - }, - - _execCommand: function(editor, command, choice){ - // summary: - // Over-ride for default exec-command label. - // Allows us to treat 'none' as special. - if(choice === "noFormat"){ - var start; - var end; - var sel = dijit.range.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 = dojo.hitch(this, function(node, array){ - 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(dojo.withGlobal(editor.window, "inSelection", dijit._editor.selection, [c])){ - var tag = c.tagName? c.tagName.toLowerCase(): ""; - if(dojo.indexOf(this.values, tag) !== -1){ - array.push(c); - } - processChildren(c,array); - } - } - } - } - }); - - var unformatNodes = dojo.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(dojo.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(dojo.withGlobal(editor.window, "inSelection", dijit._editor.selection, [node])){ - if(node.nodeType == 1){ - tag = node.tagName? node.tagName.toLowerCase(): ""; - if(dojo.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){ - dojo.place(node.firstChild, node, "before"); - } - node.parentNode.removeChild(node); - }else{ - // Everyone else works fine this way, a paste-over and is native - // undo friendly. - dojo.withGlobal(editor.window, - "selectElementChildren", dijit._editor.selection, [node]); - var html = dojo.withGlobal(editor.window, - "getSelectedHtml", dijit._editor.selection, [null]); - dojo.withGlobal(editor.window, - "selectElement", dijit._editor.selection, [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) -dojo.declare("dijit._editor.plugins.FontChoice", dijit._editor._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] booleam - // 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: dijit._editor.plugins._FontNameDropDown, - fontSize: dijit._editor.plugins._FontSizeDropDown, - formatBlock: dijit._editor.plugins._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(dojo.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); - } - - // Enable custom undo for webkit, needed for noFormat to work properly - // and still undo. - this.editor.customUndo = this.editor.customUndo || dojo.isWebKit; - }); - }, - - 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 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 = dojo.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 = dijit.range.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 && dojo.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(dojo.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 this plugin. -dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){ - if(o.plugin){ return; } - switch(o.args.name){ - case "fontName": case "fontSize": case "formatBlock": - o.plugin = new dijit._editor.plugins.FontChoice({ - command: o.args.name, - plainText: o.args.plainText?o.args.plainText:false - }); - } -}); - -} - -if(!dojo._hasResource["dijit.form._FormSelectWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form._FormSelectWidget"] = true; -dojo.provide("dijit.form._FormSelectWidget"); - - - - -/*===== -dijit.form.__SelectOption = function(){ - // value: String - // The value of the option. Setting to empty (or missing) will - // place a separator at that location - // label: String - // The label for our option. It can contain html tags. - // selected: Boolean - // Whether or not we are a selected option - // disabled: Boolean - // Whether or not this specific option is disabled - this.value = value; - this.label = label; - this.selected = selected; - this.disabled = disabled; -} -=====*/ - -dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, { - // summary: - // Extends _FormValueWidget in order to provide "select-specific" - // values - i.e., those values that are unique to <select> elements. - // This also provides the mechanism for reading the elements from - // a store, if desired. - - // multiple: Boolean - // Whether or not we are multi-valued - multiple: false, - - // options: dijit.form.__SelectOption[] - // The set of options for our select item. Roughly corresponds to - // the html <option> tag. - options: null, - - // store: dojo.data.api.Identity - // A store which, at the very least impelements dojo.data.api.Identity - // to use for getting our list of options - rather than reading them - // from the <option> html tags. - store: null, - - // query: object - // A query to use when fetching items from our store - query: null, - - // queryOptions: object - // Query options to use when fetching from the store - queryOptions: null, - - // onFetch: Function - // A callback to do with an onFetch - but before any items are actually - // iterated over (i.e. to filter even futher what you want to add) - onFetch: null, - - // sortByLabel: boolean - // Flag to sort the options returned from a store by the label of - // the store. - sortByLabel: true, - - - // loadChildrenOnOpen: boolean - // By default loadChildren is called when the items are fetched from the - // store. This property allows delaying loadChildren (and the creation - // of the options/menuitems) until the user opens the click the button. - // dropdown - loadChildrenOnOpen: false, - - getOptions: function(/* anything */ valueOrIdx){ - // summary: - // Returns a given option (or options). - // valueOrIdx: - // If passed in as a string, that string is used to look up the option - // in the array of options - based on the value property. - // (See dijit.form.__SelectOption). - // - // If passed in a number, then the option with the given index (0-based) - // within this select will be returned. - // - // If passed in a dijit.form.__SelectOption, the same option will be - // returned if and only if it exists within this select. - // - // If passed an array, then an array will be returned with each element - // in the array being looked up. - // - // If not passed a value, then all options will be returned - // - // returns: - // The option corresponding with the given value or index. null - // is returned if any of the following are true: - // - A string value is passed in which doesn't exist - // - An index is passed in which is outside the bounds of the array of options - // - A dijit.form.__SelectOption is passed in which is not a part of the select - - // NOTE: the compare for passing in a dijit.form.__SelectOption checks - // if the value property matches - NOT if the exact option exists - // NOTE: if passing in an array, null elements will be placed in the returned - // array when a value is not found. - var lookupValue = valueOrIdx, opts = this.options || [], l = opts.length; - - if(lookupValue === undefined){ - return opts; // dijit.form.__SelectOption[] - } - if(dojo.isArray(lookupValue)){ - return dojo.map(lookupValue, "return this.getOptions(item);", this); // dijit.form.__SelectOption[] - } - if(dojo.isObject(valueOrIdx)){ - // We were passed an option - so see if it's in our array (directly), - // and if it's not, try and find it by value. - if(!dojo.some(this.options, function(o, idx){ - if(o === lookupValue || - (o.value && o.value === lookupValue.value)){ - lookupValue = idx; - return true; - } - return false; - })){ - lookupValue = -1; - } - } - if(typeof lookupValue == "string"){ - for(var i=0; i<l; i++){ - if(opts[i].value === lookupValue){ - lookupValue = i; - break; - } - } - } - if(typeof lookupValue == "number" && lookupValue >= 0 && lookupValue < l){ - return this.options[lookupValue] // dijit.form.__SelectOption - } - return null; // null - }, - - addOption: function(/* dijit.form.__SelectOption, dijit.form.__SelectOption[] */ option){ - // summary: - // Adds an option or options to the end of the select. If value - // of the option is empty or missing, a separator is created instead. - // Passing in an array of options will yield slightly better performance - // since the children are only loaded once. - if(!dojo.isArray(option)){ option = [option]; } - dojo.forEach(option, function(i){ - if(i && dojo.isObject(i)){ - this.options.push(i); - } - }, this); - this._loadChildren(); - }, - - removeOption: function(/* string, dijit.form.__SelectOption, number, or array */ valueOrIdx){ - // summary: - // Removes the given option or options. You can remove by string - // (in which case the value is removed), number (in which case the - // index in the options array is removed), or select option (in - // which case, the select option with a matching value is removed). - // You can also pass in an array of those values for a slightly - // better performance since the children are only loaded once. - if(!dojo.isArray(valueOrIdx)){ valueOrIdx = [valueOrIdx]; } - var oldOpts = this.getOptions(valueOrIdx); - dojo.forEach(oldOpts, function(i){ - // We can get null back in our array - if our option was not found. In - // that case, we don't want to blow up... - if(i){ - this.options = dojo.filter(this.options, function(node, idx){ - return (node.value !== i.value); - }); - this._removeOptionItem(i); - } - }, this); - this._loadChildren(); - }, - - updateOption: function(/* dijit.form.__SelectOption, dijit.form.__SelectOption[] */ newOption){ - // summary: - // Updates the values of the given option. The option to update - // is matched based on the value of the entered option. Passing - // in an array of new options will yeild better performance since - // the children will only be loaded once. - if(!dojo.isArray(newOption)){ newOption = [newOption]; } - dojo.forEach(newOption, function(i){ - var oldOpt = this.getOptions(i), k; - if(oldOpt){ - for(k in i){ oldOpt[k] = i[k]; } - } - }, this); - this._loadChildren(); - }, - - setStore: function(/* dojo.data.api.Identity */ store, - /* anything? */ selectedValue, - /* Object? */ fetchArgs){ - // summary: - // Sets the store you would like to use with this select widget. - // The selected value is the value of the new store to set. This - // function returns the original store, in case you want to reuse - // it or something. - // store: dojo.data.api.Identity - // The store you would like to use - it MUST implement Identity, - // and MAY implement Notification. - // selectedValue: anything? - // The value that this widget should set itself to *after* the store - // has been loaded - // fetchArgs: Object? - // The arguments that will be passed to the store's fetch() function - var oStore = this.store; - fetchArgs = fetchArgs || {}; - if(oStore !== store){ - // Our store has changed, so update our notifications - dojo.forEach(this._notifyConnections || [], dojo.disconnect); - delete this._notifyConnections; - if(store && store.getFeatures()["dojo.data.api.Notification"]){ - this._notifyConnections = [ - dojo.connect(store, "onNew", this, "_onNewItem"), - dojo.connect(store, "onDelete", this, "_onDeleteItem"), - dojo.connect(store, "onSet", this, "_onSetItem") - ]; - } - this.store = store; - } - - // Turn off change notifications while we make all these changes - this._onChangeActive = false; - - // Remove existing options (if there are any) - if(this.options && this.options.length){ - this.removeOption(this.options); - } - - // Add our new options - if(store){ - var cb = function(items){ - if(this.sortByLabel && !fetchArgs.sort && items.length){ - items.sort(dojo.data.util.sorter.createSortFunction([{ - attribute: store.getLabelAttributes(items[0])[0] - }], store)); - } - - if(fetchArgs.onFetch){ - items = fetchArgs.onFetch(items); - } - // TODO: Add these guys as a batch, instead of separately - dojo.forEach(items, function(i){ - this._addOptionForItem(i); - }, this); - - // Set our value (which might be undefined), and then tweak - // it to send a change event with the real value - this._loadingStore = false; - this.set("value", (("_pendingValue" in this) ? this._pendingValue : selectedValue)); - delete this._pendingValue; - - if(!this.loadChildrenOnOpen){ - this._loadChildren(); - }else{ - this._pseudoLoadChildren(items); - } - this._fetchedWith = opts; - this._lastValueReported = this.multiple ? [] : null; - this._onChangeActive = true; - this.onSetStore(); - this._handleOnChange(this.value); - }; - var opts = dojo.mixin({onComplete:cb, scope: this}, fetchArgs); - this._loadingStore = true; - store.fetch(opts); - }else{ - delete this._fetchedWith; - } - return oStore; // dojo.data.api.Identity - }, - - _setValueAttr: function(/*anything*/ newValue, /*Boolean, optional*/ priorityChange){ - // summary: - // set the value of the widget. - // If a string is passed, then we set our value from looking it up. - if(this._loadingStore){ - // Our store is loading - so save our value, and we'll set it when - // we're done - this._pendingValue = newValue; - return; - } - var opts = this.getOptions() || []; - if(!dojo.isArray(newValue)){ - newValue = [newValue]; - } - dojo.forEach(newValue, function(i, idx){ - if(!dojo.isObject(i)){ - i = i + ""; - } - if(typeof i === "string"){ - newValue[idx] = dojo.filter(opts, function(node){ - return node.value === i; - })[0] || {value: "", label: ""}; - } - }, this); - - // Make sure some sane default is set - newValue = dojo.filter(newValue, function(i){ return i && i.value; }); - if(!this.multiple && (!newValue[0] || !newValue[0].value) && opts.length){ - newValue[0] = opts[0]; - } - dojo.forEach(opts, function(i){ - i.selected = dojo.some(newValue, function(v){ return v.value === i.value; }); - }); - var val = dojo.map(newValue, function(i){ return i.value; }), - disp = dojo.map(newValue, function(i){ return i.label; }); - - this.value = this.multiple ? val : val[0]; - this._setDisplay(this.multiple ? disp : disp[0]); - this._updateSelection(); - this._handleOnChange(this.value, priorityChange); - }, - - _getDisplayedValueAttr: function(){ - // summary: - // returns the displayed value of the widget - var val = this.get("value"); - if(!dojo.isArray(val)){ - val = [val]; - } - var ret = dojo.map(this.getOptions(val), function(v){ - if(v && "label" in v){ - return v.label; - }else if(v){ - return v.value; - } - return null; - }, this); - return this.multiple ? ret : ret[0]; - }, - - _getValueDeprecated: false, // remove when _FormWidget:getValue is removed - getValue: function(){ - // summary: - // get the value of the widget. - return this._lastValue; - }, - - undo: function(){ - // summary: - // restore the value to the last value passed to onChange - this._setValueAttr(this._lastValueReported, false); - }, - - _loadChildren: function(){ - // summary: - // Loads the children represented by this widget's options. - // reset the menu to make it "populatable on the next click - if(this._loadingStore){ return; } - dojo.forEach(this._getChildren(), function(child){ - child.destroyRecursive(); - }); - // Add each menu item - dojo.forEach(this.options, this._addOptionItem, this); - - // Update states - this._updateSelection(); - }, - - _updateSelection: function(){ - // summary: - // Sets the "selected" class on the item for styling purposes - this.value = this._getValueFromOpts(); - var val = this.value; - if(!dojo.isArray(val)){ - val = [val]; - } - if(val && val[0]){ - dojo.forEach(this._getChildren(), function(child){ - var isSelected = dojo.some(val, function(v){ - return child.option && (v === child.option.value); - }); - dojo.toggleClass(child.domNode, this.baseClass + "SelectedOption", isSelected); - dijit.setWaiState(child.domNode, "selected", isSelected); - }, this); - } - this._handleOnChange(this.value); - }, - - _getValueFromOpts: function(){ - // summary: - // Returns the value of the widget by reading the options for - // the selected flag - var opts = this.getOptions() || []; - if(!this.multiple && opts.length){ - // Mirror what a select does - choose the first one - var opt = dojo.filter(opts, function(i){ - return i.selected; - })[0]; - if(opt && opt.value){ - return opt.value - }else{ - opts[0].selected = true; - return opts[0].value; - } - }else if(this.multiple){ - // Set value to be the sum of all selected - return dojo.map(dojo.filter(opts, function(i){ - return i.selected; - }), function(i){ - return i.value; - }) || []; - } - return ""; - }, - - // Internal functions to call when we have store notifications come in - _onNewItem: function(/* item */ item, /* Object? */ parentInfo){ - if(!parentInfo || !parentInfo.parent){ - // Only add it if we are top-level - this._addOptionForItem(item); - } - }, - _onDeleteItem: function(/* item */ item){ - var store = this.store; - this.removeOption(store.getIdentity(item)); - }, - _onSetItem: function(/* item */ item){ - this.updateOption(this._getOptionObjForItem(item)); - }, - - _getOptionObjForItem: function(item){ - // summary: - // Returns an option object based off the given item. The "value" - // of the option item will be the identity of the item, the "label" - // of the option will be the label of the item. If the item contains - // children, the children value of the item will be set - var store = this.store, label = store.getLabel(item), - value = (label ? store.getIdentity(item) : null); - return {value: value, label: label, item:item}; // dijit.form.__SelectOption - }, - - _addOptionForItem: function(/* item */ item){ - // summary: - // Creates (and adds) the option for the given item - var store = this.store; - if(!store.isItemLoaded(item)){ - // We are not loaded - so let's load it and add later - store.loadItem({item: item, onComplete: function(i){ - this._addOptionForItem(item); - }, - scope: this}); - return; - } - var newOpt = this._getOptionObjForItem(item); - this.addOption(newOpt); - }, - - constructor: function(/* Object */ keywordArgs){ - // summary: - // Saves off our value, if we have an initial one set so we - // can use it if we have a store as well (see startup()) - this._oValue = (keywordArgs || {}).value || null; - }, - - _fillContent: function(){ - // summary: - // Loads our options and sets up our dropdown correctly. We - // don't want any content, so we don't call any inherit chain - // function. - var opts = this.options; - if(!opts){ - opts = this.options = this.srcNodeRef ? dojo.query(">", - this.srcNodeRef).map(function(node){ - if(node.getAttribute("type") === "separator"){ - return { value: "", label: "", selected: false, disabled: false }; - } - return { value: node.getAttribute("value"), - label: String(node.innerHTML), - selected: node.getAttribute("selected") || false, - disabled: node.getAttribute("disabled") || false }; - }, this) : []; - } - if(!this.value){ - this.value = this._getValueFromOpts(); - }else if(this.multiple && typeof this.value == "string"){ - this.value = this.value.split(","); - } - }, - - postCreate: function(){ - // summary: - // sets up our event handling that we need for functioning - // as a select - dojo.setSelectable(this.focusNode, false); - this.inherited(arguments); - - // Make our event connections for updating state - this.connect(this, "onChange", "_updateSelection"); - this.connect(this, "startup", "_loadChildren"); - - this._setValueAttr(this.value, null); - }, - - startup: function(){ - // summary: - // Connects in our store, if we have one defined - this.inherited(arguments); - var store = this.store, fetchArgs = {}; - dojo.forEach(["query", "queryOptions", "onFetch"], function(i){ - if(this[i]){ - fetchArgs[i] = this[i]; - } - delete this[i]; - }, this); - if(store && store.getFeatures()["dojo.data.api.Identity"]){ - // Temporarily set our store to null so that it will get set - // and connected appropriately - this.store = null; - this.setStore(store, this._oValue, fetchArgs); - } - }, - - destroy: function(){ - // summary: - // Clean up our connections - dojo.forEach(this._notifyConnections || [], dojo.disconnect); - this.inherited(arguments); - }, - - _addOptionItem: function(/* dijit.form.__SelectOption */ option){ - // summary: - // User-overridable function which, for the given option, adds an - // item to the select. If the option doesn't have a value, then a - // separator is added in that place. Make sure to store the option - // in the created option widget. - }, - - _removeOptionItem: function(/* dijit.form.__SelectOption */ option){ - // summary: - // User-overridable function which, for the given option, removes - // its item from the select. - }, - - _setDisplay: function(/*String or String[]*/ newDisplay){ - // summary: - // Overridable function which will set the display for the - // widget. newDisplay is either a string (in the case of - // single selects) or array of strings (in the case of multi-selects) - }, - - _getChildren: function(){ - // summary: - // Overridable function to return the children that this widget contains. - return []; - }, - - _getSelectedOptionsAttr: function(){ - // summary: - // hooks into this.attr to provide a mechanism for getting the - // option items for the current value of the widget. - return this.getOptions(this.get("value")); - }, - - _pseudoLoadChildren: function(/* item[] */ items){ - // summary: - // a function that will "fake" loading children, if needed, and - // if we have set to not load children until the widget opens. - // items: - // An array of items that will be loaded, when needed - }, - - onSetStore: function(){ - // summary: - // a function that can be connected to in order to receive a - // notification that the store has finished loading and all options - // from that store are available - } -}); - -} - -if(!dojo._hasResource["dijit.MenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.MenuItem"] = true; -dojo.provide("dijit.MenuItem"); - - - - - - -dojo.declare("dijit.MenuItem", - [dijit._Widget, dijit._Templated, dijit._Contained, dijit._CssStateMixin], - { - // summary: - // A line item in a Menu Widget - - // Make 3 columns - // icon, label, and expand arrow (BiDi-dependent) indicating sub-menu - templateString: dojo.cache("dijit", "templates/MenuItem.html", "<tr class=\"dijitReset dijitMenuItem\" dojoAttachPoint=\"focusNode\" waiRole=\"menuitem\" tabIndex=\"-1\"\n\t\tdojoAttachEvent=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" waiRole=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitMenuItemIcon\" dojoAttachPoint=\"iconNode\"/>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" dojoAttachPoint=\"containerNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" dojoAttachPoint=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" waiRole=\"presentation\">\n\t\t<div dojoAttachPoint=\"arrowWrapper\" style=\"visibility: hidden\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuExpand\"/>\n\t\t\t<span class=\"dijitMenuExpandA11y\">+</span>\n\t\t</div>\n\t</td>\n</tr>\n"), - - attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, { - label: { node: "containerNode", type: "innerHTML" }, - iconClass: { node: "iconNode", type: "class" } - }), - - baseClass: "dijitMenuItem", - - // label: String - // Menu text - label: '', - - // iconClass: String - // Class to apply to DOMNode to make it display an icon. - iconClass: "", - - // accelKey: String - // Text for the accelerator (shortcut) key combination. - // Note that although Menu can display accelerator keys there - // is no infrastructure to actually catch and execute these - // accelerators. - accelKey: "", - - // disabled: Boolean - // If true, the menu item is disabled. - // If false, the menu item is enabled. - disabled: false, - - _fillContent: function(/*DomNode*/ source){ - // If button label is specified as srcNodeRef.innerHTML rather than - // this.params.label, handle it here. - if(source && !("label" in this.params)){ - this.set('label', source.innerHTML); - } - }, - - postCreate: function(){ - this.inherited(arguments); - dojo.setSelectable(this.domNode, false); - var label = this.id+"_text"; - dojo.attr(this.containerNode, "id", label); - if(this.accelKeyNode){ - dojo.attr(this.accelKeyNode, "id", this.id + "_accel"); - label += " " + this.id + "_accel"; - } - dijit.setWaiState(this.domNode, "labelledby", label); - }, - - _onHover: function(){ - // summary: - // Handler when mouse is moved onto menu item - // tags: - // protected - this.getParent().onItemHover(this); - }, - - _onUnhover: function(){ - // summary: - // Handler when mouse is moved off of menu item, - // possibly to a child menu, or maybe to a sibling - // menuitem or somewhere else entirely. - // tags: - // protected - - // if we are unhovering the currently selected item - // then unselect it - this.getParent().onItemUnhover(this); - - // _onUnhover() is called when the menu is hidden (collapsed), due to clicking - // a MenuItem and having it execut. When that happens, FF and IE don't generate - // an onmouseout event for the MenuItem, so give _CssStateMixin some help - this._hovering = false; - this._setStateClass(); - }, - - _onClick: function(evt){ - // summary: - // Internal handler for click events on MenuItem. - // tags: - // private - this.getParent().onItemClick(this, evt); - dojo.stopEvent(evt); - }, - - onClick: function(/*Event*/ evt){ - // summary: - // User defined function to handle clicks - // tags: - // callback - }, - - focus: function(){ - // summary: - // Focus on this MenuItem - try{ - if(dojo.isIE == 8){ - // needed for IE8 which won't scroll TR tags into view on focus yet calling scrollIntoView creates flicker (#10275) - this.containerNode.focus(); - } - dijit.focus(this.focusNode); - }catch(e){ - // this throws on IE (at least) in some scenarios - } - }, - - _onFocus: function(){ - // summary: - // This is called by the focus manager when focus - // goes to this MenuItem or a child menu. - // tags: - // protected - this._setSelected(true); - this.getParent()._onItemFocus(this); - - this.inherited(arguments); - }, - - _setSelected: function(selected){ - // summary: - // Indicate that this node is the currently selected one - // tags: - // private - - /*** - * TODO: remove this method and calls to it, when _onBlur() is working for MenuItem. - * Currently _onBlur() gets called when focus is moved from the MenuItem to a child menu. - * That's not supposed to happen, but the problem is: - * In order to allow dijit.popup's getTopPopup() to work,a sub menu's popupParent - * points to the parent Menu, bypassing the parent MenuItem... thus the - * MenuItem is not in the chain of active widgets and gets a premature call to - * _onBlur() - */ - - dojo.toggleClass(this.domNode, "dijitMenuItemSelected", selected); - }, - - setLabel: function(/*String*/ content){ - // summary: - // Deprecated. Use set('label', ...) instead. - // tags: - // deprecated - dojo.deprecated("dijit.MenuItem.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0"); - this.set("label", content); - }, - - setDisabled: function(/*Boolean*/ disabled){ - // summary: - // Deprecated. Use set('disabled', bool) instead. - // tags: - // deprecated - dojo.deprecated("dijit.Menu.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0"); - this.set('disabled', disabled); - }, - _setDisabledAttr: function(/*Boolean*/ value){ - // summary: - // Hook for attr('disabled', ...) to work. - // Enable or disable this menu item. - this.disabled = value; - dijit.setWaiState(this.focusNode, 'disabled', value ? 'true' : 'false'); - }, - _setAccelKeyAttr: function(/*String*/ value){ - // summary: - // Hook for attr('accelKey', ...) to work. - // Set accelKey on this menu item. - this.accelKey=value; - - this.accelKeyNode.style.display=value?"":"none"; - this.accelKeyNode.innerHTML=value; - //have to use colSpan to make it work in IE - dojo.attr(this.containerNode,'colSpan',value?"1":"2"); - } - }); - -} - -if(!dojo._hasResource["dijit.PopupMenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.PopupMenuItem"] = true; -dojo.provide("dijit.PopupMenuItem"); - - - -dojo.declare("dijit.PopupMenuItem", - dijit.MenuItem, - { - _fillContent: function(){ - // summary: - // When Menu is declared in markup, this code gets the menu label and - // the popup widget from the srcNodeRef. - // description: - // srcNodeRefinnerHTML contains both the menu item text and a popup widget - // The first part holds the menu item text and the second part is the popup - // example: - // | <div dojoType="dijit.PopupMenuItem"> - // | <span>pick me</span> - // | <popup> ... </popup> - // | </div> - // tags: - // protected - - if(this.srcNodeRef){ - var nodes = dojo.query("*", this.srcNodeRef); - dijit.PopupMenuItem.superclass._fillContent.call(this, nodes[0]); - - // save pointer to srcNode so we can grab the drop down widget after it's instantiated - this.dropDownContainer = this.srcNodeRef; - } - }, - - startup: function(){ - if(this._started){ return; } - this.inherited(arguments); - - // we didn't copy the dropdown widget from the this.srcNodeRef, so it's in no-man's - // land now. move it to dojo.doc.body. - if(!this.popup){ - var node = dojo.query("[widgetId]", this.dropDownContainer)[0]; - this.popup = dijit.byNode(node); - } - dojo.body().appendChild(this.popup.domNode); - this.popup.startup(); - - this.popup.domNode.style.display="none"; - if(this.arrowWrapper){ - dojo.style(this.arrowWrapper, "visibility", ""); - } - dijit.setWaiState(this.focusNode, "haspopup", "true"); - }, - - destroyDescendants: function(){ - if(this.popup){ - // Destroy the popup, unless it's already been destroyed. This can happen because - // the popup is a direct child of <body> even though it's logically my child. - if(!this.popup._destroyed){ - this.popup.destroyRecursive(); - } - delete this.popup; - } - this.inherited(arguments); - } - }); - - -} - -if(!dojo._hasResource["dijit.CheckedMenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.CheckedMenuItem"] = true; -dojo.provide("dijit.CheckedMenuItem"); - - - -dojo.declare("dijit.CheckedMenuItem", - dijit.MenuItem, - { - // summary: - // A checkbox-like menu item for toggling on and off - - templateString: dojo.cache("dijit", "templates/CheckedMenuItem.html", "<tr class=\"dijitReset dijitMenuItem\" dojoAttachPoint=\"focusNode\" waiRole=\"menuitemcheckbox\" tabIndex=\"-1\"\n\t\tdojoAttachEvent=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" waiRole=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuItemIcon dijitCheckedMenuItemIcon\" dojoAttachPoint=\"iconNode\"/>\n\t\t<span class=\"dijitCheckedMenuItemIconChar\">✓</span>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" dojoAttachPoint=\"containerNode,labelNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" dojoAttachPoint=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" waiRole=\"presentation\"> </td>\n</tr>\n"), - - // checked: Boolean - // Our checked state - checked: false, - _setCheckedAttr: function(/*Boolean*/ checked){ - // summary: - // Hook so attr('checked', bool) works. - // Sets the class and state for the check box. - dojo.toggleClass(this.domNode, "dijitCheckedMenuItemChecked", checked); - dijit.setWaiState(this.domNode, "checked", checked); - this.checked = checked; - }, - - onChange: function(/*Boolean*/ checked){ - // summary: - // User defined function to handle check/uncheck events - // tags: - // callback - }, - - _onClick: function(/*Event*/ e){ - // summary: - // Clicking this item just toggles its state - // tags: - // private - if(!this.disabled){ - this.set("checked", !this.checked); - this.onChange(this.checked); - } - this.inherited(arguments); - } - }); - -} - -if(!dojo._hasResource["dijit.MenuSeparator"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.MenuSeparator"] = true; -dojo.provide("dijit.MenuSeparator"); - - - - - -dojo.declare("dijit.MenuSeparator", - [dijit._Widget, dijit._Templated, dijit._Contained], - { - // summary: - // A line between two menu items - - templateString: dojo.cache("dijit", "templates/MenuSeparator.html", "<tr class=\"dijitMenuSeparator\">\n\t<td class=\"dijitMenuSeparatorIconCell\">\n\t\t<div class=\"dijitMenuSeparatorTop\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n\t<td colspan=\"3\" class=\"dijitMenuSeparatorLabelCell\">\n\t\t<div class=\"dijitMenuSeparatorTop dijitMenuSeparatorLabel\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n</tr>\n"), - - postCreate: function(){ - dojo.setSelectable(this.domNode, false); - }, - - isFocusable: function(){ - // summary: - // Override to always return false - // tags: - // protected - - return false; // Boolean - } - }); - - -} - -if(!dojo._hasResource["dijit.Menu"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.Menu"] = true; -dojo.provide("dijit.Menu"); - - - - - - - -dojo.declare("dijit._MenuBase", - [dijit._Widget, dijit._Templated, dijit._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, - - startup: function(){ - if(this._started){ return; } - - dojo.forEach(this.getChildren(), function(child){ child.startup(); }); - this.startupKeyNavChildren(); - - this.inherited(arguments); - }, - - 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(dojo.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; - } - dijit.popup.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); - dijit.popup.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; - dijit.popup.open({ - parent: this, - popup: popup, - around: from_item.domNode, - orient: this._orient || (this.isLeftToRight() ? - {'TR': 'TL', 'TL': 'TR', 'BR': 'BL', 'BL': 'BR'} : - {'TL': 'TR', 'TR': 'TL', 'BL': 'BR', 'BR': 'BL'}), - 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: dojo.hitch(this, "_cleanUp") - }); - - this.currentPopup = popup; - // detect mouseovers to handle lazy mouse movements that temporarily focus other menu items - popup.connect(popup.domNode, "onmouseenter", dojo.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(dojo.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; - dojo.addClass(this.domNode, "dijitMenuActive"); - dojo.removeClass(this.domNode, "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 - dojo.removeClass(this.domNode, "dijitMenuActive"); - dojo.addClass(this.domNode, "dijitMenuPassive"); - }, - - 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.focusedChild){ // unhighlight the focused item - this.focusedChild._setSelected(false); - this.focusedChild._onUnhover(); - this.focusedChild = null; - } - if(this.currentPopup){ - // Close all popups that are open and descendants of this menu - dijit.popup.close(this.currentPopup); - this.currentPopup = 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(); - } - } -}); - -dojo.declare("dijit.Menu", - dijit._MenuBase, - { - // summary - // A context menu you can assign to multiple elements - - // TODO: most of the code in here is just for context menu (right-click menu) - // support. In retrospect that should have been a separate class (dijit.ContextMenu). - // Split them for 2.0 - - constructor: function(){ - this._bindings = []; - }, - - templateString: dojo.cache("dijit", "templates/Menu.html", "<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" waiRole=\"menu\" tabIndex=\"${tabIndex}\" dojoAttachEvent=\"onkeypress:_onKeyPress\" cellspacing=0>\n\t<tbody class=\"dijitReset\" dojoAttachPoint=\"containerNode\"></tbody>\n</table>\n"), - - baseClass: "dijitMenu", - - // targetNodeIds: [const] String[] - // Array of dom node ids of nodes to attach to. - // Fill this with nodeIds upon widget creation and it becomes context menu for those nodes. - targetNodeIds: [], - - // contextMenuForWindow: [const] Boolean - // If true, right clicking anywhere on the window will cause this context menu to open. - // If false, must specify targetNodeIds. - contextMenuForWindow: false, - - // leftClickToOpen: [const] Boolean - // If true, menu will open on left click instead of right click, similiar to a file menu. - leftClickToOpen: false, - - // refocus: Boolean - // When this menu closes, re-focus the element which had focus before it was opened. - refocus: true, - - postCreate: function(){ - if(this.contextMenuForWindow){ - this.bindDomNode(dojo.body()); - }else{ - // TODO: should have _setTargetNodeIds() method to handle initialization and a possible - // later attr('targetNodeIds', ...) call. There's also a problem that targetNodeIds[] - // gets stale after calls to bindDomNode()/unBindDomNode() as it still is just the original list (see #9610) - dojo.forEach(this.targetNodeIds, this.bindDomNode, this); - } - var k = dojo.keys, l = this.isLeftToRight(); - this._openSubMenuKey = l ? k.RIGHT_ARROW : k.LEFT_ARROW; - this._closeSubMenuKey = l ? k.LEFT_ARROW : k.RIGHT_ARROW; - this.connectKeyNavHandlers([k.UP_ARROW], [k.DOWN_ARROW]); - }, - - _onKeyPress: function(/*Event*/ evt){ - // summary: - // Handle keyboard based menu navigation. - // tags: - // protected - - if(evt.ctrlKey || evt.altKey){ return; } - - switch(evt.charOrCode){ - case this._openSubMenuKey: - this._moveToPopup(evt); - dojo.stopEvent(evt); - break; - case this._closeSubMenuKey: - if(this.parentMenu){ - if(this.parentMenu._isMenuBar){ - this.parentMenu.focusPrev(); - }else{ - this.onCancel(false); - } - }else{ - dojo.stopEvent(evt); - } - break; - } - }, - - // thanks burstlib! - _iframeContentWindow: function(/* HTMLIFrameElement */iframe_el){ - // summary: - // Returns the window reference of the passed iframe - // tags: - // private - var win = dojo.window.get(this._iframeContentDocument(iframe_el)) || - // Moz. TODO: is this available when defaultView isn't? - this._iframeContentDocument(iframe_el)['__parent__'] || - (iframe_el.name && dojo.doc.frames[iframe_el.name]) || null; - return win; // Window - }, - - _iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){ - // summary: - // Returns a reference to the document object inside iframe_el - // tags: - // protected - var doc = iframe_el.contentDocument // W3 - || (iframe_el.contentWindow && iframe_el.contentWindow.document) // IE - || (iframe_el.name && dojo.doc.frames[iframe_el.name] && dojo.doc.frames[iframe_el.name].document) - || null; - return doc; // HTMLDocument - }, - - bindDomNode: function(/*String|DomNode*/ node){ - // summary: - // Attach menu to given node - node = dojo.byId(node); - - var cn; // Connect node - - // Support context menus on iframes. Rather than binding to the iframe itself we need - // to bind to the <body> node inside the iframe. - if(node.tagName.toLowerCase() == "iframe"){ - var iframe = node, - win = this._iframeContentWindow(iframe); - cn = dojo.withGlobal(win, dojo.body); - }else{ - - // To capture these events at the top level, attach to <html>, not <body>. - // Otherwise right-click context menu just doesn't work. - cn = (node == dojo.body() ? dojo.doc.documentElement : node); - } - - - // "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode()) - var binding = { - node: node, - iframe: iframe - }; - - // Save info about binding in _bindings[], and make node itself record index(+1) into - // _bindings[] array. Prefix w/_dijitMenu to avoid setting an attribute that may - // start with a number, which fails on FF/safari. - dojo.attr(node, "_dijitMenu" + this.id, this._bindings.push(binding)); - - // Setup the connections to monitor click etc., unless we are connecting to an iframe which hasn't finished - // loading yet, in which case we need to wait for the onload event first, and then connect - // On linux Shift-F10 produces the oncontextmenu event, but on Windows it doesn't, so - // we need to monitor keyboard events in addition to the oncontextmenu event. - var doConnects = dojo.hitch(this, function(cn){ - return [ - // TODO: when leftClickToOpen is true then shouldn't space/enter key trigger the menu, - // rather than shift-F10? - dojo.connect(cn, this.leftClickToOpen ? "onclick" : "oncontextmenu", this, function(evt){ - // Schedule context menu to be opened unless it's already been scheduled from onkeydown handler - dojo.stopEvent(evt); - this._scheduleOpen(evt.target, iframe, {x: evt.pageX, y: evt.pageY}); - }), - dojo.connect(cn, "onkeydown", this, function(evt){ - if(evt.shiftKey && evt.keyCode == dojo.keys.F10){ - dojo.stopEvent(evt); - this._scheduleOpen(evt.target, iframe); // no coords - open near target node - } - }) - ]; - }); - binding.connects = cn ? doConnects(cn) : []; - - if(iframe){ - // Setup handler to [re]bind to the iframe when the contents are initially loaded, - // and every time the contents change. - // Need to do this b/c we are actually binding to the iframe's <body> node. - // Note: can't use dojo.connect(), see #9609. - - binding.onloadHandler = dojo.hitch(this, function(){ - // want to remove old connections, but IE throws exceptions when trying to - // access the <body> node because it's already gone, or at least in a state of limbo - - var win = this._iframeContentWindow(iframe); - cn = dojo.withGlobal(win, dojo.body); - binding.connects = doConnects(cn); - }); - if(iframe.addEventListener){ - iframe.addEventListener("load", binding.onloadHandler, false); - }else{ - iframe.attachEvent("onload", binding.onloadHandler); - } - } - }, - - unBindDomNode: function(/*String|DomNode*/ nodeName){ - // summary: - // Detach menu from given node - - var node; - try{ - node = dojo.byId(nodeName); - }catch(e){ - // On IE the dojo.byId() call will get an exception if the attach point was - // the <body> node of an <iframe> that has since been reloaded (and thus the - // <body> node is in a limbo state of destruction. - return; - } - - // node["_dijitMenu" + this.id] contains index(+1) into my _bindings[] array - var attrName = "_dijitMenu" + this.id; - if(node && dojo.hasAttr(node, attrName)){ - var bid = dojo.attr(node, attrName)-1, b = this._bindings[bid]; - dojo.forEach(b.connects, dojo.disconnect); - - // Remove listener for iframe onload events - var iframe = b.iframe; - if(iframe){ - if(iframe.removeEventListener){ - iframe.removeEventListener("load", b.onloadHandler, false); - }else{ - iframe.detachEvent("onload", b.onloadHandler); - } - } - - dojo.removeAttr(node, attrName); - delete this._bindings[bid]; - } - }, - - _scheduleOpen: function(/*DomNode?*/ target, /*DomNode?*/ iframe, /*Object?*/ coords){ - // summary: - // Set timer to display myself. Using a timer rather than displaying immediately solves - // two problems: - // - // 1. IE: without the delay, focus work in "open" causes the system - // context menu to appear in spite of stopEvent. - // - // 2. Avoid double-shows on linux, where shift-F10 generates an oncontextmenu event - // even after a dojo.stopEvent(e). (Shift-F10 on windows doesn't generate the - // oncontextmenu event.) - - if(!this._openTimer){ - this._openTimer = setTimeout(dojo.hitch(this, function(){ - delete this._openTimer; - this._openMyself({ - target: target, - iframe: iframe, - coords: coords - }); - }), 1); - } - }, - - _openMyself: function(args){ - // summary: - // Internal function for opening myself when the user does a right-click or something similar. - // args: - // This is an Object containing: - // * target: - // The node that is being clicked - // * iframe: - // If an <iframe> is being clicked, iframe points to that iframe - // * coords: - // Put menu at specified x/y position in viewport, or if iframe is - // specified, then relative to iframe. - // - // _openMyself() formerly took the event object, and since various code references - // evt.target (after connecting to _openMyself()), using an Object for parameters - // (so that old code still works). - - var target = args.target, - iframe = args.iframe, - coords = args.coords; - - // Get coordinates to open menu, either at specified (mouse) position or (if triggered via keyboard) - // then near the node the menu is assigned to. - if(coords){ - if(iframe){ - // Specified coordinates are on <body> node of an <iframe>, convert to match main document - var od = target.ownerDocument, - ifc = dojo.position(iframe, true), - win = this._iframeContentWindow(iframe), - scroll = dojo.withGlobal(win, "_docScroll", dojo); - - var cs = dojo.getComputedStyle(iframe), - tp = dojo._toPixelValue, - left = (dojo.isIE && dojo.isQuirks ? 0 : tp(iframe, cs.paddingLeft)) + (dojo.isIE && dojo.isQuirks ? tp(iframe, cs.borderLeftWidth) : 0), - top = (dojo.isIE && dojo.isQuirks ? 0 : tp(iframe, cs.paddingTop)) + (dojo.isIE && dojo.isQuirks ? tp(iframe, cs.borderTopWidth) : 0); - - coords.x += ifc.x + left - scroll.x; - coords.y += ifc.y + top - scroll.y; - } - }else{ - coords = dojo.position(target, true); - coords.x += 10; - coords.y += 10; - } - - var self=this; - var savedFocus = dijit.getFocus(this); - function closeAndRestoreFocus(){ - // user has clicked on a menu or popup - if(self.refocus){ - dijit.focus(savedFocus); - } - dijit.popup.close(self); - } - dijit.popup.open({ - popup: this, - x: coords.x, - y: coords.y, - onExecute: closeAndRestoreFocus, - onCancel: closeAndRestoreFocus, - orient: this.isLeftToRight() ? 'L' : 'R' - }); - this.focus(); - - this._onBlur = function(){ - this.inherited('_onBlur', arguments); - // Usually the parent closes the child widget but if this is a context - // menu then there is no parent - dijit.popup.close(this); - // don't try to restore focus; user has clicked another part of the screen - // and set focus there - }; - }, - - uninitialize: function(){ - dojo.forEach(this._bindings, function(b){ if(b){ this.unBindDomNode(b.node); } }, this); - this.inherited(arguments); - } -} -); - -// Back-compat (TODO: remove in 2.0) - - - - - - -} - -if(!dojo._hasResource["dijit.form.Select"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.Select"] = true; -dojo.provide("dijit.form.Select"); - - - - - - - - -dojo.declare("dijit.form._SelectMenu", dijit.Menu, { - // summary: - // An internally-used menu for dropdown that allows us a vertical scrollbar - buildRendering: function(){ - // summary: - // Stub in our own changes, so that our domNode is not a table - // otherwise, we won't respond correctly to heights/overflows - this.inherited(arguments); - var o = (this.menuTableNode = this.domNode); - var n = (this.domNode = dojo.create("div", {style: {overflowX: "hidden", overflowY: "scroll"}})); - if(o.parentNode){ - o.parentNode.replaceChild(n, o); - } - dojo.removeClass(o, "dijitMenuTable"); - n.className = o.className + " dijitSelectMenu"; - o.className = "dijitReset dijitMenuTable"; - dijit.setWaiRole(o,"listbox"); - dijit.setWaiRole(n,"presentation"); - n.appendChild(o); - }, - resize: function(/*Object*/ mb){ - // summary: - // Overridden so that we are able to handle resizing our - // internal widget. Note that this is not a "full" resize - // implementation - it only works correctly if you pass it a - // marginBox. - // - // mb: Object - // The margin box to set this dropdown to. - if(mb){ - dojo.marginBox(this.domNode, mb); - if("w" in mb){ - // We've explicitly set the wrapper <div>'s width, so set <table> width to match. - // 100% is safer than a pixel value because there may be a scroll bar with - // browser/OS specific width. - this.menuTableNode.style.width = "100%"; - } - } - } -}); - -dojo.declare("dijit.form.Select", [dijit.form._FormSelectWidget, dijit._HasDropDown], { - // summary: - // This is a "styleable" select box - it is basically a DropDownButton which - // can take a <select> as its input. - - baseClass: "dijitSelect", - - templateString: dojo.cache("dijit.form", "templates/Select.html", "<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tdojoAttachPoint=\"_buttonNode,tableNode,focusNode\" cellspacing='0' cellpadding='0'\n\twaiRole=\"combobox\" waiState=\"haspopup-true\"\n\t><tbody waiRole=\"presentation\"><tr waiRole=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonContents dijitButtonNode\" waiRole=\"presentation\"\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\" dojoAttachPoint=\"containerNode,_popupStateNode\"></span\n\t\t\t><input type=\"hidden\" ${!nameAttrSetting} dojoAttachPoint=\"valueNode\" value=\"${value}\" waiState=\"hidden-true\"\n\t\t/></td><td class=\"dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton\"\n\t\t\t\tdojoAttachPoint=\"titleNode\" waiRole=\"presentation\"\n\t\t\t><div class=\"dijitReset dijitArrowButtonInner\" waiRole=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitArrowButtonChar\" waiRole=\"presentation\">▼</div\n\t\t></td\n\t></tr></tbody\n></table>\n"), - - // attributeMap: Object - // Add in our style to be applied to the focus node - attributeMap: dojo.mixin(dojo.clone(dijit.form._FormSelectWidget.prototype.attributeMap),{style:"tableNode"}), - - // required: Boolean - // Can be true or false, default is false. - required: false, - - // state: String - // Shows current state (ie, validation result) of input (Normal, Warning, or Error) - state: "", - - // tooltipPosition: String[] - // See description of dijit.Tooltip.defaultPosition for details on this parameter. - tooltipPosition: [], - - // emptyLabel: string - // What to display in an "empty" dropdown - emptyLabel: "", - - // _isLoaded: Boolean - // Whether or not we have been loaded - _isLoaded: false, - - // _childrenLoaded: Boolean - // Whether or not our children have been loaded - _childrenLoaded: false, - - _fillContent: function(){ - // summary: - // Set the value to be the first, or the selected index - this.inherited(arguments); - if(this.options.length && !this.value && this.srcNodeRef){ - var si = this.srcNodeRef.selectedIndex; - this.value = this.options[si != -1 ? si : 0].value; - } - - // Create the dropDown widget - this.dropDown = new dijit.form._SelectMenu({id: this.id + "_menu"}); - dojo.addClass(this.dropDown.domNode, this.baseClass + "Menu"); - }, - - _getMenuItemForOption: function(/*dijit.form.__SelectOption*/ option){ - // summary: - // For the given option, return the menu item that should be - // used to display it. This can be overridden as needed - if(!option.value){ - // We are a separator (no label set for it) - return new dijit.MenuSeparator(); - }else{ - // Just a regular menu option - var click = dojo.hitch(this, "_setValueAttr", option); - var item = new dijit.MenuItem({ - option: option, - label: option.label, - onClick: click, - disabled: option.disabled || false - }); - dijit.setWaiRole(item.focusNode, "listitem"); - return item; - } - }, - - _addOptionItem: function(/*dijit.form.__SelectOption*/ option){ - // summary: - // For the given option, add an option to our dropdown. - // If the option doesn't have a value, then a separator is added - // in that place. - if(this.dropDown){ - this.dropDown.addChild(this._getMenuItemForOption(option)); - } - }, - - _getChildren: function(){ - if(!this.dropDown){ - return []; - } - return this.dropDown.getChildren(); - }, - - _loadChildren: function(/*Boolean*/ loadMenuItems){ - // summary: - // Resets the menu and the length attribute of the button - and - // ensures that the label is appropriately set. - // loadMenuItems: Boolean - // actually loads the child menu items - we only do this when we are - // populating for showing the dropdown. - - if(loadMenuItems === true){ - // this.inherited destroys this.dropDown's child widgets (MenuItems). - // Avoid this.dropDown (Menu widget) having a pointer to a destroyed widget (which will cause - // issues later in _setSelected). (see #10296) - if(this.dropDown){ - delete this.dropDown.focusedChild; - } - if(this.options.length){ - this.inherited(arguments); - }else{ - // Drop down menu is blank but add one blank entry just so something appears on the screen - // to let users know that they are no choices (mimicing native select behavior) - dojo.forEach(this._getChildren(), function(child){ child.destroyRecursive(); }); - var item = new dijit.MenuItem({label: " "}); - this.dropDown.addChild(item); - } - }else{ - this._updateSelection(); - } - - var len = this.options.length; - this._isLoaded = false; - this._childrenLoaded = true; - - if(!this._loadingStore){ - // Don't call this if we are loading - since we will handle it later - this._setValueAttr(this.value); - } - }, - - _setValueAttr: function(value){ - this.inherited(arguments); - dojo.attr(this.valueNode, "value", this.get("value")); - }, - - _setDisplay: function(/*String*/ newDisplay){ - // summary: - // sets the display for the given value (or values) - this.containerNode.innerHTML = '<span class="dijitReset dijitInline ' + this.baseClass + 'Label">' + - (newDisplay || this.emptyLabel || " ") + - '</span>'; - dijit.setWaiState(this.focusNode, "valuetext", (newDisplay || this.emptyLabel || " ") ); - }, - - validate: function(/*Boolean*/ isFocused){ - // summary: - // Called by oninit, onblur, and onkeypress. - // description: - // Show missing or invalid messages if appropriate, and highlight textbox field. - // Used when a select is initially set to no value and the user is required to - // set the value. - - var isValid = this.isValid(isFocused); - this.state = isValid ? "" : "Error"; - this._setStateClass(); - dijit.setWaiState(this.focusNode, "invalid", isValid ? "false" : "true"); - var message = isValid ? "" : this._missingMsg; - if(this._message !== message){ - this._message = message; - dijit.hideTooltip(this.domNode); - if(message){ - dijit.showTooltip(message, this.domNode, this.tooltipPosition, !this.isLeftToRight()); - } - } - return isValid; - }, - - isValid: function(/*Boolean*/ isFocused){ - // summary: - // Whether or not this is a valid value. The only way a Select - // can be invalid is when it's required but nothing is selected. - return (!this.required || !(/^\s*$/.test(this.value))); - }, - - reset: function(){ - // summary: - // Overridden so that the state will be cleared. - this.inherited(arguments); - dijit.hideTooltip(this.domNode); - this.state = ""; - this._setStateClass(); - delete this._message; - }, - - postMixInProperties: function(){ - // summary: - // set the missing message - this.inherited(arguments); - this._missingMsg = dojo.i18n.getLocalization("dijit.form", "validate", - this.lang).missingMessage; - }, - - postCreate: function(){ - this.inherited(arguments); - if(this.tableNode.style.width){ - dojo.addClass(this.domNode, this.baseClass + "FixedWidth"); - } - }, - - isLoaded: function(){ - return this._isLoaded; - }, - - loadDropDown: function(/*Function*/ loadCallback){ - // summary: - // populates the menu - this._loadChildren(true); - this._isLoaded = true; - loadCallback(); - }, - - closeDropDown: function(){ - // overriding _HasDropDown.closeDropDown() - this.inherited(arguments); - - if(this.dropDown && this.dropDown.menuTableNode){ - // Erase possible width: 100% setting from _SelectMenu.resize(). - // Leaving it would interfere with the next openDropDown() call, which - // queries the natural size of the drop down. - this.dropDown.menuTableNode.style.width = ""; - } - }, - - uninitialize: function(preserveDom){ - if(this.dropDown && !this.dropDown._destroyed){ - this.dropDown.destroyRecursive(preserveDom); - delete this.dropDown; - } - this.inherited(arguments); - } -}); - -} - -if(!dojo._hasResource["dijit._editor.plugins.LinkDialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit._editor.plugins.LinkDialog"] = true; -dojo.provide("dijit._editor.plugins.LinkDialog"); - - - - - - - - - - - - - - -dojo.declare("dijit._editor.plugins.LinkDialog", dijit._editor._Plugin, { - // summary: - // This plugin provides the basis for an 'anchor' (link) dialog and an extension of it - // provides the image link dialog. - // - // description: - // The command provided by this plugin is: - // * createLink - - // Override _Plugin.buttonClass. This plugin is controlled by a DropDownButton - // (which triggers a TooltipDialog). - buttonClass: dijit.form.DropDownButton, - - // Override _Plugin.useDefaultCommand... processing is handled by this plugin, not by dijit.Editor. - useDefaultCommand: false, - - // urlRegExp: [protected] String - // Used for validating input as correct URL. While file:// urls are not terribly - // useful, they are technically valid. - urlRegExp: "((https?|ftps?|file)\\://|\./|/|)(/[a-zA-Z]{1,1}:/|)(((?:(?:[\\da-zA-Z](?:[-\\da-zA-Z]{0,61}[\\da-zA-Z])?)\\.)*(?:[a-zA-Z](?:[-\\da-zA-Z]{0,80}[\\da-zA-Z])?)\\.?)|(((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])|(0[xX]0*[\\da-fA-F]?[\\da-fA-F]\\.){3}0[xX]0*[\\da-fA-F]?[\\da-fA-F]|(0+[0-3][0-7][0-7]\\.){3}0+[0-3][0-7][0-7]|(0|[1-9]\\d{0,8}|[1-3]\\d{9}|4[01]\\d{8}|42[0-8]\\d{7}|429[0-3]\\d{6}|4294[0-8]\\d{5}|42949[0-5]\\d{4}|429496[0-6]\\d{3}|4294967[01]\\d{2}|42949672[0-8]\\d|429496729[0-5])|0[xX]0*[\\da-fA-F]{1,8}|([\\da-fA-F]{1,4}\\:){7}[\\da-fA-F]{1,4}|([\\da-fA-F]{1,4}\\:){6}((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])))(\\:\\d+)?(/(?:[^?#\\s/]+/)*(?:[^?#\\s/]+(?:\\?[^?#\\s/]*)?(?:#.*)?)?)?", - - // emailRegExp: [protected] String - // Used for validating input as correct email address. Taken from dojox.validate - emailRegExp: "<?(mailto\\:)([!#-'*+\\-\\/-9=?A-Z^-~]+[.])*[!#-'*+\\-\\/-9=?A-Z^-~]+" /*username*/ + "@" + - "((?:(?:[\\da-zA-Z](?:[-\\da-zA-Z]{0,61}[\\da-zA-Z])?)\\.)+(?:[a-zA-Z](?:[-\\da-zA-Z]{0,6}[\\da-zA-Z])?)\\.?)|localhost|^[^-][a-zA-Z0-9_-]*>?", // host. - - // htmlTemplate: [protected] String - // String used for templating the HTML to insert at the desired point. - htmlTemplate: "<a href=\"${urlInput}\" _djrealurl=\"${urlInput}\"" + - " target=\"${targetSelect}\"" + - ">${textInput}</a>", - - // tag: [protected] String - // Tag used for the link type. - tag: "a", - - // _hostRxp [private] RegExp - // Regular expression used to validate url fragments (ip address, hostname, etc) - _hostRxp: new RegExp("^((([^\\[:]+):)?([^@]+)@)?(\\[([^\\]]+)\\]|([^\\[:]*))(:([0-9]+))?$"), - - // _userAtRxp [private] RegExp - // Regular expression used to validate e-mail address fragment. - _userAtRxp: new RegExp("^([!#-'*+\\-\\/-9=?A-Z^-~]+[.])*[!#-'*+\\-\\/-9=?A-Z^-~]+@", "i"), - - // linkDialogTemplate: [protected] String - // Template for contents of TooltipDialog to pick URL - linkDialogTemplate: [ - "<table><tr><td>", - "<label for='${id}_urlInput'>${url}</label>", - "</td><td>", - "<input dojoType='dijit.form.ValidationTextBox' required='true' " + - "id='${id}_urlInput' name='urlInput' intermediateChanges='true'>", - "</td></tr><tr><td>", - "<label for='${id}_textInput'>${text}</label>", - "</td><td>", - "<input dojoType='dijit.form.ValidationTextBox' required='true' id='${id}_textInput' " + - "name='textInput' intermediateChanges='true'>", - "</td></tr><tr><td>", - "<label for='${id}_targetSelect'>${target}</label>", - "</td><td>", - "<select id='${id}_targetSelect' name='targetSelect' dojoType='dijit.form.Select'>", - "<option selected='selected' value='_self'>${currentWindow}</option>", - "<option value='_blank'>${newWindow}</option>", - "<option value='_top'>${topWindow}</option>", - "<option value='_parent'>${parentWindow}</option>", - "</select>", - "</td></tr><tr><td colspan='2'>", - "<button dojoType='dijit.form.Button' type='submit' id='${id}_setButton'>${set}</button>", - "<button dojoType='dijit.form.Button' type='button' id='${id}_cancelButton'>${buttonCancel}</button>", - "</td></tr></table>" - ].join(""), - - _initButton: function(){ - // Override _Plugin._initButton() to initialize DropDownButton and TooltipDialog. - var _this = this; - this.tag = this.command == 'insertImage' ? 'img' : 'a'; - var messages = dojo.mixin(dojo.i18n.getLocalization("dijit", "common", this.lang), - dojo.i18n.getLocalization("dijit._editor", "LinkDialog", this.lang)); - var dropDown = (this.dropDown = new dijit.TooltipDialog({ - title: messages[this.command + "Title"], - execute: dojo.hitch(this, "setValue"), - onOpen: function(){ - _this._onOpenDialog(); - dijit.TooltipDialog.prototype.onOpen.apply(this, arguments); - }, - onCancel: function(){ - setTimeout(dojo.hitch(_this, "_onCloseDialog"),0); - } - })); - messages.urlRegExp = this.urlRegExp; - messages.id = dijit.getUniqueId(this.editor.id); - this._uniqueId = messages.id; - this._setContent(dropDown.title + - "<div style='border-bottom: 1px black solid;padding-bottom:2pt;margin-bottom:4pt'></div>" + - dojo.string.substitute(this.linkDialogTemplate, messages)); - dropDown.startup(); - this._urlInput = dijit.byId(this._uniqueId + "_urlInput"); - this._textInput = dijit.byId(this._uniqueId + "_textInput"); - this._setButton = dijit.byId(this._uniqueId + "_setButton"); - this.connect(dijit.byId(this._uniqueId + "_cancelButton"), "onClick", function(){ - this.dropDown.onCancel(); - }); - if(this._urlInput){ - this.connect(this._urlInput, "onChange", "_checkAndFixInput"); - } - if(this._textInput){ - this.connect(this._textInput, "onChange", "_checkAndFixInput"); - } - - // Build up the dual check for http/https/file:, and mailto formats. - this._urlRegExp = new RegExp("^" + this.urlRegExp + "$", "i"); - this._emailRegExp = new RegExp("^" + this.emailRegExp + "$", "i"); - this._urlInput.isValid = dojo.hitch(this, function(){ - // Function over-ride of isValid to test if the input matches a url or a mailto style link. - var value = this._urlInput.get("value"); - return this._urlRegExp.test(value) || this._emailRegExp.test(value); - }); - - this._connectTagEvents(); - this.inherited(arguments); - }, - - _checkAndFixInput: function(){ - // summary: - // A function to listen for onChange events and test the input contents - // for valid information, such as valid urls with http/https/ftp and if - // not present, try and guess if the input url is relative or not, and if - // not, append http:// to it. Also validates other fields as determined by - // the internal _isValid function. - var self = this; - var url = this._urlInput.get("value"); - var fixupUrl = function(url){ - var appendHttp = false; - var appendMailto = false; - if(url && url.length > 1){ - url = dojo.trim(url); - if(url.indexOf("mailto:") !== 0){ - if(url.indexOf("/") > 0){ - if(url.indexOf("://") === -1){ - // Check that it doesn't start with / or ./, which would - // imply 'target server relativeness' - if(url.charAt(0) !== '/' && url.indexOf("./") !== 0){ - if(self._hostRxp.test(url)){ - appendHttp = true; - } - } - } - }else if(self._userAtRxp.test(url)){ - // If it looks like a foo@, append a mailto. - appendMailto = true; - } - } - } - if(appendHttp){ - self._urlInput.set("value", "http://" + url); - } - if(appendMailto){ - self._urlInput.set("value", "mailto:" + url); - } - self._setButton.set("disabled", !self._isValid()); - }; - if(this._delayedCheck){ - clearTimeout(this._delayedCheck); - this._delayedCheck = null; - } - this._delayedCheck = setTimeout(function(){ - fixupUrl(url); - }, 250); - }, - - _connectTagEvents: function(){ - // summary: - // Over-ridable function that connects tag specific events. - this.editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){ - this.connect(this.editor.editNode, "ondblclick", this._onDblClick); - })); - }, - - _isValid: function(){ - // summary: - // Internal function to allow validating of the inputs - // for a link to determine if set should be disabled or not - // tags: - // protected - return this._urlInput.isValid() && this._textInput.isValid(); - }, - - _setContent: function(staticPanel){ - // summary: - // Helper for _initButton above. Not sure why it's a separate method. - this.dropDown.set('content', staticPanel); - }, - - _checkValues: function(args){ - // summary: - // Function to check the values in args and 'fix' them up as needed. - // args: Object - // Content being set. - // tags: - // protected - if(args && args.urlInput){ - args.urlInput = args.urlInput.replace(/"/g, """); - } - return args; - }, - - setValue: function(args){ - // summary: - // Callback from the dialog when user presses "set" button. - // tags: - // private - //TODO: prevent closing popup if the text is empty - this._onCloseDialog(); - if(dojo.isIE){ //see #4151 - var sel = dijit.range.getSelection(this.editor.window); - var range = sel.getRangeAt(0); - var a = range.endContainer; - if(a.nodeType === 3){ - // Text node, may be the link contents, so check parent. - // This plugin doesn't really support nested HTML elements - // in the link, it assumes all link content is text. - a = a.parentNode; - } - if(a && (a.nodeName && a.nodeName.toLowerCase() !== this.tag)){ - // Stll nothing, one last thing to try on IE, as it might be 'img' - // and thus considered a control. - a = dojo.withGlobal(this.editor.window, - "getSelectedElement", dijit._editor.selection, [this.tag]); - } - if(a && (a.nodeName && a.nodeName.toLowerCase() === this.tag)){ - // Okay, we do have a match. IE, for some reason, sometimes pastes before - // instead of removing the targetted paste-over element, so we unlink the - // old one first. If we do not the <a> tag remains, but it has no content, - // so isn't readily visible (but is wrong for the action). - if(this.editor.queryCommandEnabled("unlink")){ - // Select all the link childent, then unlink. The following insert will - // then replace the selected text. - dojo.withGlobal(this.editor.window, - "selectElementChildren", dijit._editor.selection, [a]); - this.editor.execCommand("unlink"); - } - } - } - // make sure values are properly escaped, etc. - args = this._checkValues(args); - this.editor.execCommand('inserthtml', - dojo.string.substitute(this.htmlTemplate, args)); - }, - - _onCloseDialog: function(){ - // summary: - // Handler for close event on the dialog - this.editor.focus(); - }, - - _getCurrentValues: function(a){ - // summary: - // Over-ride for getting the values to set in the dropdown. - // a: - // The anchor/link to process for data for the dropdown. - // tags: - // protected - var url, text, target; - if(a && a.tagName.toLowerCase() === this.tag){ - url = a.getAttribute('_djrealurl') || a.getAttribute('href'); - target = a.getAttribute('target') || "_self"; - text = a.textContent || a.innerText; - dojo.withGlobal(this.editor.window, "selectElement", dijit._editor.selection, [a, true]); - }else{ - text = dojo.withGlobal(this.editor.window, dijit._editor.selection.getSelectedText); - } - return {urlInput: url || '', textInput: text || '', targetSelect: target || ''}; //Object; - }, - - _onOpenDialog: function(){ - // summary: - // Handler for when the dialog is opened. - // If the caret is currently in a URL then populate the URL's info into the dialog. - var a; - if(dojo.isIE){ - // IE is difficult to select the element in, using the range unified - // API seems to work reasonably well. - var sel = dijit.range.getSelection(this.editor.window); - var range = sel.getRangeAt(0); - a = range.endContainer; - if(a.nodeType === 3){ - // Text node, may be the link contents, so check parent. - // This plugin doesn't really support nested HTML elements - // in the link, it assumes all link content is text. - a = a.parentNode; - } - if(a && (a.nodeName && a.nodeName.toLowerCase() !== this.tag)){ - // Stll nothing, one last thing to try on IE, as it might be 'img' - // and thus considered a control. - a = dojo.withGlobal(this.editor.window, - "getSelectedElement", dijit._editor.selection, [this.tag]); - } - }else{ - a = dojo.withGlobal(this.editor.window, - "getAncestorElement", dijit._editor.selection, [this.tag]); - } - this.dropDown.reset(); - this._setButton.set("disabled", true); - this.dropDown.set("value", this._getCurrentValues(a)); - }, - - _onDblClick: function(e){ - // summary: - // Function to define a behavior on double clicks on the element - // type this dialog edits to select it and pop up the editor - // dialog. - // e: Object - // The double-click event. - // tags: - // protected. - if(e && e.target){ - var t = e.target; - var tg = t.tagName? t.tagName.toLowerCase() : ""; - if(tg === this.tag && dojo.attr(t,"href")){ - dojo.withGlobal(this.editor.window, - "selectElement", - dijit._editor.selection, [t]); - this.editor.onDisplayChanged(); - setTimeout(dojo.hitch(this, function(){ - // Focus shift outside the event handler. - // IE doesn't like focus changes in event handles. - this.button.set("disabled", false); - this.button.openDropDown(); - }), 10); - } - } - } -}); - -dojo.declare("dijit._editor.plugins.ImgLinkDialog", [dijit._editor.plugins.LinkDialog], { - // summary: - // This plugin extends LinkDialog and adds in a plugin for handling image links. - // provides the image link dialog. - // - // description: - // The command provided by this plugin is: - // * insertImage - - // linkDialogTemplate: [protected] String - // Over-ride for template since img dialog doesn't need target that anchor tags may. - linkDialogTemplate: [ - "<table><tr><td>", - "<label for='${id}_urlInput'>${url}</label>", - "</td><td>", - "<input dojoType='dijit.form.ValidationTextBox' regExp='${urlRegExp}' " + - "required='true' id='${id}_urlInput' name='urlInput' intermediateChanges='true'>", - "</td></tr><tr><td>", - "<label for='${id}_textInput'>${text}</label>", - "</td><td>", - "<input dojoType='dijit.form.ValidationTextBox' required='false' id='${id}_textInput' " + - "name='textInput' intermediateChanges='true'>", - "</td></tr><tr><td>", - "</td><td>", - "</td></tr><tr><td colspan='2'>", - "<button dojoType='dijit.form.Button' type='submit' id='${id}_setButton'>${set}</button>", - "<button dojoType='dijit.form.Button' type='button' id='${id}_cancelButton'>${buttonCancel}</button>", - "</td></tr></table>" - ].join(""), - - // htmlTemplate: [protected] String - // String used for templating the <img> HTML to insert at the desired point. - htmlTemplate: "<img src=\"${urlInput}\" _djrealurl=\"${urlInput}\" alt=\"${textInput}\" />", - - // tag: [protected] String - // Tag used for the link type (img). - tag: "img", - - _getCurrentValues: function(img){ - // summary: - // Over-ride for getting the values to set in the dropdown. - // a: - // The anchor/link to process for data for the dropdown. - // tags: - // protected - var url, text; - if(img && img.tagName.toLowerCase() === this.tag){ - url = img.getAttribute('_djrealurl') || img.getAttribute('src'); - text = img.getAttribute('alt'); - dojo.withGlobal(this.editor.window, - "selectElement", dijit._editor.selection, [img, true]); - }else{ - text = dojo.withGlobal(this.editor.window, dijit._editor.selection.getSelectedText); - } - return {urlInput: url || '', textInput: text || ''}; //Object; - }, - - _isValid: function(){ - // summary: - // Over-ride for images. You can have alt text of blank, it is valid. - // tags: - // protected - return this._urlInput.isValid(); - }, - - _connectTagEvents: function(){ - // summary: - // Over-ridable function that connects tag specific events. - this.inherited(arguments); - this.editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){ - // Use onmousedown instead of onclick. Seems that IE eats the first onclick - // to wrap it in a selector box, then the second one acts as onclick. See #10420 - this.connect(this.editor.editNode, "onmousedown", this._selectTag); - })); - }, - - _selectTag: function(e){ - // summary: - // A simple event handler that lets me select an image if it is clicked on. - // makes it easier to select images in a standard way across browsers. Otherwise - // selecting an image for edit becomes difficult. - // e: Event - // The mousedown event. - // tags: - // private - if(e && e.target){ - var t = e.target; - var tg = t.tagName? t.tagName.toLowerCase() : ""; - if(tg === this.tag){ - dojo.withGlobal(this.editor.window, - "selectElement", - dijit._editor.selection, [t]); - } - } - }, - - _checkValues: function(args){ - // summary: - // Function to check the values in args and 'fix' them up as needed - // (special characters in the url or alt text) - // args: Object - // Content being set. - // tags: - // protected - if(args && args.urlInput){ - args.urlInput = args.urlInput.replace(/"/g, """); - } - if(args && args.textInput){ - args.textInput = args.textInput.replace(/"/g, """); - } - return args; - }, - - _onDblClick: function(e){ - // summary: - // Function to define a behavior on double clicks on the element - // type this dialog edits to select it and pop up the editor - // dialog. - // e: Object - // The double-click event. - // tags: - // protected. - if(e && e.target){ - var t = e.target; - var tg = t.tagName? t.tagName.toLowerCase() : ""; - if(tg === this.tag && dojo.attr(t,"src")){ - dojo.withGlobal(this.editor.window, - "selectElement", - dijit._editor.selection, [t]); - this.editor.onDisplayChanged(); - setTimeout(dojo.hitch(this, function(){ - // Focus shift outside the event handler. - // IE doesn't like focus changes in event handles. - this.button.set("disabled", false); - this.button.openDropDown(); - }), 10); - } - } - } -}); - -// Register this plugin. -dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){ - if(o.plugin){ return; } - switch(o.args.name){ - case "createLink": - o.plugin = new dijit._editor.plugins.LinkDialog({command: o.args.name}); - break; - case "insertImage": - o.plugin = new dijit._editor.plugins.ImgLinkDialog({command: o.args.name}); - break; - } -}); - -} - -if(!dojo._hasResource["dijit.MenuBar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.MenuBar"] = true; -dojo.provide("dijit.MenuBar"); - - - -dojo.declare("dijit.MenuBar", dijit._MenuBase, { - // summary: - // A menu bar, listing menu choices horizontally, like the "File" menu in most desktop applications - - templateString: dojo.cache("dijit", "templates/MenuBar.html", "<div class=\"dijitMenuBar dijitMenuPassive\" dojoAttachPoint=\"containerNode\" waiRole=\"menubar\" tabIndex=\"${tabIndex}\" dojoAttachEvent=\"onkeypress: _onKeyPress\"></div>\n"), - - baseClass: "dijitMenuBar", - - // _isMenuBar: [protected] Boolean - // This is a MenuBar widget, not a (vertical) Menu widget. - _isMenuBar: true, - - postCreate: function(){ - var k = dojo.keys, l = this.isLeftToRight(); - this.connectKeyNavHandlers( - l ? [k.LEFT_ARROW] : [k.RIGHT_ARROW], - l ? [k.RIGHT_ARROW] : [k.LEFT_ARROW] - ); - - // parameter to dijit.popup.open() about where to put popup (relative to this.domNode) - this._orient = this.isLeftToRight() ? {BL: 'TL'} : {BR: 'TR'}; - }, - - focusChild: function(item){ - // overload focusChild so that whenever the focus is moved to a new item, - // check the previous focused whether it has its popup open, if so, after - // focusing the new item, open its submenu immediately - var prev_item = this.focusedChild, - showpopup = prev_item && prev_item.popup && prev_item.popup.isShowingNow; - this.inherited(arguments); - if(showpopup && item.popup && !item.disabled){ - this._openPopup(); // TODO: on down arrow, _openPopup() is called here and in onItemClick() - } - }, - - _onKeyPress: function(/*Event*/ evt){ - // summary: - // Handle keyboard based menu navigation. - // tags: - // protected - - if(evt.ctrlKey || evt.altKey){ return; } - - switch(evt.charOrCode){ - case dojo.keys.DOWN_ARROW: - this._moveToPopup(evt); - dojo.stopEvent(evt); - } - }, - - onItemClick: function(/*dijit._Widget*/ item, /*Event*/ evt){ - // summary: - // Handle clicks on an item. Cancels a dropdown if already open. - // tags: - // private - if(item.popup && item.popup.isShowingNow){ - item.popup.onCancel(); - }else{ - this.inherited(arguments); - } - } -}); - -} - -if(!dojo._hasResource["dijit.MenuBarItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.MenuBarItem"] = true; -dojo.provide("dijit.MenuBarItem"); - - - -dojo.declare("dijit._MenuBarItemMixin", null, { - templateString: dojo.cache("dijit", "templates/MenuBarItem.html", "<div class=\"dijitReset dijitInline dijitMenuItem dijitMenuItemLabel\" dojoAttachPoint=\"focusNode\" waiRole=\"menuitem\" tabIndex=\"-1\"\n\t\tdojoAttachEvent=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\n\t<span dojoAttachPoint=\"containerNode\"></span>\n</div>\n"), - - // overriding attributeMap because we don't have icon - attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, { - label: { node: "containerNode", type: "innerHTML" } - }) -}); - -dojo.declare("dijit.MenuBarItem", [dijit.MenuItem, dijit._MenuBarItemMixin], { - // summary: - // Item in a MenuBar that's clickable, and doesn't spawn a submenu when pressed (or hovered) - -}); - -} - -if(!dojo._hasResource["dijit.PopupMenuBarItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.PopupMenuBarItem"] = true; -dojo.provide("dijit.PopupMenuBarItem"); - - - - -dojo.declare("dijit.PopupMenuBarItem", [dijit.PopupMenuItem, dijit._MenuBarItemMixin], { - // summary: - // Item in a MenuBar like "File" or "Edit", that spawns a submenu when pressed (or hovered) -}); - - -} - -if(!dojo._hasResource["dojo.number"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.number"] = true; -dojo.provide("dojo.number"); - - - - - - - -/*===== -dojo.number = { - // summary: localized formatting and parsing routines for Number -} - -dojo.number.__FormatOptions = function(){ - // pattern: String? - // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns) - // with this string. Default value is based on locale. Overriding this property will defeat - // localization. Literal characters in patterns are not supported. - // type: String? - // choose a format type based on the locale from the following: - // decimal, scientific (not yet supported), percent, currency. decimal by default. - // places: Number? - // fixed number of decimal places to show. This overrides any - // information in the provided pattern. - // round: Number? - // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1 - // means do not round. - // locale: String? - // override the locale used to determine formatting rules - // fractional: Boolean? - // If false, show no decimal places, overriding places and pattern settings. - this.pattern = pattern; - this.type = type; - this.places = places; - this.round = round; - this.locale = locale; - this.fractional = fractional; -} -=====*/ - -dojo.number.format = function(/*Number*/value, /*dojo.number.__FormatOptions?*/options){ - // summary: - // Format a Number as a String, using locale-specific settings - // description: - // Create a string from a Number using a known localized pattern. - // Formatting patterns appropriate to the locale are chosen from the - // [Common Locale Data Repository](http://unicode.org/cldr) as well as the appropriate symbols and - // delimiters. - // If value is Infinity, -Infinity, or is not a valid JavaScript number, return null. - // value: - // the number to be formatted - - options = dojo.mixin({}, options || {}); - var locale = dojo.i18n.normalizeLocale(options.locale), - bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale); - options.customs = bundle; - var pattern = options.pattern || bundle[(options.type || "decimal") + "Format"]; - if(isNaN(value) || Math.abs(value) == Infinity){ return null; } // null - return dojo.number._applyPattern(value, pattern, options); // String -}; - -//dojo.number._numberPatternRE = /(?:[#0]*,?)*[#0](?:\.0*#*)?/; // not precise, but good enough -dojo.number._numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough - -dojo.number._applyPattern = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatOptions?*/options){ - // summary: - // Apply pattern to format value as a string using options. Gives no - // consideration to local customs. - // value: - // the number to be formatted. - // pattern: - // a pattern string as described by - // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns) - // options: dojo.number.__FormatOptions? - // _applyPattern is usually called via `dojo.number.format()` which - // populates an extra property in the options parameter, "customs". - // The customs object specifies group and decimal parameters if set. - - //TODO: support escapes - options = options || {}; - var group = options.customs.group, - decimal = options.customs.decimal, - patternList = pattern.split(';'), - positivePattern = patternList[0]; - pattern = patternList[(value < 0) ? 1 : 0] || ("-" + positivePattern); - - //TODO: only test against unescaped - if(pattern.indexOf('%') != -1){ - value *= 100; - }else if(pattern.indexOf('\u2030') != -1){ - value *= 1000; // per mille - }else if(pattern.indexOf('\u00a4') != -1){ - group = options.customs.currencyGroup || group;//mixins instead? - decimal = options.customs.currencyDecimal || decimal;// Should these be mixins instead? - pattern = pattern.replace(/\u00a4{1,3}/, function(match){ - var prop = ["symbol", "currency", "displayName"][match.length-1]; - return options[prop] || options.currency || ""; - }); - }else if(pattern.indexOf('E') != -1){ - throw new Error("exponential notation not supported"); - } - - //TODO: support @ sig figs? - var numberPatternRE = dojo.number._numberPatternRE; - var numberPattern = positivePattern.match(numberPatternRE); - if(!numberPattern){ - throw new Error("unable to find a number expression in pattern: "+pattern); - } - if(options.fractional === false){ options.places = 0; } - return pattern.replace(numberPatternRE, - dojo.number._formatAbsolute(value, numberPattern[0], {decimal: decimal, group: group, places: options.places, round: options.round})); -} - -dojo.number.round = function(/*Number*/value, /*Number?*/places, /*Number?*/increment){ - // summary: - // Rounds to the nearest value with the given number of decimal places, away from zero - // description: - // Rounds to the nearest value with the given number of decimal places, away from zero if equal. - // Similar to Number.toFixed(), but compensates for browser quirks. Rounding can be done by - // fractional increments also, such as the nearest quarter. - // NOTE: Subject to floating point errors. See dojox.math.round for experimental workaround. - // value: - // The number to round - // places: - // The number of decimal places where rounding takes place. Defaults to 0 for whole rounding. - // Must be non-negative. - // increment: - // Rounds next place to nearest value of increment/10. 10 by default. - // example: - // >>> dojo.number.round(-0.5) - // -1 - // >>> dojo.number.round(162.295, 2) - // 162.29 // note floating point error. Should be 162.3 - // >>> dojo.number.round(10.71, 0, 2.5) - // 10.75 - var factor = 10 / (increment || 10); - return (factor * +value).toFixed(places) / factor; // Number -} - -if((0.9).toFixed() == 0){ - // (isIE) toFixed() bug workaround: Rounding fails on IE when most significant digit - // is just after the rounding place and is >=5 - (function(){ - var round = dojo.number.round; - dojo.number.round = function(v, p, m){ - var d = Math.pow(10, -p || 0), a = Math.abs(v); - if(!v || a >= d || a * Math.pow(10, p + 1) < 5){ - d = 0; - } - return round(v, p, m) + (v > 0 ? d : -d); - } - })(); -} - -/*===== -dojo.number.__FormatAbsoluteOptions = function(){ - // decimal: String? - // the decimal separator - // group: String? - // the group separator - // places: Number?|String? - // number of decimal places. the range "n,m" will format to m places. - // round: Number? - // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1 - // means don't round. - this.decimal = decimal; - this.group = group; - this.places = places; - this.round = round; -} -=====*/ - -dojo.number._formatAbsolute = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatAbsoluteOptions?*/options){ - // summary: - // Apply numeric pattern to absolute value using options. Gives no - // consideration to local customs. - // value: - // the number to be formatted, ignores sign - // pattern: - // the number portion of a pattern (e.g. `#,##0.00`) - options = options || {}; - if(options.places === true){options.places=0;} - if(options.places === Infinity){options.places=6;} // avoid a loop; pick a limit - - var patternParts = pattern.split("."), - comma = typeof options.places == "string" && options.places.indexOf(","), - maxPlaces = options.places; - if(comma){ - maxPlaces = options.places.substring(comma + 1); - }else if(!(maxPlaces >= 0)){ - maxPlaces = (patternParts[1] || []).length; - } - if(!(options.round < 0)){ - value = dojo.number.round(value, maxPlaces, options.round); - } - - var valueParts = String(Math.abs(value)).split("."), - fractional = valueParts[1] || ""; - if(patternParts[1] || options.places){ - if(comma){ - options.places = options.places.substring(0, comma); - } - // Pad fractional with trailing zeros - var pad = options.places !== undefined ? options.places : (patternParts[1] && patternParts[1].lastIndexOf("0") + 1); - if(pad > fractional.length){ - valueParts[1] = dojo.string.pad(fractional, pad, '0', true); - } - - // Truncate fractional - if(maxPlaces < fractional.length){ - valueParts[1] = fractional.substr(0, maxPlaces); - } - }else{ - if(valueParts[1]){ valueParts.pop(); } - } - - // Pad whole with leading zeros - var patternDigits = patternParts[0].replace(',', ''); - pad = patternDigits.indexOf("0"); - if(pad != -1){ - pad = patternDigits.length - pad; - if(pad > valueParts[0].length){ - valueParts[0] = dojo.string.pad(valueParts[0], pad); - } - - // Truncate whole - if(patternDigits.indexOf("#") == -1){ - valueParts[0] = valueParts[0].substr(valueParts[0].length - pad); - } - } - - // Add group separators - var index = patternParts[0].lastIndexOf(','), - groupSize, groupSize2; - if(index != -1){ - groupSize = patternParts[0].length - index - 1; - var remainder = patternParts[0].substr(0, index); - index = remainder.lastIndexOf(','); - if(index != -1){ - groupSize2 = remainder.length - index - 1; - } - } - var pieces = []; - for(var whole = valueParts[0]; whole;){ - var off = whole.length - groupSize; - pieces.push((off > 0) ? whole.substr(off) : whole); - whole = (off > 0) ? whole.slice(0, off) : ""; - if(groupSize2){ - groupSize = groupSize2; - delete groupSize2; - } - } - valueParts[0] = pieces.reverse().join(options.group || ","); - - return valueParts.join(options.decimal || "."); -}; - -/*===== -dojo.number.__RegexpOptions = function(){ - // pattern: String? - // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns) - // with this string. Default value is based on locale. Overriding this property will defeat - // localization. - // type: String? - // choose a format type based on the locale from the following: - // decimal, scientific (not yet supported), percent, currency. decimal by default. - // locale: String? - // override the locale used to determine formatting rules - // strict: Boolean? - // strict parsing, false by default. Strict parsing requires input as produced by the format() method. - // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators - // places: Number|String? - // number of decimal places to accept: Infinity, a positive number, or - // a range "n,m". Defined by pattern or Infinity if pattern not provided. - this.pattern = pattern; - this.type = type; - this.locale = locale; - this.strict = strict; - this.places = places; -} -=====*/ -dojo.number.regexp = function(/*dojo.number.__RegexpOptions?*/options){ - // summary: - // Builds the regular needed to parse a number - // description: - // Returns regular expression with positive and negative match, group - // and decimal separators - return dojo.number._parseInfo(options).regexp; // String -} - -dojo.number._parseInfo = function(/*Object?*/options){ - options = options || {}; - var locale = dojo.i18n.normalizeLocale(options.locale), - bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale), - pattern = options.pattern || bundle[(options.type || "decimal") + "Format"], -//TODO: memoize? - group = bundle.group, - decimal = bundle.decimal, - factor = 1; - - if(pattern.indexOf('%') != -1){ - factor /= 100; - }else if(pattern.indexOf('\u2030') != -1){ - factor /= 1000; // per mille - }else{ - var isCurrency = pattern.indexOf('\u00a4') != -1; - if(isCurrency){ - group = bundle.currencyGroup || group; - decimal = bundle.currencyDecimal || decimal; - } - } - - //TODO: handle quoted escapes - var patternList = pattern.split(';'); - if(patternList.length == 1){ - patternList.push("-" + patternList[0]); - } - - var re = dojo.regexp.buildGroupRE(patternList, function(pattern){ - pattern = "(?:"+dojo.regexp.escapeString(pattern, '.')+")"; - return pattern.replace(dojo.number._numberPatternRE, function(format){ - var flags = { - signed: false, - separator: options.strict ? group : [group,""], - fractional: options.fractional, - decimal: decimal, - exponent: false - }, - - parts = format.split('.'), - places = options.places; - - // special condition for percent (factor != 1) - // allow decimal places even if not specified in pattern - if(parts.length == 1 && factor != 1){ - parts[1] = "###"; - } - if(parts.length == 1 || places === 0){ - flags.fractional = false; - }else{ - if(places === undefined){ places = options.pattern ? parts[1].lastIndexOf('0') + 1 : Infinity; } - if(places && options.fractional == undefined){flags.fractional = true;} // required fractional, unless otherwise specified - if(!options.places && (places < parts[1].length)){ places += "," + parts[1].length; } - flags.places = places; - } - var groups = parts[0].split(','); - if(groups.length > 1){ - flags.groupSize = groups.pop().length; - if(groups.length > 1){ - flags.groupSize2 = groups.pop().length; - } - } - return "("+dojo.number._realNumberRegexp(flags)+")"; - }); - }, true); - - if(isCurrency){ - // substitute the currency symbol for the placeholder in the pattern - re = re.replace(/([\s\xa0]*)(\u00a4{1,3})([\s\xa0]*)/g, function(match, before, target, after){ - var prop = ["symbol", "currency", "displayName"][target.length-1], - symbol = dojo.regexp.escapeString(options[prop] || options.currency || ""); - before = before ? "[\\s\\xa0]" : ""; - after = after ? "[\\s\\xa0]" : ""; - if(!options.strict){ - if(before){before += "*";} - if(after){after += "*";} - return "(?:"+before+symbol+after+")?"; - } - return before+symbol+after; - }); - } - -//TODO: substitute localized sign/percent/permille/etc.? - - // normalize whitespace and return - return {regexp: re.replace(/[\xa0 ]/g, "[\\s\\xa0]"), group: group, decimal: decimal, factor: factor}; // Object -} - -/*===== -dojo.number.__ParseOptions = function(){ - // pattern: String? - // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns) - // with this string. Default value is based on locale. Overriding this property will defeat - // localization. Literal characters in patterns are not supported. - // type: String? - // choose a format type based on the locale from the following: - // decimal, scientific (not yet supported), percent, currency. decimal by default. - // locale: String? - // override the locale used to determine formatting rules - // strict: Boolean? - // strict parsing, false by default. Strict parsing requires input as produced by the format() method. - // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators - // fractional: Boolean?|Array? - // Whether to include the fractional portion, where the number of decimal places are implied by pattern - // or explicit 'places' parameter. The value [true,false] makes the fractional portion optional. - this.pattern = pattern; - this.type = type; - this.locale = locale; - this.strict = strict; - this.fractional = fractional; -} -=====*/ -dojo.number.parse = function(/*String*/expression, /*dojo.number.__ParseOptions?*/options){ - // summary: - // Convert a properly formatted string to a primitive Number, using - // locale-specific settings. - // description: - // Create a Number from a string using a known localized pattern. - // Formatting patterns are chosen appropriate to the locale - // and follow the syntax described by - // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns) - // Note that literal characters in patterns are not supported. - // expression: - // A string representation of a Number - var info = dojo.number._parseInfo(options), - results = (new RegExp("^"+info.regexp+"$")).exec(expression); - if(!results){ - return NaN; //NaN - } - var absoluteMatch = results[1]; // match for the positive expression - if(!results[1]){ - if(!results[2]){ - return NaN; //NaN - } - // matched the negative pattern - absoluteMatch =results[2]; - info.factor *= -1; - } - - // Transform it to something Javascript can parse as a number. Normalize - // decimal point and strip out group separators or alternate forms of whitespace - absoluteMatch = absoluteMatch. - replace(new RegExp("["+info.group + "\\s\\xa0"+"]", "g"), ""). - replace(info.decimal, "."); - // Adjust for negative sign, percent, etc. as necessary - return absoluteMatch * info.factor; //Number -}; - -/*===== -dojo.number.__RealNumberRegexpFlags = function(){ - // places: Number? - // The integer number of decimal places or a range given as "n,m". If - // not given, the decimal part is optional and the number of places is - // unlimited. - // decimal: String? - // A string for the character used as the decimal point. Default - // is ".". - // fractional: Boolean?|Array? - // Whether decimal places are used. Can be true, false, or [true, - // false]. Default is [true, false] which means optional. - // exponent: Boolean?|Array? - // Express in exponential notation. Can be true, false, or [true, - // false]. Default is [true, false], (i.e. will match if the - // exponential part is present are not). - // eSigned: Boolean?|Array? - // The leading plus-or-minus sign on the exponent. Can be true, - // false, or [true, false]. Default is [true, false], (i.e. will - // match if it is signed or unsigned). flags in regexp.integer can be - // applied. - this.places = places; - this.decimal = decimal; - this.fractional = fractional; - this.exponent = exponent; - this.eSigned = eSigned; -} -=====*/ - -dojo.number._realNumberRegexp = function(/*dojo.number.__RealNumberRegexpFlags?*/flags){ - // summary: - // Builds a regular expression to match a real number in exponential - // notation - - // assign default values to missing parameters - flags = flags || {}; - //TODO: use mixin instead? - if(!("places" in flags)){ flags.places = Infinity; } - if(typeof flags.decimal != "string"){ flags.decimal = "."; } - if(!("fractional" in flags) || /^0/.test(flags.places)){ flags.fractional = [true, false]; } - if(!("exponent" in flags)){ flags.exponent = [true, false]; } - if(!("eSigned" in flags)){ flags.eSigned = [true, false]; } - - var integerRE = dojo.number._integerRegexp(flags), - decimalRE = dojo.regexp.buildGroupRE(flags.fractional, - function(q){ - var re = ""; - if(q && (flags.places!==0)){ - re = "\\" + flags.decimal; - if(flags.places == Infinity){ - re = "(?:" + re + "\\d+)?"; - }else{ - re += "\\d{" + flags.places + "}"; - } - } - return re; - }, - true - ); - - var exponentRE = dojo.regexp.buildGroupRE(flags.exponent, - function(q){ - if(q){ return "([eE]" + dojo.number._integerRegexp({ signed: flags.eSigned}) + ")"; } - return ""; - } - ); - - var realRE = integerRE + decimalRE; - // allow for decimals without integers, e.g. .25 - if(decimalRE){realRE = "(?:(?:"+ realRE + ")|(?:" + decimalRE + "))";} - return realRE + exponentRE; // String -}; - -/*===== -dojo.number.__IntegerRegexpFlags = function(){ - // signed: Boolean? - // The leading plus-or-minus sign. Can be true, false, or `[true,false]`. - // Default is `[true, false]`, (i.e. will match if it is signed - // or unsigned). - // separator: String? - // The character used as the thousands separator. Default is no - // separator. For more than one symbol use an array, e.g. `[",", ""]`, - // makes ',' optional. - // groupSize: Number? - // group size between separators - // groupSize2: Number? - // second grouping, where separators 2..n have a different interval than the first separator (for India) - this.signed = signed; - this.separator = separator; - this.groupSize = groupSize; - this.groupSize2 = groupSize2; -} -=====*/ - -dojo.number._integerRegexp = function(/*dojo.number.__IntegerRegexpFlags?*/flags){ - // summary: - // Builds a regular expression that matches an integer - - // assign default values to missing parameters - flags = flags || {}; - if(!("signed" in flags)){ flags.signed = [true, false]; } - if(!("separator" in flags)){ - flags.separator = ""; - }else if(!("groupSize" in flags)){ - flags.groupSize = 3; - } - - var signRE = dojo.regexp.buildGroupRE(flags.signed, - function(q){ return q ? "[-+]" : ""; }, - true - ); - - var numberRE = dojo.regexp.buildGroupRE(flags.separator, - function(sep){ - if(!sep){ - return "(?:\\d+)"; - } - - sep = dojo.regexp.escapeString(sep); - if(sep == " "){ sep = "\\s"; } - else if(sep == "\xa0"){ sep = "\\s\\xa0"; } - - var grp = flags.groupSize, grp2 = flags.groupSize2; - //TODO: should we continue to enforce that numbers with separators begin with 1-9? See #6933 - if(grp2){ - var grp2RE = "(?:0|[1-9]\\d{0," + (grp2-1) + "}(?:[" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})"; - return ((grp-grp2) > 0) ? "(?:" + grp2RE + "|(?:0|[1-9]\\d{0," + (grp-1) + "}))" : grp2RE; - } - return "(?:0|[1-9]\\d{0," + (grp-1) + "}(?:[" + sep + "]\\d{" + grp + "})*)"; - }, - true - ); - - return signRE + numberRE; // String -} - -} - -if(!dojo._hasResource["dijit.ProgressBar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.ProgressBar"] = true; -dojo.provide("dijit.ProgressBar"); - - - - - - - -dojo.declare("dijit.ProgressBar", [dijit._Widget, dijit._Templated], { - // summary: - // A progress indication widget, showing the amount completed - // (often the percentage completed) of a task. - // - // example: - // | <div dojoType="ProgressBar" - // | places="0" - // | progress="..." maximum="..."> - // | </div> - // - // description: - // Note that the progress bar is updated via (a non-standard) - // update() method, rather than via attr() like other widgets. - - // progress: [const] String (Percentage or Number) - // Number or percentage indicating amount of task completed. - // With "%": percentage value, 0% <= progress <= 100%, or - // without "%": absolute value, 0 <= progress <= maximum - // TODO: rename to value for 2.0 - progress: "0", - - // maximum: [const] Float - // Max sample number - maximum: 100, - - // places: [const] Number - // Number of places to show in values; 0 by default - places: 0, - - // indeterminate: [const] Boolean - // If false: show progress value (number or percentage). - // If true: show that a process is underway but that the amount completed is unknown. - indeterminate: false, - - // name: String - // this is the field name (for a form) if set. This needs to be set if you want to use - // this widget in a dijit.form.Form widget (such as dijit.Dialog) - name: '', - - templateString: dojo.cache("dijit", "templates/ProgressBar.html", "<div class=\"dijitProgressBar dijitProgressBarEmpty\"\n\t><div waiRole=\"progressbar\" dojoAttachPoint=\"internalProgress\" class=\"dijitProgressBarFull\"\n\t\t><div class=\"dijitProgressBarTile\"></div\n\t\t><span style=\"visibility:hidden\"> </span\n\t></div\n\t><div dojoAttachPoint=\"label\" class=\"dijitProgressBarLabel\" id=\"${id}_label\"> </div\n\t><img dojoAttachPoint=\"indeterminateHighContrastImage\" class=\"dijitProgressBarIndeterminateHighContrastImage\" alt=\"\"\n/></div>\n"), - - // _indeterminateHighContrastImagePath: [private] dojo._URL - // URL to image to use for indeterminate progress bar when display is in high contrast mode - _indeterminateHighContrastImagePath: - dojo.moduleUrl("dijit", "themes/a11y/indeterminate_progress.gif"), - - // public functions - postCreate: function(){ - this.inherited(arguments); - this.indeterminateHighContrastImage.setAttribute("src", - this._indeterminateHighContrastImagePath.toString()); - this.update(); - }, - - update: function(/*Object?*/attributes){ - // summary: - // Change attributes of ProgressBar, similar to attr(hash). - // - // attributes: - // May provide progress and/or maximum properties on this parameter; - // see attribute specs for details. - // - // example: - // | myProgressBar.update({'indeterminate': true}); - // | myProgressBar.update({'progress': 80}); - - // TODO: deprecate this method and use set() instead - - dojo.mixin(this, attributes || {}); - var tip = this.internalProgress; - var percent = 1, classFunc; - if(this.indeterminate){ - classFunc = "addClass"; - dijit.removeWaiState(tip, "valuenow"); - dijit.removeWaiState(tip, "valuemin"); - dijit.removeWaiState(tip, "valuemax"); - }else{ - classFunc = "removeClass"; - if(String(this.progress).indexOf("%") != -1){ - percent = Math.min(parseFloat(this.progress)/100, 1); - this.progress = percent * this.maximum; - }else{ - this.progress = Math.min(this.progress, this.maximum); - percent = this.progress / this.maximum; - } - var text = this.report(percent); - this.label.firstChild.nodeValue = text; - dijit.setWaiState(tip, "describedby", this.label.id); - dijit.setWaiState(tip, "valuenow", this.progress); - dijit.setWaiState(tip, "valuemin", 0); - dijit.setWaiState(tip, "valuemax", this.maximum); - } - dojo[classFunc](this.domNode, "dijitProgressBarIndeterminate"); - tip.style.width = (percent * 100) + "%"; - this.onChange(); - }, - - _setValueAttr: function(v){ - if(v == Infinity){ - this.update({indeterminate:true}); - }else{ - this.update({indeterminate:false, progress:v}); - } - }, - - _getValueAttr: function(){ - return this.progress; - }, - - report: function(/*float*/percent){ - // summary: - // Generates message to show inside progress bar (normally indicating amount of task completed). - // May be overridden. - // tags: - // extension - - return dojo.number.format(percent, { type: "percent", places: this.places, locale: this.lang }); - }, - - onChange: function(){ - // summary: - // Callback fired when progress updates. - // tags: - // progress - } -}); - -} - -if(!dojo._hasResource["dijit.TitlePane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.TitlePane"] = true; -dojo.provide("dijit.TitlePane"); - - - - - - - -dojo.declare( - "dijit.TitlePane", - [dijit.layout.ContentPane, dijit._Templated, dijit._CssStateMixin], -{ - // summary: - // A pane with a title on top, that can be expanded or collapsed. - // - // description: - // An accessible container with a title Heading, and a content - // section that slides open and closed. TitlePane is an extension to - // `dijit.layout.ContentPane`, providing all the useful content-control aspects from it. - // - // example: - // | // load a TitlePane from remote file: - // | var foo = new dijit.TitlePane({ href: "foobar.html", title:"Title" }); - // | foo.startup(); - // - // example: - // | <!-- markup href example: --> - // | <div dojoType="dijit.TitlePane" href="foobar.html" title="Title"></div> - // - // example: - // | <!-- markup with inline data --> - // | <div dojoType="dijit.TitlePane" title="Title"> - // | <p>I am content</p> - // | </div> - - // title: String - // Title of the pane - title: "", - - // 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: dijit.defaultDuration, - - // baseClass: [protected] String - // The root className to be placed on this widget's domNode. - baseClass: "dijitTitlePane", - - templateString: dojo.cache("dijit", "templates/TitlePane.html", "<div>\n\t<div dojoAttachEvent=\"onclick:_onTitleClick, onkeypress:_onTitleKey\"\n\t\t\tclass=\"dijitTitlePaneTitle\" dojoAttachPoint=\"titleBarNode\">\n\t\t<div class=\"dijitTitlePaneTitleFocus\" dojoAttachPoint=\"focusNode\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" dojoAttachPoint=\"arrowNode\" class=\"dijitArrowNode\" waiRole=\"presentation\"\n\t\t\t/><span dojoAttachPoint=\"arrowNodeInner\" class=\"dijitArrowNodeInner\"></span\n\t\t\t><span dojoAttachPoint=\"titleNode\" class=\"dijitTitlePaneTextNode\"></span>\n\t\t</div>\n\t</div>\n\t<div class=\"dijitTitlePaneContentOuter\" dojoAttachPoint=\"hideNode\" waiRole=\"presentation\">\n\t\t<div class=\"dijitReset\" dojoAttachPoint=\"wipeNode\" waiRole=\"presentation\">\n\t\t\t<div class=\"dijitTitlePaneContentInner\" dojoAttachPoint=\"containerNode\" waiRole=\"region\" tabindex=\"-1\" id=\"${id}_pane\">\n\t\t\t\t<!-- nested divs because wipeIn()/wipeOut() doesn't work right on node w/padding etc. Put padding on inner div. -->\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n"), - - attributeMap: dojo.delegate(dijit.layout.ContentPane.prototype.attributeMap, { - title: { node: "titleNode", type: "innerHTML" }, - tooltip: {node: "focusNode", type: "attribute", attribute: "title"}, // focusNode spans the entire width, titleNode doesn't - id:"" - }), - - postCreate: function(){ - if(!this.open){ - this.hideNode.style.display = this.wipeNode.style.display = "none"; - } - - // 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"); - } - this._setCss(); - dojo.setSelectable(this.titleNode, false); - - // setup open/close animations - var hideNode = this.hideNode, wipeNode = this.wipeNode; - this._wipeIn = dojo.fx.wipeIn({ - node: this.wipeNode, - duration: this.duration, - beforeBegin: function(){ - hideNode.style.display=""; - } - }); - this._wipeOut = dojo.fx.wipeOut({ - node: this.wipeNode, - duration: this.duration, - onEnd: function(){ - hideNode.style.display="none"; - } - }); - this.inherited(arguments); - }, - - _setOpenAttr: function(/* Boolean */ open){ - // summary: - // Hook to make attr("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. - if(this.open !== open){ this.toggle(); } - dijit.setWaiState(this.containerNode,"hidden", this.open ? "false" : "true"); - dijit.setWaiState(this.focusNode, "pressed", this.open ? "true" : "false"); - }, - - _setToggleableAttr: function(/* Boolean */ canToggle){ - // summary: - // Hook to make attr("canToggle", boolean) work. - // canToggle: Boolean - // True to allow user to open/close pane by clicking title bar. - this.toggleable = canToggle; - dijit.setWaiRole(this.focusNode, canToggle ? "button" : "heading"); - if(canToggle){ - // TODO: if canToggle is switched from true false shouldn't we remove this setting? - dijit.setWaiState(this.focusNode, "controls", this.id+"_pane"); - dojo.attr(this.focusNode, "tabIndex", this.tabIndex); - } - else{ - dojo.removeAttr(this.focusNode, "tabIndex"); - } - this._setCss(); - }, - - _setContentAttr: function(content){ - // summary: - // Hook to make attr("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 - dojo.marginBox(this.wipeNode, { h: dojo.marginBox(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 - - dojo.forEach([this._wipeIn, this._wipeOut], function(animation){ - if(animation && animation.status() == "playing"){ - animation.stop(); - } - }); - - var anim = this[this.open ? "_wipeOut" : "_wipeIn"] - if(anim){ - anim.play(); - }else{ - this.hideNode.style.display = this.open ? "" : "none"; - } - this.open =! this.open; - - // 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.open){ - this._onShow(); - }else{ - this.onHide(); - } - - this._setCss(); - }, - - _setCss: function(){ - // summary: - // Set the open/close css state for the TitlePane - // tags: - // private - - var node = this.titleBarNode || this.focusNode; - - if(this._titleBarClass){ - dojo.removeClass(node, this._titleBarClass); - } - this._titleBarClass = "dijit" + (this.toggleable ? "" : "Fixed") + (this.open ? "Open" : "Closed"); - dojo.addClass(node, this._titleBarClass); - this.arrowNodeInner.innerHTML = this.open ? "-" : "+"; - }, - - _onTitleKey: function(/*Event*/ e){ - // summary: - // Handler for when user hits a key - // tags: - // private - - if(e.charOrCode == dojo.keys.ENTER || e.charOrCode == ' '){ - if(this.toggleable){ - this.toggle(); - } - dojo.stopEvent(e); - }else if(e.charOrCode == dojo.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 - dojo.deprecated("dijit.TitlePane.setTitle() is deprecated. Use set('title', ...) instead.", "", "2.0"); - this.set("title", title); - } -}); - -} - -if(!dojo._hasResource["dojo.DeferredList"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.DeferredList"] = true; -dojo.provide("dojo.DeferredList"); -dojo.DeferredList = function(/*Array*/ list, /*Boolean?*/ fireOnOneCallback, /*Boolean?*/ fireOnOneErrback, /*Boolean?*/ consumeErrors, /*Function?*/ canceller){ - // summary: - // Provides event handling for a group of Deferred objects. - // description: - // DeferredList takes an array of existing deferreds and returns a new deferred of its own - // this new deferred will typically have its callback fired when all of the deferreds in - // the given list have fired their own deferreds. The parameters `fireOnOneCallback` and - // fireOnOneErrback, will fire before all the deferreds as appropriate - // - // list: - // The list of deferreds to be synchronizied with this DeferredList - // fireOnOneCallback: - // Will cause the DeferredLists callback to be fired as soon as any - // of the deferreds in its list have been fired instead of waiting until - // the entire list has finished - // fireonOneErrback: - // Will cause the errback to fire upon any of the deferreds errback - // canceller: - // A deferred canceller function, see dojo.Deferred - var resultList = []; - dojo.Deferred.call(this); - var self = this; - if(list.length === 0 && !fireOnOneCallback){ - this.resolve([0, []]); - } - var finished = 0; - dojo.forEach(list, function(item, i){ - item.then(function(result){ - if(fireOnOneCallback){ - self.resolve([i, result]); - }else{ - addResult(true, result); - } - },function(error){ - if(fireOnOneErrback){ - self.reject(error); - }else{ - addResult(false, error); - } - if(consumeErrors){ - return null; - } - throw error; - }); - function addResult(succeeded, result){ - resultList[i] = [succeeded, result]; - finished++; - if(finished === list.length){ - self.resolve(resultList); - } - - } - }); -}; -dojo.DeferredList.prototype = new dojo.Deferred(); - -dojo.DeferredList.prototype.gatherResults= function(deferredList){ - // summary: - // Gathers the results of the deferreds for packaging - // as the parameters to the Deferred Lists' callback - - var d = new dojo.DeferredList(deferredList, false, true, false); - d.addCallback(function(results){ - var ret = []; - dojo.forEach(results, function(result){ - ret.push(result[1]); - }); - return ret; - }); - return d; -}; - -} - -if(!dojo._hasResource["dojo.cookie"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.cookie"] = true; -dojo.provide("dojo.cookie"); - - - -/*===== -dojo.__cookieProps = function(){ - // expires: Date|String|Number? - // If a number, the number of days from today at which the cookie - // will expire. If a date, the date past which the cookie will expire. - // If expires is in the past, the cookie will be deleted. - // If expires is omitted or is 0, the cookie will expire when the browser closes. << FIXME: 0 seems to disappear right away? FF3. - // path: String? - // The path to use for the cookie. - // domain: String? - // The domain to use for the cookie. - // secure: Boolean? - // Whether to only send the cookie on secure connections - this.expires = expires; - this.path = path; - this.domain = domain; - this.secure = secure; -} -=====*/ - - -dojo.cookie = function(/*String*/name, /*String?*/value, /*dojo.__cookieProps?*/props){ - // summary: - // Get or set a cookie. - // description: - // If one argument is passed, returns the value of the cookie - // For two or more arguments, acts as a setter. - // name: - // Name of the cookie - // value: - // Value for the cookie - // props: - // Properties for the cookie - // example: - // set a cookie with the JSON-serialized contents of an object which - // will expire 5 days from now: - // | dojo.cookie("configObj", dojo.toJson(config), { expires: 5 }); - // - // example: - // de-serialize a cookie back into a JavaScript object: - // | var config = dojo.fromJson(dojo.cookie("configObj")); - // - // example: - // delete a cookie: - // | dojo.cookie("configObj", null, {expires: -1}); - var c = document.cookie; - if(arguments.length == 1){ - var matches = c.match(new RegExp("(?:^|; )" + dojo.regexp.escapeString(name) + "=([^;]*)")); - return matches ? decodeURIComponent(matches[1]) : undefined; // String or undefined - }else{ - props = props || {}; -// FIXME: expires=0 seems to disappear right away, not on close? (FF3) Change docs? - var exp = props.expires; - if(typeof exp == "number"){ - var d = new Date(); - d.setTime(d.getTime() + exp*24*60*60*1000); - exp = props.expires = d; - } - if(exp && exp.toUTCString){ props.expires = exp.toUTCString(); } - - value = encodeURIComponent(value); - var updatedCookie = name + "=" + value, propName; - for(propName in props){ - updatedCookie += "; " + propName; - var propValue = props[propName]; - if(propValue !== true){ updatedCookie += "=" + propValue; } - } - document.cookie = updatedCookie; - } -}; - -dojo.cookie.isSupported = function(){ - // summary: - // Use to determine if the current browser supports cookies or not. - // - // Returns true if user allows cookies. - // Returns false if user doesn't allow cookies. - - if(!("cookieEnabled" in navigator)){ - this("__djCookieTest__", "CookiesAllowed"); - navigator.cookieEnabled = this("__djCookieTest__") == "CookiesAllowed"; - if(navigator.cookieEnabled){ - this("__djCookieTest__", "", {expires: -1}); - } - } - return navigator.cookieEnabled; -}; - -} - -if(!dojo._hasResource["dijit.tree.TreeStoreModel"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.tree.TreeStoreModel"] = true; -dojo.provide("dijit.tree.TreeStoreModel"); - -dojo.declare( - "dijit.tree.TreeStoreModel", - null, - { - // summary: - // Implements dijit.Tree.model connecting to a 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 - - dojo.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([ - dojo.connect(store, "onNew", this, "onNewItem"), - dojo.connect(store, "onDelete", this, "onDeleteItem"), - dojo.connect(store, "onSet", this, "onSetItem") - ]); - } - }, - - destroy: function(){ - dojo.forEach(this.connects, dojo.disconnect); - // 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: dojo.hitch(this, function(items){ - if(items.length != 1){ - throw new Error(this.declaredClass + ": query " + dojo.toJson(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 dojo.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 = dojo.hitch(this, arguments.callee); - store.loadItem({ - item: parentItem, - onItem: function(parentItem){ - getChildren(parentItem, onComplete, onError); - }, - onError: onError - }); - return; - } - // get children of specified item - var childItems = []; - for(var i=0; i<this.childrenAttrs.length; i++){ - var vals = store.getValues(parentItem, this.childrenAttrs[i]); - childItems = childItems.concat(vals); - } - - // count how many items need to be loaded - var _waitCount = 0; - if(!this.deferItemLoadingUntilExpand){ - dojo.forEach(childItems, function(item){ if(!store.isItemLoaded(item)){ _waitCount++; } }); - } - - if(_waitCount == 0){ - // all items are already loaded (or we aren't loading them). proceed... - onComplete(childItems); - }else{ - // still waiting for some or all of the items to load - dojo.forEach(childItems, function(item, idx){ - if(!store.isItemLoaded(item)){ - store.loadItem({ - item: item, - onItem: function(item){ - childItems[idx] = item; - if(--_waitCount == 0){ - // all nodes have been loaded, send them to the tree - onComplete(childItems); - } - }, - onError: onError - }); - } - }); - } - }, - - // ======================================================================= - // Inspecting items - - isItem: function(/* anything */ something){ - return this.store.isItem(something); // Boolean - }, - - fetchItemByIdentity: function(/* object */ keywordArgs){ - this.store.fetchItemByIdentity(keywordArgs); - }, - - getIdentity: function(/* item */ item){ - return this.store.getIdentity(item); // Object - }, - - getLabel: function(/*dojo.data.Item*/ item){ - // summary: - // Get the label for an item - if(this.labelAttr){ - return this.store.getValue(item,this.labelAttr); // String - }else{ - return this.store.getLabel(item); // String - } - }, - - // ======================================================================= - // Write interface - - newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){ - // summary: - // Creates a new item. See `dojo.data.api.Write` for details on args. - // Used in drag & drop when item from external source dropped onto tree. - // description: - // Developers will need to override this method if new items get added - // to parents with multiple children attributes, in order to define which - // children attribute points to the new item. - - var pInfo = {parent: parent, attribute: this.childrenAttrs[0], insertIndex: insertIndex}; - - if(this.newItemIdAttr && args[this.newItemIdAttr]){ - // Maybe there's already a corresponding item in the store; if so, reuse it. - this.fetchItemByIdentity({identity: args[this.newItemIdAttr], scope: this, onItem: function(item){ - if(item){ - // There's already a matching item in store, use it - this.pasteItem(item, null, parent, true, insertIndex); - }else{ - // Create new item in the tree, based on the drag source. - this.store.newItem(args, pInfo); - } - }}); - }else{ - // [as far as we know] there is no id so we must assume this is a new item - this.store.newItem(args, pInfo); - } - }, - - pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){ - // summary: - // Move or copy an item from one parent item to another. - // Used in drag & drop - var store = this.store, - parentAttr = this.childrenAttrs[0]; // name of "children" attr in parent item - - // remove child from source item, and record the attribute that child occurred in - if(oldParentItem){ - dojo.forEach(this.childrenAttrs, function(attr){ - if(store.containsValue(oldParentItem, attr, childItem)){ - if(!bCopy){ - var values = dojo.filter(store.getValues(oldParentItem, attr), function(x){ - return x != childItem; - }); - store.setValues(oldParentItem, attr, values); - } - parentAttr = attr; - } - }); - } - - // modify target item's children attribute to include this item - if(newParentItem){ - if(typeof insertIndex == "number"){ - // call slice() to avoid modifying the original array, confusing the data store - var childItems = store.getValues(newParentItem, parentAttr).slice(); - childItems.splice(insertIndex, 0, childItem); - store.setValues(newParentItem, parentAttr, childItems); - }else{ - store.setValues(newParentItem, parentAttr, - store.getValues(newParentItem, parentAttr).concat(childItem)); - } - } - }, - - // ======================================================================= - // Callbacks - - onChange: function(/*dojo.data.Item*/ item){ - // summary: - // Callback whenever an item has changed, so that Tree - // can update the label, icon, etc. Note that changes - // to an item's children or parent(s) will trigger an - // onChildrenChange() so you can ignore those changes here. - // tags: - // callback - }, - - onChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){ - // summary: - // Callback to do notifications about new, updated, or deleted items. - // tags: - // callback - }, - - onDelete: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){ - // summary: - // Callback when an item has been deleted. - // description: - // Note that there will also be an onChildrenChange() callback for the parent - // of this item. - // tags: - // callback - }, - - // ======================================================================= - // Events from data store - - onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){ - // summary: - // Handler for when new items appear in the store, either from a drop operation - // or some other way. Updates the tree view (if necessary). - // description: - // If the new item is a child of an existing item, - // calls onChildrenChange() with the new list of children - // for that existing item. - // - // tags: - // extension - - // We only care about the new item if it has a parent that corresponds to a TreeNode - // we are currently displaying - if(!parentInfo){ - return; - } - - // Call onChildrenChange() on parent (ie, existing) item with new list of children - // In the common case, the new list of children is simply parentInfo.newValue or - // [ parentInfo.newValue ], although if items in the store has multiple - // child attributes (see `childrenAttr`), then it's a superset of parentInfo.newValue, - // so call getChildren() to be sure to get right answer. - this.getChildren(parentInfo.item, dojo.hitch(this, function(children){ - this.onChildrenChange(parentInfo.item, children); - })); - }, - - onDeleteItem: function(/*Object*/ item){ - // summary: - // Handler for delete notifications from underlying store - this.onDelete(item); - }, - - onSetItem: function(/* item */ item, - /* attribute-name-string */ attribute, - /* object | array */ oldValue, - /* object | array */ newValue){ - // summary: - // Updates the tree view according to changes in the data store. - // description: - // Handles updates to an item's children by calling onChildrenChange(), and - // other updates to an item by calling onChange(). - // - // See `onNewItem` for more details on handling updates to an item's children. - // tags: - // extension - - if(dojo.indexOf(this.childrenAttrs, attribute) != -1){ - // item's children list changed - this.getChildren(item, dojo.hitch(this, function(children){ - // See comments in onNewItem() about calling getChildren() - this.onChildrenChange(item, children); - })); - }else{ - // item's label/icon/etc. changed. - this.onChange(item); - } - } - }); - - - -} - -if(!dojo._hasResource["dijit.tree.ForestStoreModel"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.tree.ForestStoreModel"] = true; -dojo.provide("dijit.tree.ForestStoreModel"); - - - -dojo.declare("dijit.tree.ForestStoreModel", dijit.tree.TreeStoreModel, { - // summary: - // Interface between Tree and a dojo.store that doesn't have a root item, - // i.e. has multiple "top level" items. - // - // description - // Use this class to wrap a dojo.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. - // It allows dijit.Tree to assume a single root item, even if the store doesn't have one. - - // 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: dojo.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:dojo.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(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); - } - } - dijit.tree.TreeStoreModel.prototype.pasteItem.call(this, 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: dojo.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 || - dojo.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). - // - // Developers can override this function to do something more efficient if they can - // detect which items are possible top level items (based on the item and the - // parentInfo parameters). 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(dojo.indexOf(this.root.children, item) != -1){ - this._requeryTop(); - } - - this.inherited(arguments); - } -}); - - - -} - -if(!dojo._hasResource["dijit.Tree"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.Tree"] = true; -dojo.provide("dijit.Tree"); - - - - - - - - - - - -dojo.declare( - "dijit._TreeNode", - [dijit._Widget, dijit._Templated, dijit._Container, dijit._Contained, dijit._CssStateMixin], -{ - // summary: - // Single node within a tree. This class is used internally - // by Tree and should not be accessed directly. - // tags: - // private - - // item: dojo.data.Item - // the dojo.data entry this tree represents - item: null, - - // isTreeNode: [protected] Boolean - // Indicates that this is a TreeNode. Used by `dijit.Tree` only, - // should not be accessed directly. - isTreeNode: true, - - // label: String - // Text of this tree node - label: "", - - // isExpandable: [private] Boolean - // This node has children, so show the expando node (+ sign) - isExpandable: null, - - // isExpanded: [readonly] Boolean - // This node is currently expanded (ie, opened) - isExpanded: false, - - // state: [private] String - // Dynamic loading-related stuff. - // When an empty folder node appears, it is "UNCHECKED" first, - // then after dojo.data query it becomes "LOADING" and, finally "LOADED" - state: "UNCHECKED", - - templateString: dojo.cache("dijit", "templates/TreeNode.html", "<div class=\"dijitTreeNode\" waiRole=\"presentation\"\n\t><div dojoAttachPoint=\"rowNode\" class=\"dijitTreeRow\" waiRole=\"presentation\" dojoAttachEvent=\"onmouseenter:_onMouseEnter, onmouseleave:_onMouseLeave, onclick:_onClick, ondblclick:_onDblClick\"\n\t\t><img src=\"${_blankGif}\" alt=\"\" dojoAttachPoint=\"expandoNode\" class=\"dijitTreeExpando\" waiRole=\"presentation\"\n\t\t/><span dojoAttachPoint=\"expandoNodeText\" class=\"dijitExpandoText\" waiRole=\"presentation\"\n\t\t></span\n\t\t><span dojoAttachPoint=\"contentNode\"\n\t\t\tclass=\"dijitTreeContent\" waiRole=\"presentation\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" dojoAttachPoint=\"iconNode\" class=\"dijitIcon dijitTreeIcon\" waiRole=\"presentation\"\n\t\t\t/><span dojoAttachPoint=\"labelNode\" class=\"dijitTreeLabel\" wairole=\"treeitem\" tabindex=\"-1\" waiState=\"selected-false\" dojoAttachEvent=\"onfocus:_onLabelFocus\"></span>\n\t\t</span\n\t></div>\n\t<div dojoAttachPoint=\"containerNode\" class=\"dijitTreeContainer\" waiRole=\"presentation\" style=\"display: none;\"></div>\n</div>\n"), - - baseClass: "dijitTreeNode", - - // For hover effect for tree node, and focus effect for label - cssStateNodes: { - rowNode: "dijitTreeRow", - labelNode: "dijitTreeLabel" - }, - - attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, { - label: {node: "labelNode", type: "innerText"}, - tooltip: {node: "rowNode", type: "attribute", attribute: "title"} - }), - - postCreate: function(){ - this.inherited(arguments); - - // set expand icon for leaf - this._setExpando(); - - // set icon and label class based on item - this._updateItemClasses(this.item); - - if(this.isExpandable){ - dijit.setWaiState(this.labelNode, "expanded", this.isExpanded); - } - }, - - _setIndentAttr: function(indent){ - // summary: - // Tell this node how many levels it should be indented - // description: - // 0 for top level nodes, 1 for their children, 2 for their - // grandchildren, etc. - this.indent = indent; - - // Math.max() is to prevent negative padding on hidden root node (when indent == -1) - var pixels = (Math.max(indent, 0) * this.tree._nodePixelIndent) + "px"; - - dojo.style(this.domNode, "backgroundPosition", pixels + " 0px"); - dojo.style(this.rowNode, this.isLeftToRight() ? "paddingLeft" : "paddingRight", pixels); - - dojo.forEach(this.getChildren(), function(child){ - child.set("indent", indent+1); - }); - }, - - markProcessing: function(){ - // summary: - // Visually denote that tree is loading data, etc. - // tags: - // private - this.state = "LOADING"; - this._setExpando(true); - }, - - unmarkProcessing: function(){ - // summary: - // Clear markup from markProcessing() call - // tags: - // private - this._setExpando(false); - }, - - _updateItemClasses: function(item){ - // summary: - // Set appropriate CSS classes for icon and label dom node - // (used to allow for item updates to change respective CSS) - // tags: - // private - var tree = this.tree, model = tree.model; - if(tree._v10Compat && item === model.root){ - // For back-compat with 1.0, need to use null to specify root item (TODO: remove in 2.0) - item = null; - } - this._applyClassAndStyle(item, "icon", "Icon"); - this._applyClassAndStyle(item, "label", "Label"); - this._applyClassAndStyle(item, "row", "Row"); - }, - - _applyClassAndStyle: function(item, lower, upper){ - // summary: - // Set the appropriate CSS classes and styles for labels, icons and rows. - // - // item: - // The data item. - // - // lower: - // The lower case attribute to use, e.g. 'icon', 'label' or 'row'. - // - // upper: - // The upper case attribute to use, e.g. 'Icon', 'Label' or 'Row'. - // - // tags: - // private - - var clsName = "_" + lower + "Class"; - var nodeName = lower + "Node"; - - if(this[clsName]){ - dojo.removeClass(this[nodeName], this[clsName]); - } - this[clsName] = this.tree["get" + upper + "Class"](item, this.isExpanded); - if(this[clsName]){ - dojo.addClass(this[nodeName], this[clsName]); - } - dojo.style(this[nodeName], this.tree["get" + upper + "Style"](item, this.isExpanded) || {}); - }, - - _updateLayout: function(){ - // summary: - // Set appropriate CSS classes for this.domNode - // tags: - // private - var parent = this.getParent(); - if(!parent || parent.rowNode.style.display == "none"){ - /* if we are hiding the root node then make every first level child look like a root node */ - dojo.addClass(this.domNode, "dijitTreeIsRoot"); - }else{ - dojo.toggleClass(this.domNode, "dijitTreeIsLast", !this.getNextSibling()); - } - }, - - _setExpando: function(/*Boolean*/ processing){ - // summary: - // Set the right image for the expando node - // tags: - // private - - var styles = ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened", - "dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"], - _a11yStates = ["*","-","+","*"], - idx = processing ? 0 : (this.isExpandable ? (this.isExpanded ? 1 : 2) : 3); - - // apply the appropriate class to the expando node - dojo.removeClass(this.expandoNode, styles); - dojo.addClass(this.expandoNode, styles[idx]); - - // provide a non-image based indicator for images-off mode - this.expandoNodeText.innerHTML = _a11yStates[idx]; - - }, - - expand: function(){ - // summary: - // Show my children - // returns: - // Deferred that fires when expansion is complete - - // If there's already an expand in progress or we are already expanded, just return - if(this._expandDeferred){ - return this._expandDeferred; // dojo.Deferred - } - - // cancel in progress collapse operation - this._wipeOut && this._wipeOut.stop(); - - // All the state information for when a node is expanded, maybe this should be - // set when the animation completes instead - this.isExpanded = true; - dijit.setWaiState(this.labelNode, "expanded", "true"); - dijit.setWaiRole(this.containerNode, "group"); - dojo.addClass(this.contentNode,'dijitTreeContentExpanded'); - this._setExpando(); - this._updateItemClasses(this.item); - if(this == this.tree.rootNode){ - dijit.setWaiState(this.tree.domNode, "expanded", "true"); - } - - var def, - wipeIn = dojo.fx.wipeIn({ - node: this.containerNode, duration: dijit.defaultDuration, - onEnd: function(){ - def.callback(true); - } - }); - - // Deferred that fires when expand is complete - def = (this._expandDeferred = new dojo.Deferred(function(){ - // Canceller - wipeIn.stop(); - })); - - wipeIn.play(); - - return def; // dojo.Deferred - }, - - collapse: function(){ - // summary: - // Collapse this node (if it's expanded) - - if(!this.isExpanded){ return; } - - // cancel in progress expand operation - if(this._expandDeferred){ - this._expandDeferred.cancel(); - delete this._expandDeferred; - } - - this.isExpanded = false; - dijit.setWaiState(this.labelNode, "expanded", "false"); - if(this == this.tree.rootNode){ - dijit.setWaiState(this.tree.domNode, "expanded", "false"); - } - dojo.removeClass(this.contentNode,'dijitTreeContentExpanded'); - this._setExpando(); - this._updateItemClasses(this.item); - - if(!this._wipeOut){ - this._wipeOut = dojo.fx.wipeOut({ - node: this.containerNode, duration: dijit.defaultDuration - }); - } - this._wipeOut.play(); - }, - - // indent: Integer - // Levels from this node to the root node - indent: 0, - - setChildItems: function(/* Object[] */ items){ - // summary: - // Sets the child items of this node, removing/adding nodes - // from current children to match specified items[] array. - // Also, if this.persist == true, expands any children that were previously - // opened. - // returns: - // Deferred object that fires after all previously opened children - // have been expanded again (or fires instantly if there are no such children). - - var tree = this.tree, - model = tree.model, - defs = []; // list of deferreds that need to fire before I am complete - - - // Orphan all my existing children. - // If items contains some of the same items as before then we will reattach them. - // Don't call this.removeChild() because that will collapse the tree etc. - dojo.forEach(this.getChildren(), function(child){ - dijit._Container.prototype.removeChild.call(this, child); - }, this); - - this.state = "LOADED"; - - if(items && items.length > 0){ - this.isExpandable = true; - - // Create _TreeNode widget for each specified tree node, unless one already - // exists and isn't being used (presumably it's from a DnD move and was recently - // released - dojo.forEach(items, function(item){ - var id = model.getIdentity(item), - existingNodes = tree._itemNodesMap[id], - node; - if(existingNodes){ - for(var i=0;i<existingNodes.length;i++){ - if(existingNodes[i] && !existingNodes[i].getParent()){ - node = existingNodes[i]; - node.set('indent', this.indent+1); - break; - } - } - } - if(!node){ - node = this.tree._createTreeNode({ - item: item, - tree: tree, - isExpandable: model.mayHaveChildren(item), - label: tree.getLabel(item), - tooltip: tree.getTooltip(item), - dir: tree.dir, - lang: tree.lang, - indent: this.indent + 1 - }); - if(existingNodes){ - existingNodes.push(node); - }else{ - tree._itemNodesMap[id] = [node]; - } - } - this.addChild(node); - - // If node was previously opened then open it again now (this may trigger - // more data store accesses, recursively) - if(this.tree.autoExpand || this.tree._state(item)){ - defs.push(tree._expandNode(node)); - } - }, this); - - // note that updateLayout() needs to be called on each child after - // _all_ the children exist - dojo.forEach(this.getChildren(), function(child, idx){ - child._updateLayout(); - }); - }else{ - this.isExpandable=false; - } - - if(this._setExpando){ - // change expando to/from dot or + icon, as appropriate - this._setExpando(false); - } - - // Set leaf icon or folder icon, as appropriate - this._updateItemClasses(this.item); - - // On initial tree show, make the selected TreeNode as either the root node of the tree, - // or the first child, if the root node is hidden - if(this == tree.rootNode){ - var fc = this.tree.showRoot ? this : this.getChildren()[0]; - if(fc){ - fc.setFocusable(true); - tree.lastFocused = fc; - }else{ - // fallback: no nodes in tree so focus on Tree <div> itself - tree.domNode.setAttribute("tabIndex", "0"); - } - } - - return new dojo.DeferredList(defs); // dojo.Deferred - }, - - removeChild: function(/* treeNode */ node){ - this.inherited(arguments); - - var children = this.getChildren(); - if(children.length == 0){ - this.isExpandable = false; - this.collapse(); - } - - dojo.forEach(children, function(child){ - child._updateLayout(); - }); - }, - - makeExpandable: function(){ - // summary: - // if this node wasn't already showing the expando node, - // turn it into one and call _setExpando() - - // TODO: hmm this isn't called from anywhere, maybe should remove it for 2.0 - - this.isExpandable = true; - this._setExpando(false); - }, - - _onLabelFocus: function(evt){ - // summary: - // Called when this row is focused (possibly programatically) - // Note that we aren't using _onFocus() builtin to dijit - // because it's called when focus is moved to a descendant TreeNode. - // tags: - // private - this.tree._onNodeFocus(this); - }, - - setSelected: function(/*Boolean*/ selected){ - // summary: - // A Tree has a (single) currently selected node. - // Mark that this node is/isn't that currently selected node. - // description: - // In particular, setting a node as selected involves setting tabIndex - // so that when user tabs to the tree, focus will go to that node (only). - dijit.setWaiState(this.labelNode, "selected", selected); - dojo.toggleClass(this.rowNode, "dijitTreeRowSelected", selected); - }, - - setFocusable: function(/*Boolean*/ selected){ - // summary: - // A Tree has a (single) node that's focusable. - // Mark that this node is/isn't that currently focsuable node. - // description: - // In particular, setting a node as selected involves setting tabIndex - // so that when user tabs to the tree, focus will go to that node (only). - - this.labelNode.setAttribute("tabIndex", selected ? "0" : "-1"); - }, - - _onClick: function(evt){ - // summary: - // Handler for onclick event on a node - // tags: - // private - this.tree._onClick(this, evt); - }, - _onDblClick: function(evt){ - // summary: - // Handler for ondblclick event on a node - // tags: - // private - this.tree._onDblClick(this, evt); - }, - - _onMouseEnter: function(evt){ - // summary: - // Handler for onmouseenter event on a node - // tags: - // private - this.tree._onNodeMouseEnter(this, evt); - }, - - _onMouseLeave: function(evt){ - // summary: - // Handler for onmouseenter event on a node - // tags: - // private - this.tree._onNodeMouseLeave(this, evt); - } -}); - -dojo.declare( - "dijit.Tree", - [dijit._Widget, dijit._Templated], -{ - // summary: - // This widget displays hierarchical data from a store. - - // store: [deprecated] String||dojo.data.Store - // Deprecated. Use "model" parameter instead. - // The store to get data to display in the tree. - store: null, - - // model: dijit.Tree.model - // Interface to read tree data, get notifications of changes to tree data, - // and for handling drop operations (i.e drag and drop onto the tree) - model: null, - - // query: [deprecated] anything - // Deprecated. User should specify query to the model directly instead. - // Specifies datastore query to return the root item or top items for the tree. - query: null, - - // label: [deprecated] String - // Deprecated. Use dijit.tree.ForestStoreModel directly instead. - // Used in conjunction with query parameter. - // If a query is specified (rather than a root node id), and a label is also specified, - // then a fake root node is created and displayed, with this label. - label: "", - - // showRoot: [const] Boolean - // Should the root node be displayed, or hidden? - showRoot: true, - - // childrenAttr: [deprecated] String[] - // Deprecated. This information should be specified in the model. - // One ore more attributes that holds children of a tree node - childrenAttr: ["children"], - - // path: String[] or Item[] - // Full path from rootNode to selected node expressed as array of items or array of ids. - // Since setting the path may be asynchronous (because ofwaiting on dojo.data), set("path", ...) - // returns a Deferred to indicate when the set is complete. - path: [], - - // selectedItem: [readonly] Item - // The currently selected item in this tree. - // This property can only be set (via set('selectedItem', ...)) when that item is already - // visible in the tree. (I.e. the tree has already been expanded to show that node.) - // Should generally use `path` attribute to set the selected item instead. - selectedItem: null, - - // openOnClick: Boolean - // If true, clicking a folder node's label will open it, rather than calling onClick() - openOnClick: false, - - // openOnDblClick: Boolean - // If true, double-clicking a folder node's label will open it, rather than calling onDblClick() - openOnDblClick: false, - - templateString: dojo.cache("dijit", "templates/Tree.html", "<div class=\"dijitTree dijitTreeContainer\" waiRole=\"tree\"\n\tdojoAttachEvent=\"onkeypress:_onKeyPress\">\n\t<div class=\"dijitInline dijitTreeIndent\" style=\"position: absolute; top: -9999px\" dojoAttachPoint=\"indentDetector\"></div>\n</div>\n"), - - // persist: Boolean - // Enables/disables use of cookies for state saving. - persist: true, - - // autoExpand: Boolean - // Fully expand the tree on load. Overrides `persist` - autoExpand: false, - - // dndController: [protected] String - // Class name to use as as the dnd controller. Specifying this class enables DnD. - // Generally you should specify this as "dijit.tree.dndSource". - dndController: null, - - // parameters to pull off of the tree and pass on to the dndController as its params - dndParams: ["onDndDrop","itemCreator","onDndCancel","checkAcceptance", "checkItemAcceptance", "dragThreshold", "betweenThreshold"], - - //declare the above items so they can be pulled from the tree's markup - - // onDndDrop: [protected] Function - // Parameter to dndController, see `dijit.tree.dndSource.onDndDrop`. - // Generally this doesn't need to be set. - onDndDrop: null, - - /*===== - itemCreator: function(nodes, target, source){ - // summary: - // Returns objects passed to `Tree.model.newItem()` based on DnD nodes - // dropped onto the tree. Developer must override this method to enable - // dropping from external sources onto this Tree, unless the Tree.model's items - // happen to look like {id: 123, name: "Apple" } with no other attributes. - // description: - // For each node in nodes[], which came from source, create a hash of name/value - // pairs to be passed to Tree.model.newItem(). Returns array of those hashes. - // nodes: DomNode[] - // The DOMNodes dragged from the source container - // target: DomNode - // The target TreeNode.rowNode - // source: dojo.dnd.Source - // The source container the nodes were dragged from, perhaps another Tree or a plain dojo.dnd.Source - // returns: Object[] - // Array of name/value hashes for each new item to be added to the Tree, like: - // | [ - // | { id: 123, label: "apple", foo: "bar" }, - // | { id: 456, label: "pear", zaz: "bam" } - // | ] - // tags: - // extension - return [{}]; - }, - =====*/ - itemCreator: null, - - // onDndCancel: [protected] Function - // Parameter to dndController, see `dijit.tree.dndSource.onDndCancel`. - // Generally this doesn't need to be set. - onDndCancel: null, - -/*===== - checkAcceptance: function(source, nodes){ - // summary: - // Checks if the Tree itself can accept nodes from this source - // source: dijit.tree._dndSource - // The source which provides items - // nodes: DOMNode[] - // Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if - // source is a dijit.Tree. - // tags: - // extension - return true; // Boolean - }, -=====*/ - checkAcceptance: null, - -/*===== - checkItemAcceptance: function(target, source, position){ - // summary: - // Stub function to be overridden if one wants to check for the ability to drop at the node/item level - // description: - // In the base case, this is called to check if target can become a child of source. - // When betweenThreshold is set, position="before" or "after" means that we - // are asking if the source node can be dropped before/after the target node. - // target: DOMNode - // The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to - // Use dijit.getEnclosingWidget(target) to get the TreeNode. - // source: dijit.tree.dndSource - // The (set of) nodes we are dropping - // position: String - // "over", "before", or "after" - // tags: - // extension - return true; // Boolean - }, -=====*/ - checkItemAcceptance: null, - - // dragThreshold: Integer - // Number of pixels mouse moves before it's considered the start of a drag operation - dragThreshold: 5, - - // betweenThreshold: Integer - // Set to a positive value to allow drag and drop "between" nodes. - // - // If during DnD mouse is over a (target) node but less than betweenThreshold - // pixels from the bottom edge, dropping the the dragged node will make it - // the next sibling of the target node, rather than the child. - // - // Similarly, if mouse is over a target node but less that betweenThreshold - // pixels from the top edge, dropping the dragged node will make it - // the target node's previous sibling rather than the target node's child. - betweenThreshold: 0, - - // _nodePixelIndent: Integer - // Number of pixels to indent tree nodes (relative to parent node). - // Default is 19 but can be overridden by setting CSS class dijitTreeIndent - // and calling resize() or startup() on tree after it's in the DOM. - _nodePixelIndent: 19, - - _publish: function(/*String*/ topicName, /*Object*/ message){ - // summary: - // Publish a message for this widget/topic - dojo.publish(this.id, [dojo.mixin({tree: this, event: topicName}, message || {})]); - }, - - postMixInProperties: function(){ - this.tree = this; - - if(this.autoExpand){ - // There's little point in saving opened/closed state of nodes for a Tree - // that initially opens all it's nodes. - this.persist = false; - } - - this._itemNodesMap={}; - - if(!this.cookieName){ - this.cookieName = this.id + "SaveStateCookie"; - } - - this._loadDeferred = new dojo.Deferred(); - - this.inherited(arguments); - }, - - postCreate: function(){ - this._initState(); - - // Create glue between store and Tree, if not specified directly by user - if(!this.model){ - this._store2model(); - } - - // monitor changes to items - this.connect(this.model, "onChange", "_onItemChange"); - this.connect(this.model, "onChildrenChange", "_onItemChildrenChange"); - this.connect(this.model, "onDelete", "_onItemDelete"); - - this._load(); - - this.inherited(arguments); - - if(this.dndController){ - if(dojo.isString(this.dndController)){ - this.dndController = dojo.getObject(this.dndController); - } - var params={}; - for(var i=0; i<this.dndParams.length;i++){ - if(this[this.dndParams[i]]){ - params[this.dndParams[i]] = this[this.dndParams[i]]; - } - } - this.dndController = new this.dndController(this, params); - } - }, - - _store2model: function(){ - // summary: - // User specified a store&query rather than model, so create model from store/query - this._v10Compat = true; - dojo.deprecated("Tree: from version 2.0, should specify a model object rather than a store/query"); - - var modelParams = { - id: this.id + "_ForestStoreModel", - store: this.store, - query: this.query, - childrenAttrs: this.childrenAttr - }; - - // Only override the model's mayHaveChildren() method if the user has specified an override - if(this.params.mayHaveChildren){ - modelParams.mayHaveChildren = dojo.hitch(this, "mayHaveChildren"); - } - - if(this.params.getItemChildren){ - modelParams.getChildren = dojo.hitch(this, function(item, onComplete, onError){ - this.getItemChildren((this._v10Compat && item === this.model.root) ? null : item, onComplete, onError); - }); - } - this.model = new dijit.tree.ForestStoreModel(modelParams); - - // For backwards compatibility, the visibility of the root node is controlled by - // whether or not the user has specified a label - this.showRoot = Boolean(this.label); - }, - - onLoad: function(){ - // summary: - // Called when tree finishes loading and expanding. - // description: - // If persist == true the loading may encompass many levels of fetches - // from the data store, each asynchronous. Waits for all to finish. - // tags: - // callback - }, - - _load: function(){ - // summary: - // Initial load of the tree. - // Load root node (possibly hidden) and it's children. - this.model.getRoot( - dojo.hitch(this, function(item){ - var rn = (this.rootNode = this.tree._createTreeNode({ - item: item, - tree: this, - isExpandable: true, - label: this.label || this.getLabel(item), - indent: this.showRoot ? 0 : -1 - })); - if(!this.showRoot){ - rn.rowNode.style.display="none"; - } - this.domNode.appendChild(rn.domNode); - var identity = this.model.getIdentity(item); - if(this._itemNodesMap[identity]){ - this._itemNodesMap[identity].push(rn); - }else{ - this._itemNodesMap[identity] = [rn]; - } - - rn._updateLayout(); // sets "dijitTreeIsRoot" CSS classname - - // load top level children and then fire onLoad() event - this._expandNode(rn).addCallback(dojo.hitch(this, function(){ - this._loadDeferred.callback(true); - this.onLoad(); - })); - }), - function(err){ - console.error(this, ": error loading root: ", err); - } - ); - }, - - getNodesByItem: function(/*dojo.data.Item or id*/ item){ - // summary: - // Returns all tree nodes that refer to an item - // returns: - // Array of tree nodes that refer to passed item - - if(!item){ return []; } - var identity = dojo.isString(item) ? item : this.model.getIdentity(item); - // return a copy so widget don't get messed up by changes to returned array - return [].concat(this._itemNodesMap[identity]); - }, - - _setSelectedItemAttr: function(/*dojo.data.Item or id*/ item){ - // summary: - // Select a tree node related to passed item. - // WARNING: if model use multi-parented items or desired tree node isn't already loaded - // behavior is undefined. Use set('path', ...) instead. - - var oldValue = this.get("selectedItem"); - var identity = (!item || dojo.isString(item)) ? item : this.model.getIdentity(item); - if(identity == oldValue ? this.model.getIdentity(oldValue) : null){ return; } - var nodes = this._itemNodesMap[identity]; - this._selectNode((nodes && nodes[0]) || null); //select the first item - }, - - _getSelectedItemAttr: function(){ - // summary: - // Return item related to selected tree node. - return this.selectedNode && this.selectedNode.item; - }, - - _setPathAttr: function(/*Item[] || String[]*/ path){ - // summary: - // Select the tree node identified by passed path. - // path: - // Array of items or item id's - // returns: - // Deferred to indicate when the set is complete - - var d = new dojo.Deferred(); - - this._selectNode(null); - if(!path || !path.length){ - d.resolve(true); - return d; - } - - // If this is called during initialization, defer running until Tree has finished loading - this._loadDeferred.addCallback(dojo.hitch(this, function(){ - if(!this.rootNode){ - d.reject(new Error("!this.rootNode")); - return; - } - if(path[0] !== this.rootNode.item && (dojo.isString(path[0]) && path[0] != this.model.getIdentity(this.rootNode.item))){ - d.reject(new Error(this.id + ":path[0] doesn't match this.rootNode.item. Maybe you are using the wrong tree.")); - return; - } - path.shift(); - - var node = this.rootNode; - - function advance(){ - // summary: - // Called when "node" has completed loading and expanding. Pop the next item from the path - // (which must be a child of "node") and advance to it, and then recurse. - - // Set item and identity to next item in path (node is pointing to the item that was popped - // from the path _last_ time. - var item = path.shift(), - identity = dojo.isString(item) ? item : this.model.getIdentity(item); - - // Change "node" from previous item in path to the item we just popped from path - dojo.some(this._itemNodesMap[identity], function(n){ - if(n.getParent() == node){ - node = n; - return true; - } - return false; - }); - - if(path.length){ - // Need to do more expanding - this._expandNode(node).addCallback(dojo.hitch(this, advance)); - }else{ - // Final destination node, select it - this._selectNode(node); - - // signal that path setting is finished - d.resolve(true); - } - } - - this._expandNode(node).addCallback(dojo.hitch(this, advance)); - })); - - return d; - }, - - _getPathAttr: function(){ - // summary: - // Return an array of items that is the path to selected tree node. - if(!this.selectedNode){ return; } - var res = []; - var treeNode = this.selectedNode; - while(treeNode && treeNode !== this.rootNode){ - res.unshift(treeNode.item); - treeNode = treeNode.getParent(); - } - res.unshift(this.rootNode.item); - return res; - }, - - ////////////// Data store related functions ////////////////////// - // These just get passed to the model; they are here for back-compat - - mayHaveChildren: function(/*dojo.data.Item*/ item){ - // summary: - // Deprecated. This should be specified on the model itself. - // - // Overridable function to tell if an item has or may have children. - // Controls whether or not +/- expando icon is shown. - // (For efficiency reasons we may not want to check if an element actually - // has children until user clicks the expando node) - // tags: - // deprecated - }, - - getItemChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete){ - // summary: - // Deprecated. This should be specified on the model itself. - // - // Overridable function that return array of child items of given parent item, - // or if parentItem==null then return top items in tree - // tags: - // deprecated - }, - - /////////////////////////////////////////////////////// - // Functions for converting an item to a TreeNode - getLabel: function(/*dojo.data.Item*/ item){ - // summary: - // Overridable function to get the label for a tree node (given the item) - // tags: - // extension - return this.model.getLabel(item); // String - }, - - getIconClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){ - // summary: - // Overridable function to return CSS class name to display icon - // tags: - // extension - return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "dijitLeaf" - }, - - getLabelClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){ - // summary: - // Overridable function to return CSS class name to display label - // tags: - // extension - }, - - getRowClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){ - // summary: - // Overridable function to return CSS class name to display row - // tags: - // extension - }, - - getIconStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){ - // summary: - // Overridable function to return CSS styles to display icon - // returns: - // Object suitable for input to dojo.style() like {backgroundImage: "url(...)"} - // tags: - // extension - }, - - getLabelStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){ - // summary: - // Overridable function to return CSS styles to display label - // returns: - // Object suitable for input to dojo.style() like {color: "red", background: "green"} - // tags: - // extension - }, - - getRowStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){ - // summary: - // Overridable function to return CSS styles to display row - // returns: - // Object suitable for input to dojo.style() like {background-color: "#bbb"} - // tags: - // extension - }, - - getTooltip: function(/*dojo.data.Item*/ item){ - // summary: - // Overridable function to get the tooltip for a tree node (given the item) - // tags: - // extension - return ""; // String - }, - - /////////// Keyboard and Mouse handlers //////////////////// - - _onKeyPress: function(/*Event*/ e){ - // summary: - // Translates keypress events into commands for the controller - if(e.altKey){ return; } - var dk = dojo.keys; - var treeNode = dijit.getEnclosingWidget(e.target); - if(!treeNode){ return; } - - var key = e.charOrCode; - if(typeof key == "string"){ // handle printables (letter navigation) - // Check for key navigation. - if(!e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey){ - this._onLetterKeyNav( { node: treeNode, key: key.toLowerCase() } ); - dojo.stopEvent(e); - } - }else{ // handle non-printables (arrow keys) - // clear record of recent printables (being saved for multi-char letter navigation), - // because "a", down-arrow, "b" shouldn't search for "ab" - if(this._curSearch){ - clearTimeout(this._curSearch.timer); - delete this._curSearch; - } - - var map = this._keyHandlerMap; - if(!map){ - // setup table mapping keys to events - map = {}; - map[dk.ENTER]="_onEnterKey"; - map[this.isLeftToRight() ? dk.LEFT_ARROW : dk.RIGHT_ARROW]="_onLeftArrow"; - map[this.isLeftToRight() ? dk.RIGHT_ARROW : dk.LEFT_ARROW]="_onRightArrow"; - map[dk.UP_ARROW]="_onUpArrow"; - map[dk.DOWN_ARROW]="_onDownArrow"; - map[dk.HOME]="_onHomeKey"; - map[dk.END]="_onEndKey"; - this._keyHandlerMap = map; - } - if(this._keyHandlerMap[key]){ - this[this._keyHandlerMap[key]]( { node: treeNode, item: treeNode.item, evt: e } ); - dojo.stopEvent(e); - } - } - }, - - _onEnterKey: function(/*Object*/ message, /*Event*/ evt){ - this._publish("execute", { item: message.item, node: message.node } ); - this._selectNode(message.node); - this.onClick(message.item, message.node, evt); - }, - - _onDownArrow: function(/*Object*/ message){ - // summary: - // down arrow pressed; get next visible node, set focus there - var node = this._getNextNode(message.node); - if(node && node.isTreeNode){ - this.focusNode(node); - } - }, - - _onUpArrow: function(/*Object*/ message){ - // summary: - // Up arrow pressed; move to previous visible node - - var node = message.node; - - // if younger siblings - var previousSibling = node.getPreviousSibling(); - if(previousSibling){ - node = previousSibling; - // if the previous node is expanded, dive in deep - while(node.isExpandable && node.isExpanded && node.hasChildren()){ - // move to the last child - var children = node.getChildren(); - node = children[children.length-1]; - } - }else{ - // if this is the first child, return the parent - // unless the parent is the root of a tree with a hidden root - var parent = node.getParent(); - if(!(!this.showRoot && parent === this.rootNode)){ - node = parent; - } - } - - if(node && node.isTreeNode){ - this.focusNode(node); - } - }, - - _onRightArrow: function(/*Object*/ message){ - // summary: - // Right arrow pressed; go to child node - var node = message.node; - - // if not expanded, expand, else move to 1st child - if(node.isExpandable && !node.isExpanded){ - this._expandNode(node); - }else if(node.hasChildren()){ - node = node.getChildren()[0]; - if(node && node.isTreeNode){ - this.focusNode(node); - } - } - }, - - _onLeftArrow: function(/*Object*/ message){ - // summary: - // Left arrow pressed. - // If not collapsed, collapse, else move to parent. - - var node = message.node; - - if(node.isExpandable && node.isExpanded){ - this._collapseNode(node); - }else{ - var parent = node.getParent(); - if(parent && parent.isTreeNode && !(!this.showRoot && parent === this.rootNode)){ - this.focusNode(parent); - } - } - }, - - _onHomeKey: function(){ - // summary: - // Home key pressed; get first visible node, and set focus there - var node = this._getRootOrFirstNode(); - if(node){ - this.focusNode(node); - } - }, - - _onEndKey: function(/*Object*/ message){ - // summary: - // End key pressed; go to last visible node. - - var node = this.rootNode; - while(node.isExpanded){ - var c = node.getChildren(); - node = c[c.length - 1]; - } - - if(node && node.isTreeNode){ - this.focusNode(node); - } - }, - - // multiCharSearchDuration: Number - // If multiple characters are typed where each keystroke happens within - // multiCharSearchDuration of the previous keystroke, - // search for nodes matching all the keystrokes. - // - // For example, typing "ab" will search for entries starting with - // "ab" unless the delay between "a" and "b" is greater than multiCharSearchDuration. - multiCharSearchDuration: 250, - - _onLetterKeyNav: function(message){ - // summary: - // Called when user presses a prinatable key; search for node starting with recently typed letters. - // message: Object - // Like { node: TreeNode, key: 'a' } where key is the key the user pressed. - - // Branch depending on whether this key starts a new search, or modifies an existing search - var cs = this._curSearch; - if(cs){ - // We are continuing a search. Ex: user has pressed 'a', and now has pressed - // 'b', so we want to search for nodes starting w/"ab". - cs.pattern = cs.pattern + message.key; - clearTimeout(cs.timer); - }else{ - // We are starting a new search - cs = this._curSearch = { - pattern: message.key, - startNode: message.node - }; - } - - // set/reset timer to forget recent keystrokes - var self = this; - cs.timer = setTimeout(function(){ - delete self._curSearch; - }, this.multiCharSearchDuration); - - // Navigate to TreeNode matching keystrokes [entered so far]. - var node = cs.startNode; - do{ - node = this._getNextNode(node); - //check for last node, jump to first node if necessary - if(!node){ - node = this._getRootOrFirstNode(); - } - }while(node !== cs.startNode && (node.label.toLowerCase().substr(0, cs.pattern.length) != cs.pattern)); - if(node && node.isTreeNode){ - // no need to set focus if back where we started - if(node !== cs.startNode){ - this.focusNode(node); - } - } - }, - - _onClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){ - // summary: - // Translates click events into commands for the controller to process - - var domElement = e.target, - isExpandoClick = (domElement == nodeWidget.expandoNode || domElement == nodeWidget.expandoNodeText); - - if( (this.openOnClick && nodeWidget.isExpandable) || isExpandoClick ){ - // expando node was clicked, or label of a folder node was clicked; open it - if(nodeWidget.isExpandable){ - this._onExpandoClick({node:nodeWidget}); - } - }else{ - this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } ); - this.onClick(nodeWidget.item, nodeWidget, e); - this.focusNode(nodeWidget); - } - if(!isExpandoClick){ - this._selectNode(nodeWidget); - } - dojo.stopEvent(e); - }, - _onDblClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){ - // summary: - // Translates double-click events into commands for the controller to process - - var domElement = e.target, - isExpandoClick = (domElement == nodeWidget.expandoNode || domElement == nodeWidget.expandoNodeText); - - if( (this.openOnDblClick && nodeWidget.isExpandable) ||isExpandoClick ){ - // expando node was clicked, or label of a folder node was clicked; open it - if(nodeWidget.isExpandable){ - this._onExpandoClick({node:nodeWidget}); - } - }else{ - this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } ); - this.onDblClick(nodeWidget.item, nodeWidget, e); - this.focusNode(nodeWidget); - } - if(!isExpandoClick){ - this._selectNode(nodeWidget); - } - dojo.stopEvent(e); - }, - - _onExpandoClick: function(/*Object*/ message){ - // summary: - // User clicked the +/- icon; expand or collapse my children. - var node = message.node; - - // If we are collapsing, we might be hiding the currently focused node. - // Also, clicking the expando node might have erased focus from the current node. - // For simplicity's sake just focus on the node with the expando. - this.focusNode(node); - - if(node.isExpanded){ - this._collapseNode(node); - }else{ - this._expandNode(node); - } - }, - - onClick: function(/* dojo.data */ item, /*TreeNode*/ node, /*Event*/ evt){ - // summary: - // Callback when a tree node is clicked - // tags: - // callback - }, - onDblClick: function(/* dojo.data */ item, /*TreeNode*/ node, /*Event*/ evt){ - // summary: - // Callback when a tree node is double-clicked - // tags: - // callback - }, - onOpen: function(/* dojo.data */ item, /*TreeNode*/ node){ - // summary: - // Callback when a node is opened - // tags: - // callback - }, - onClose: function(/* dojo.data */ item, /*TreeNode*/ node){ - // summary: - // Callback when a node is closed - // tags: - // callback - }, - - _getNextNode: function(node){ - // summary: - // Get next visible node - - if(node.isExpandable && node.isExpanded && node.hasChildren()){ - // if this is an expanded node, get the first child - return node.getChildren()[0]; // _TreeNode - }else{ - // find a parent node with a sibling - while(node && node.isTreeNode){ - var returnNode = node.getNextSibling(); - if(returnNode){ - return returnNode; // _TreeNode - } - node = node.getParent(); - } - return null; - } - }, - - _getRootOrFirstNode: function(){ - // summary: - // Get first visible node - return this.showRoot ? this.rootNode : this.rootNode.getChildren()[0]; - }, - - _collapseNode: function(/*_TreeNode*/ node){ - // summary: - // Called when the user has requested to collapse the node - - if(node._expandNodeDeferred){ - delete node._expandNodeDeferred; - } - - if(node.isExpandable){ - if(node.state == "LOADING"){ - // ignore clicks while we are in the process of loading data - return; - } - - node.collapse(); - this.onClose(node.item, node); - - if(node.item){ - this._state(node.item,false); - this._saveState(); - } - } - }, - - _expandNode: function(/*_TreeNode*/ node, /*Boolean?*/ recursive){ - // summary: - // Called when the user has requested to expand the node - // recursive: - // Internal flag used when _expandNode() calls itself, don't set. - // returns: - // Deferred that fires when the node is loaded and opened and (if persist=true) all it's descendants - // that were previously opened too - - if(node._expandNodeDeferred && !recursive){ - // there's already an expand in progress (or completed), so just return - return node._expandNodeDeferred; // dojo.Deferred - } - - var model = this.model, - item = node.item, - _this = this; - - switch(node.state){ - case "UNCHECKED": - // need to load all the children, and then expand - node.markProcessing(); - - // Setup deferred to signal when the load and expand are finished. - // Save that deferred in this._expandDeferred as a flag that operation is in progress. - var def = (node._expandNodeDeferred = new dojo.Deferred()); - - // Get the children - model.getChildren( - item, - function(items){ - node.unmarkProcessing(); - - // Display the children and also start expanding any children that were previously expanded - // (if this.persist == true). The returned Deferred will fire when those expansions finish. - var scid = node.setChildItems(items); - - // Call _expandNode() again but this time it will just to do the animation (default branch). - // The returned Deferred will fire when the animation completes. - // TODO: seems like I can avoid recursion and just use a deferred to sequence the events? - var ed = _this._expandNode(node, true); - - // After the above two tasks (setChildItems() and recursive _expandNode()) finish, - // signal that I am done. - scid.addCallback(function(){ - ed.addCallback(function(){ - def.callback(); - }) - }); - }, - function(err){ - console.error(_this, ": error loading root children: ", err); - } - ); - break; - - default: // "LOADED" - // data is already loaded; just expand node - def = (node._expandNodeDeferred = node.expand()); - - this.onOpen(node.item, node); - - if(item){ - this._state(item, true); - this._saveState(); - } - } - - return def; // dojo.Deferred - }, - - ////////////////// Miscellaneous functions //////////////// - - focusNode: function(/* _tree.Node */ node){ - // summary: - // Focus on the specified node (which must be visible) - // tags: - // protected - - // set focus so that the label will be voiced using screen readers - dijit.focus(node.labelNode); - }, - - _selectNode: function(/*_tree.Node*/ node){ - // summary: - // Mark specified node as select, and unmark currently selected node. - // tags: - // protected - - if(this.selectedNode && !this.selectedNode._destroyed){ - this.selectedNode.setSelected(false); - } - if(node){ - node.setSelected(true); - } - this.selectedNode = node; - }, - - _onNodeFocus: function(/*dijit._Widget*/ node){ - // summary: - // Called when a TreeNode gets focus, either by user clicking - // it, or programatically by arrow key handling code. - // description: - // It marks that the current node is the selected one, and the previously - // selected node no longer is. - - if(node && node != this.lastFocused){ - if(this.lastFocused && !this.lastFocused._destroyed){ - // mark that the previously focsable node is no longer focusable - this.lastFocused.setFocusable(false); - } - - // mark that the new node is the currently selected one - node.setFocusable(true); - this.lastFocused = node; - } - }, - - _onNodeMouseEnter: function(/*dijit._Widget*/ node){ - // summary: - // Called when mouse is over a node (onmouseenter event), - // this is monitored by the DND code - }, - - _onNodeMouseLeave: function(/*dijit._Widget*/ node){ - // summary: - // Called when mouse leaves a node (onmouseleave event), - // this is monitored by the DND code - }, - - //////////////// Events from the model ////////////////////////// - - _onItemChange: function(/*Item*/ item){ - // summary: - // Processes notification of a change to an item's scalar values like label - var model = this.model, - identity = model.getIdentity(item), - nodes = this._itemNodesMap[identity]; - - if(nodes){ - var label = this.getLabel(item), - tooltip = this.getTooltip(item); - dojo.forEach(nodes, function(node){ - node.set({ - item: item, // theoretically could be new JS Object representing same item - label: label, - tooltip: tooltip - }); - node._updateItemClasses(item); - }); - } - }, - - _onItemChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){ - // summary: - // Processes notification of a change to an item's children - var model = this.model, - identity = model.getIdentity(parent), - parentNodes = this._itemNodesMap[identity]; - - if(parentNodes){ - dojo.forEach(parentNodes,function(parentNode){ - parentNode.setChildItems(newChildrenList); - }); - } - }, - - _onItemDelete: function(/*Item*/ item){ - // summary: - // Processes notification of a deletion of an item - var model = this.model, - identity = model.getIdentity(item), - nodes = this._itemNodesMap[identity]; - - if(nodes){ - dojo.forEach(nodes,function(node){ - var parent = node.getParent(); - if(parent){ - // if node has not already been orphaned from a _onSetItem(parent, "children", ..) call... - parent.removeChild(node); - } - node.destroyRecursive(); - }); - delete this._itemNodesMap[identity]; - } - }, - - /////////////// Miscellaneous funcs - - _initState: function(){ - // summary: - // Load in which nodes should be opened automatically - if(this.persist){ - var cookie = dojo.cookie(this.cookieName); - this._openedItemIds = {}; - if(cookie){ - dojo.forEach(cookie.split(','), function(item){ - this._openedItemIds[item] = true; - }, this); - } - } - }, - _state: function(item,expanded){ - // summary: - // Query or set expanded state for an item, - if(!this.persist){ - return false; - } - var id=this.model.getIdentity(item); - if(arguments.length === 1){ - return this._openedItemIds[id]; - } - if(expanded){ - this._openedItemIds[id] = true; - }else{ - delete this._openedItemIds[id]; - } - }, - _saveState: function(){ - // summary: - // Create and save a cookie with the currently expanded nodes identifiers - if(!this.persist){ - return; - } - var ary = []; - for(var id in this._openedItemIds){ - ary.push(id); - } - dojo.cookie(this.cookieName, ary.join(","), {expires:365}); - }, - - destroy: function(){ - if(this._curSearch){ - clearTimeout(this._curSearch.timer); - delete this._curSearch; - } - if(this.rootNode){ - this.rootNode.destroyRecursive(); - } - if(this.dndController && !dojo.isString(this.dndController)){ - this.dndController.destroy(); - } - this.rootNode = null; - this.inherited(arguments); - }, - - destroyRecursive: function(){ - // A tree is treated as a leaf, not as a node with children (like a grid), - // but defining destroyRecursive for back-compat. - this.destroy(); - }, - - resize: function(changeSize){ - if(changeSize){ - dojo.marginBox(this.domNode, changeSize); - dojo.style(this.domNode, "overflow", "auto"); // for scrollbars - } - - // The only JS sizing involved w/tree is the indentation, which is specified - // in CSS and read in through this dummy indentDetector node (tree must be - // visible and attached to the DOM to read this) - this._nodePixelIndent = dojo.marginBox(this.tree.indentDetector).w; - - if(this.tree.rootNode){ - // If tree has already loaded, then reset indent for all the nodes - this.tree.rootNode.set('indent', this.showRoot ? 0 : -1); - } - }, - - _createTreeNode: function(/*Object*/ args){ - // summary: - // creates a TreeNode - // description: - // Developers can override this method to define their own TreeNode class; - // However it will probably be removed in a future release in favor of a way - // of just specifying a widget for the label, rather than one that contains - // the children too. - return new dijit._TreeNode(args); - } -}); - -// For back-compat. TODO: remove in 2.0 - - - -} - -if(!dojo._hasResource["dijit.InlineEditBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.InlineEditBox"] = true; -dojo.provide("dijit.InlineEditBox"); - - - - - - - - - - -dojo.declare("dijit.InlineEditBox", - dijit._Widget, - { - // summary: - // An element with in-line edit capabilites - // - // description: - // Behavior for an existing node (`<p>`, `<div>`, `<span>`, etc.) so that - // when you click it, an editor shows up in place of the original - // text. Optionally, Save and Cancel button are displayed below the edit widget. - // When Save is clicked, the text is pulled from the edit - // widget and redisplayed and the edit widget is again hidden. - // By default a plain Textarea widget is used as the editor (or for - // inline values a TextBox), but you can specify an editor such as - // dijit.Editor (for editing HTML) or a Slider (for adjusting a number). - // An edit widget must support the following API to be used: - // - displayedValue or value as initialization parameter, - // and available through set('displayedValue') / set('value') - // - void focus() - // - DOM-node focusNode = node containing editable text - - // editing: [readonly] Boolean - // Is the node currently in edit mode? - editing: false, - - // autoSave: Boolean - // Changing the value automatically saves it; don't have to push save button - // (and save button isn't even displayed) - autoSave: true, - - // buttonSave: String - // Save button label - buttonSave: "", - - // buttonCancel: String - // Cancel button label - buttonCancel: "", - - // renderAsHtml: Boolean - // Set this to true if the specified Editor's value should be interpreted as HTML - // rather than plain text (ex: `dijit.Editor`) - renderAsHtml: false, - - // editor: String - // Class name for Editor widget - editor: "dijit.form.TextBox", - - // editorWrapper: String - // Class name for widget that wraps the editor widget, displaying save/cancel - // buttons. - editorWrapper: "dijit._InlineEditor", - - // editorParams: Object - // Set of parameters for editor, like {required: true} - editorParams: {}, - - onChange: function(value){ - // summary: - // Set this handler to be notified of changes to value. - // tags: - // callback - }, - - onCancel: function(){ - // summary: - // Set this handler to be notified when editing is cancelled. - // tags: - // callback - }, - - // width: String - // Width of editor. By default it's width=100% (ie, block mode). - width: "100%", - - // value: String - // The display value of the widget in read-only mode - value: "", - - // noValueIndicator: [const] String - // The text that gets displayed when there is no value (so that the user has a place to click to edit) - noValueIndicator: dojo.isIE <= 6 ? // font-family needed on IE6 but it messes up IE8 - "<span style='font-family: wingdings; text-decoration: underline;'> ✍ </span>" : - "<span style='text-decoration: underline;'> ✍ </span>", - - constructor: function(){ - // summary: - // Sets up private arrays etc. - // tags: - // private - this.editorParams = {}; - }, - - postMixInProperties: function(){ - this.inherited(arguments); - - // save pointer to original source node, since Widget nulls-out srcNodeRef - this.displayNode = this.srcNodeRef; - - // connect handlers to the display node - var events = { - ondijitclick: "_onClick", - onmouseover: "_onMouseOver", - onmouseout: "_onMouseOut", - onfocus: "_onMouseOver", - onblur: "_onMouseOut" - }; - for(var name in events){ - this.connect(this.displayNode, name, events[name]); - } - dijit.setWaiRole(this.displayNode, "button"); - if(!this.displayNode.getAttribute("tabIndex")){ - this.displayNode.setAttribute("tabIndex", 0); - } - - if(!this.value && !("value" in this.params)){ // "" is a good value if specified directly so check params){ - this.value = dojo.trim(this.renderAsHtml ? this.displayNode.innerHTML : - (this.displayNode.innerText||this.displayNode.textContent||"")); - } - if(!this.value){ - this.displayNode.innerHTML = this.noValueIndicator; - } - - dojo.addClass(this.displayNode, 'dijitInlineEditBoxDisplayMode'); - }, - - setDisabled: function(/*Boolean*/ disabled){ - // summary: - // Deprecated. Use set('disabled', ...) instead. - // tags: - // deprecated - dojo.deprecated("dijit.InlineEditBox.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0"); - this.set('disabled', disabled); - }, - - _setDisabledAttr: function(/*Boolean*/ disabled){ - // summary: - // Hook to make set("disabled", ...) work. - // Set disabled state of widget. - this.disabled = disabled; - dijit.setWaiState(this.domNode, "disabled", disabled); - if(disabled){ - this.displayNode.removeAttribute("tabIndex"); - }else{ - this.displayNode.setAttribute("tabIndex", 0); - } - dojo.toggleClass(this.displayNode, "dijitInlineEditBoxDisplayModeDisabled", disabled); - }, - - _onMouseOver: function(){ - // summary: - // Handler for onmouseover and onfocus event. - // tags: - // private - if(!this.disabled){ - dojo.addClass(this.displayNode, "dijitInlineEditBoxDisplayModeHover"); - } - }, - - _onMouseOut: function(){ - // summary: - // Handler for onmouseout and onblur event. - // tags: - // private - dojo.removeClass(this.displayNode, "dijitInlineEditBoxDisplayModeHover"); - }, - - _onClick: function(/*Event*/ e){ - // summary: - // Handler for onclick event. - // tags: - // private - if(this.disabled){ return; } - if(e){ dojo.stopEvent(e); } - this._onMouseOut(); - - // Since FF gets upset if you move a node while in an event handler for that node... - setTimeout(dojo.hitch(this, "edit"), 0); - }, - - edit: function(){ - // summary: - // Display the editor widget in place of the original (read only) markup. - // tags: - // private - - if(this.disabled || this.editing){ return; } - this.editing = true; - - // save some display node values that can be restored later - this._savedPosition = dojo.style(this.displayNode, "position") || "static"; - this._savedOpacity = dojo.style(this.displayNode, "opacity") || "1"; - this._savedTabIndex = dojo.attr(this.displayNode, "tabIndex") || "0"; - - if(this.wrapperWidget){ - var ew = this.wrapperWidget.editWidget; - ew.set("displayedValue" in ew ? "displayedValue" : "value", this.value); - }else{ - // Placeholder for edit widget - // Put place holder (and eventually editWidget) before the display node so that it's positioned correctly - // when Calendar dropdown appears, which happens automatically on focus. - var placeholder = dojo.create("span", null, this.domNode, "before"); - - // Create the editor wrapper (the thing that holds the editor widget and the save/cancel buttons) - var ewc = dojo.getObject(this.editorWrapper); - this.wrapperWidget = new ewc({ - value: this.value, - buttonSave: this.buttonSave, - buttonCancel: this.buttonCancel, - dir: this.dir, - lang: this.lang, - tabIndex: this._savedTabIndex, - editor: this.editor, - inlineEditBox: this, - sourceStyle: dojo.getComputedStyle(this.displayNode), - save: dojo.hitch(this, "save"), - cancel: dojo.hitch(this, "cancel") - }, placeholder); - } - var ww = this.wrapperWidget; - - if(dojo.isIE){ - dijit.focus(dijit.getFocus()); // IE (at least 8) needs help with tab order changes - } - // to avoid screen jitter, we first create the editor with position:absolute, visibility:hidden, - // and then when it's finished rendering, we switch from display mode to editor - // position:absolute releases screen space allocated to the display node - // opacity:0 is the same as visibility:hidden but is still focusable - // visiblity:hidden removes focus outline - - dojo.style(this.displayNode, { position: "absolute", opacity: "0", display: "none" }); // makes display node invisible, display style used for focus-ability - dojo.style(ww.domNode, { position: this._savedPosition, visibility: "visible", opacity: "1" }); - dojo.attr(this.displayNode, "tabIndex", "-1"); // needed by WebKit for TAB from editor to skip displayNode - - // Replace the display widget with edit widget, leaving them both displayed for a brief time so that - // focus can be shifted without incident. (browser may needs some time to render the editor.) - setTimeout(dojo.hitch(this, function(){ - ww.focus(); // both nodes are showing, so we can switch focus safely - ww._resetValue = ww.getValue(); - }), 0); - }, - - _onBlur: function(){ - // summary: - // Called when focus moves outside the InlineEditBox. - // Performs garbage collection. - // tags: - // private - - this.inherited(arguments); - if(!this.editing){ - /* causes IE focus problems, see TooltipDialog_a11y.html... - setTimeout(dojo.hitch(this, function(){ - if(this.wrapperWidget){ - this.wrapperWidget.destroy(); - delete this.wrapperWidget; - } - }), 0); - */ - } - }, - - destroy: function(){ - if(this.wrapperWidget){ - this.wrapperWidget.destroy(); - delete this.wrapperWidget; - } - this.inherited(arguments); - }, - - _showText: function(/*Boolean*/ focus){ - // summary: - // Revert to display mode, and optionally focus on display node - // tags: - // private - - var ww = this.wrapperWidget; - dojo.style(ww.domNode, { position: "absolute", visibility: "hidden", opacity: "0" }); // hide the editor from mouse/keyboard events - dojo.style(this.displayNode, { position: this._savedPosition, opacity: this._savedOpacity, display: "" }); // make the original text visible - dojo.attr(this.displayNode, "tabIndex", this._savedTabIndex); - if(focus){ - dijit.focus(this.displayNode); - } - }, - - save: function(/*Boolean*/ focus){ - // summary: - // Save the contents of the editor and revert to display mode. - // focus: Boolean - // Focus on the display mode text - // tags: - // private - - if(this.disabled || !this.editing){ return; } - this.editing = false; - - var ww = this.wrapperWidget; - var value = ww.getValue(); - this.set('value', value); // display changed, formatted value - - // tell the world that we have changed - setTimeout(dojo.hitch(this, "onChange", value), 0); // setTimeout prevents browser freeze for long-running event handlers - - this._showText(focus); // set focus as needed - }, - - setValue: function(/*String*/ val){ - // summary: - // Deprecated. Use set('value', ...) instead. - // tags: - // deprecated - dojo.deprecated("dijit.InlineEditBox.setValue() is deprecated. Use set('value', ...) instead.", "", "2.0"); - return this.set("value", val); - }, - - _setValueAttr: function(/*String*/ val){ - // summary: - // Hook to make set("value", ...) work. - // Inserts specified HTML value into this node, or an "input needed" character if node is blank. - - this.value = val = dojo.trim(val); - if(!this.renderAsHtml){ - val = val.replace(/&/gm, "&").replace(/</gm, "<").replace(/>/gm, ">").replace(/"/gm, """).replace(/\n/g, "<br>"); - } - this.displayNode.innerHTML = val || this.noValueIndicator; - }, - - getValue: function(){ - // summary: - // Deprecated. Use get('value') instead. - // tags: - // deprecated - dojo.deprecated("dijit.InlineEditBox.getValue() is deprecated. Use get('value') instead.", "", "2.0"); - return this.get("value"); - }, - - cancel: function(/*Boolean*/ focus){ - // summary: - // Revert to display mode, discarding any changes made in the editor - // tags: - // private - - if(this.disabled || !this.editing){ return; } - this.editing = false; - - // tell the world that we have no changes - setTimeout(dojo.hitch(this, "onCancel"), 0); // setTimeout prevents browser freeze for long-running event handlers - - this._showText(focus); - } -}); - -dojo.declare( - "dijit._InlineEditor", - [dijit._Widget, dijit._Templated], -{ - // summary: - // Internal widget used by InlineEditBox, displayed when in editing mode - // to display the editor and maybe save/cancel buttons. Calling code should - // connect to save/cancel methods to detect when editing is finished - // - // Has mainly the same parameters as InlineEditBox, plus these values: - // - // style: Object - // Set of CSS attributes of display node, to replicate in editor - // - // value: String - // Value as an HTML string or plain text string, depending on renderAsHTML flag - - templateString: dojo.cache("dijit", "templates/InlineEditBox.html", "<span dojoAttachPoint=\"editNode\" waiRole=\"presentation\" style=\"position: absolute; visibility:hidden\" class=\"dijitReset dijitInline\"\n\tdojoAttachEvent=\"onkeypress: _onKeyPress\"\n\t><span dojoAttachPoint=\"editorPlaceholder\"></span\n\t><span dojoAttachPoint=\"buttonContainer\"\n\t\t><button class='saveButton' dojoAttachPoint=\"saveButton\" dojoType=\"dijit.form.Button\" dojoAttachEvent=\"onClick:save\" label=\"${buttonSave}\"></button\n\t\t><button class='cancelButton' dojoAttachPoint=\"cancelButton\" dojoType=\"dijit.form.Button\" dojoAttachEvent=\"onClick:cancel\" label=\"${buttonCancel}\"></button\n\t></span\n></span>\n"), - widgetsInTemplate: true, - - postMixInProperties: function(){ - this.inherited(arguments); - this.messages = dojo.i18n.getLocalization("dijit", "common", this.lang); - dojo.forEach(["buttonSave", "buttonCancel"], function(prop){ - if(!this[prop]){ this[prop] = this.messages[prop]; } - }, this); - }, - - postCreate: function(){ - // Create edit widget in place in the template - var cls = dojo.getObject(this.editor); - - // Copy the style from the source - // Don't copy ALL properties though, just the necessary/applicable ones. - // wrapperStyle/destStyle code is to workaround IE bug where getComputedStyle().fontSize - // is a relative value like 200%, rather than an absolute value like 24px, and - // the 200% can refer *either* to a setting on the node or it's ancestor (see #11175) - var srcStyle = this.sourceStyle, - editStyle = "line-height:" + srcStyle.lineHeight + ";", - destStyle = dojo.getComputedStyle(this.domNode); - dojo.forEach(["Weight","Family","Size","Style"], function(prop){ - var textStyle = srcStyle["font"+prop], - wrapperStyle = destStyle["font"+prop]; - if(wrapperStyle != textStyle){ - editStyle += "font-"+prop+":"+srcStyle["font"+prop]+";"; - } - }, this); - dojo.forEach(["marginTop","marginBottom","marginLeft", "marginRight"], function(prop){ - this.domNode.style[prop] = srcStyle[prop]; - }, this); - var width = this.inlineEditBox.width; - if(width == "100%"){ - // block mode - editStyle += "width:100%;"; - this.domNode.style.display = "block"; - }else{ - // inline-block mode - editStyle += "width:" + (width + (Number(width) == width ? "px" : "")) + ";"; - } - var editorParams = dojo.delegate(this.inlineEditBox.editorParams, { - style: editStyle, - dir: this.dir, - lang: this.lang - }); - editorParams[ "displayedValue" in cls.prototype ? "displayedValue" : "value"] = this.value; - var ew = (this.editWidget = new cls(editorParams, this.editorPlaceholder)); - - if(this.inlineEditBox.autoSave){ - // Remove the save/cancel buttons since saving is done by simply tabbing away or - // selecting a value from the drop down list - dojo.destroy(this.buttonContainer); - - // Selecting a value from a drop down list causes an onChange event and then we save - this.connect(ew, "onChange", "_onChange"); - - // ESC and TAB should cancel and save. Note that edit widgets do a stopEvent() on ESC key (to - // prevent Dialog from closing when the user just wants to revert the value in the edit widget), - // so this is the only way we can see the key press event. - this.connect(ew, "onKeyPress", "_onKeyPress"); - }else{ - // If possible, enable/disable save button based on whether the user has changed the value - if("intermediateChanges" in cls.prototype){ - ew.set("intermediateChanges", true); - this.connect(ew, "onChange", "_onIntermediateChange"); - this.saveButton.set("disabled", true); - } - } - }, - - _onIntermediateChange: function(val){ - // summary: - // Called for editor widgets that support the intermediateChanges=true flag as a way - // to detect when to enable/disabled the save button - this.saveButton.set("disabled", (this.getValue() == this._resetValue) || !this.enableSave()); - }, - - destroy: function(){ - this.editWidget.destroy(true); // let the parent wrapper widget clean up the DOM - this.inherited(arguments); - }, - - getValue: function(){ - // summary: - // Return the [display] value of the edit widget - var ew = this.editWidget; - return String(ew.get("displayedValue" in ew ? "displayedValue" : "value")); - }, - - _onKeyPress: function(e){ - // summary: - // Handler for keypress in the edit box in autoSave mode. - // description: - // For autoSave widgets, if Esc/Enter, call cancel/save. - // tags: - // private - - if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){ - if(e.altKey || e.ctrlKey){ return; } - // If Enter/Esc pressed, treat as save/cancel. - if(e.charOrCode == dojo.keys.ESCAPE){ - dojo.stopEvent(e); - this.cancel(true); // sets editing=false which short-circuits _onBlur processing - }else if(e.charOrCode == dojo.keys.ENTER && e.target.tagName == "INPUT"){ - dojo.stopEvent(e); - this._onChange(); // fire _onBlur and then save - } - - // _onBlur will handle TAB automatically by allowing - // the TAB to change focus before we mess with the DOM: #6227 - // Expounding by request: - // The current focus is on the edit widget input field. - // save() will hide and destroy this widget. - // We want the focus to jump from the currently hidden - // displayNode, but since it's hidden, it's impossible to - // unhide it, focus it, and then have the browser focus - // away from it to the next focusable element since each - // of these events is asynchronous and the focus-to-next-element - // is already queued. - // So we allow the browser time to unqueue the move-focus event - // before we do all the hide/show stuff. - } - }, - - _onBlur: function(){ - // summary: - // Called when focus moves outside the editor - // tags: - // private - - this.inherited(arguments); - if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){ - if(this.getValue() == this._resetValue){ - this.cancel(false); - }else if(this.enableSave()){ - this.save(false); - } - } - }, - - _onChange: function(){ - // summary: - // Called when the underlying widget fires an onChange event, - // such as when the user selects a value from the drop down list of a ComboBox, - // which means that the user has finished entering the value and we should save. - // tags: - // private - - if(this.inlineEditBox.autoSave && this.inlineEditBox.editing && this.enableSave()){ - dojo.style(this.inlineEditBox.displayNode, { display: "" }); - dijit.focus(this.inlineEditBox.displayNode); // fires _onBlur which will save the formatted value - } - }, - - enableSave: function(){ - // summary: - // User overridable function returning a Boolean to indicate - // if the Save button should be enabled or not - usually due to invalid conditions - // tags: - // extension - return ( - this.editWidget.isValid - ? this.editWidget.isValid() - : true - ); - }, - - focus: function(){ - // summary: - // Focus the edit widget. - // tags: - // protected - - this.editWidget.focus(); - setTimeout(dojo.hitch(this, function(){ - if(this.editWidget.focusNode && this.editWidget.focusNode.tagName == "INPUT"){ - dijit.selectInputText(this.editWidget.focusNode); - } - }), 0); - } -}); - -} - -if(!dojo._hasResource["dijit.form.Form"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.Form"] = true; -dojo.provide("dijit.form.Form"); - - - - - -dojo.declare( - "dijit.form.Form", - [dijit._Widget, dijit._Templated, dijit.form._FormMixin], - { - // summary: - // Widget corresponding to HTML form tag, for validation and serialization - // - // example: - // | <form dojoType="dijit.form.Form" id="myForm"> - // | Name: <input type="text" name="name" /> - // | </form> - // | myObj = {name: "John Doe"}; - // | dijit.byId('myForm').set('value', myObj); - // | - // | myObj=dijit.byId('myForm').get('value'); - - // HTML <FORM> attributes - - // name: String? - // Name of form for scripting. - name: "", - - // action: String? - // Server-side form handler. - action: "", - - // method: String? - // HTTP method used to submit the form, either "GET" or "POST". - method: "", - - // encType: String? - // Encoding type for the form, ex: application/x-www-form-urlencoded. - encType: "", - - // accept-charset: String? - // List of supported charsets. - "accept-charset": "", - - // accept: String? - // List of MIME types for file upload. - accept: "", - - // target: String? - // Target frame for the document to be opened in. - target: "", - - templateString: "<form dojoAttachPoint='containerNode' dojoAttachEvent='onreset:_onReset,onsubmit:_onSubmit' ${!nameAttrSetting}></form>", - - attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, { - action: "", - method: "", - encType: "", - "accept-charset": "", - accept: "", - target: "" - }), - - postMixInProperties: function(){ - // Setup name=foo string to be referenced from the template (but only if a name has been specified) - // Unfortunately we can't use attributeMap to set the name due to IE limitations, see #8660 - this.nameAttrSetting = this.name ? ("name='" + this.name + "'") : ""; - this.inherited(arguments); - }, - - execute: function(/*Object*/ formContents){ - // summary: - // Deprecated: use submit() - // tags: - // deprecated - }, - - onExecute: function(){ - // summary: - // Deprecated: use onSubmit() - // tags: - // deprecated - }, - - _setEncTypeAttr: function(/*String*/ value){ - this.encType = value; - dojo.attr(this.domNode, "encType", value); - if(dojo.isIE){ this.domNode.encoding = value; } - }, - - postCreate: function(){ - // IE tries to hide encType - // TODO: this code should be in parser, not here. - if(dojo.isIE && this.srcNodeRef && this.srcNodeRef.attributes){ - var item = this.srcNodeRef.attributes.getNamedItem('encType'); - if(item && !item.specified && (typeof item.value == "string")){ - this.set('encType', item.value); - } - } - this.inherited(arguments); - }, - - reset: function(/*Event?*/ e){ - // summary: - // restores all widget values back to their init values, - // calls onReset() which can cancel the reset by returning false - - // create fake event so we can know if preventDefault() is called - var faux = { - returnValue: true, // the IE way - preventDefault: function(){ // not IE - this.returnValue = false; - }, - stopPropagation: function(){}, - currentTarget: e ? e.target : this.domNode, - target: e ? e.target : this.domNode - }; - // if return value is not exactly false, and haven't called preventDefault(), then reset - if(!(this.onReset(faux) === false) && faux.returnValue){ - this.inherited(arguments, []); - } - }, - - onReset: function(/*Event?*/ e){ - // summary: - // Callback when user resets the form. This method is intended - // to be over-ridden. When the `reset` method is called - // programmatically, the return value from `onReset` is used - // to compute whether or not resetting should proceed - // tags: - // callback - return true; // Boolean - }, - - _onReset: function(e){ - this.reset(e); - dojo.stopEvent(e); - return false; - }, - - _onSubmit: function(e){ - var fp = dijit.form.Form.prototype; - // TODO: remove this if statement beginning with 2.0 - if(this.execute != fp.execute || this.onExecute != fp.onExecute){ - dojo.deprecated("dijit.form.Form:execute()/onExecute() are deprecated. Use onSubmit() instead.", "", "2.0"); - this.onExecute(); - this.execute(this.getValues()); - } - if(this.onSubmit(e) === false){ // only exactly false stops submit - dojo.stopEvent(e); - } - }, - - onSubmit: function(/*Event?*/e){ - // summary: - // Callback when user submits the form. - // description: - // This method is intended to be over-ridden, but by default it checks and - // returns the validity of form elements. When the `submit` - // method is called programmatically, the return value from - // `onSubmit` is used to compute whether or not submission - // should proceed - // tags: - // extension - - return this.isValid(); // Boolean - }, - - submit: function(){ - // summary: - // programmatically submit form if and only if the `onSubmit` returns true - if(!(this.onSubmit() === false)){ - this.containerNode.submit(); - } - } - } -); - -} - -if(!dojo._hasResource["dijit.form.DropDownButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.DropDownButton"] = true; -dojo.provide("dijit.form.DropDownButton"); - - - -} - -if(!dojo._hasResource["dijit.form.ComboButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.ComboButton"] = true; -dojo.provide("dijit.form.ComboButton"); - - -} - -if(!dojo._hasResource["dijit.form.ToggleButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.ToggleButton"] = true; -dojo.provide("dijit.form.ToggleButton"); - - -} - -if(!dojo._hasResource["dijit.form.CheckBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.CheckBox"] = true; -dojo.provide("dijit.form.CheckBox"); - - - -dojo.declare( - "dijit.form.CheckBox", - dijit.form.ToggleButton, - { - // summary: - // Same as an HTML checkbox, but with fancy styling. - // - // description: - // User interacts with real html inputs. - // On onclick (which occurs by mouse click, space-bar, or - // using the arrow keys to switch the selected radio button), - // we update the state of the checkbox/radio. - // - // There are two modes: - // 1. High contrast mode - // 2. Normal mode - // - // In case 1, the regular html inputs are shown and used by the user. - // In case 2, the regular html inputs are invisible but still used by - // the user. They are turned quasi-invisible and overlay the background-image. - - templateString: dojo.cache("dijit.form", "templates/CheckBox.html", "<div class=\"dijit dijitReset dijitInline\" waiRole=\"presentation\"\n\t><input\n\t \t${!nameAttrSetting} type=\"${type}\" ${checkedAttrSetting}\n\t\tclass=\"dijitReset dijitCheckBoxInput\"\n\t\tdojoAttachPoint=\"focusNode\"\n\t \tdojoAttachEvent=\"onclick:_onClick\"\n/></div>\n"), - - baseClass: "dijitCheckBox", - - // type: [private] String - // type attribute on <input> node. - // Overrides `dijit.form.Button.type`. Users should not change this value. - type: "checkbox", - - // value: String - // As an initialization parameter, equivalent to value field on normal checkbox - // (if checked, the value is passed as the value when form is submitted). - // - // However, attr('value') will return either the string or false depending on - // whether or not the checkbox is checked. - // - // attr('value', string) will check the checkbox and change the value to the - // specified string - // - // attr('value', boolean) will change the checked state. - value: "on", - - // readOnly: Boolean - // Should this widget respond to user input? - // In markup, this is specified as "readOnly". - // Similar to disabled except readOnly form values are submitted. - readOnly: false, - - // the attributeMap should inherit from dijit.form._FormWidget.prototype.attributeMap - // instead of ToggleButton as the icon mapping has no meaning for a CheckBox - attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, { - readOnly: "focusNode" - }), - - _setReadOnlyAttr: function(/*Boolean*/ value){ - this.readOnly = value; - dojo.attr(this.focusNode, 'readOnly', value); - dijit.setWaiState(this.focusNode, "readonly", value); - }, - - _setValueAttr: function(/*String or Boolean*/ newValue, /*Boolean*/ priorityChange){ - // summary: - // Handler for value= attribute to constructor, and also calls to - // attr('value', val). - // description: - // During initialization, just saves as attribute to the <input type=checkbox>. - // - // After initialization, - // when passed a boolean, controls whether or not the CheckBox is checked. - // If passed a string, changes the value attribute of the CheckBox (the one - // specified as "value" when the CheckBox was constructed (ex: <input - // dojoType="dijit.CheckBox" value="chicken">) - if(typeof newValue == "string"){ - this.value = newValue; - dojo.attr(this.focusNode, 'value', newValue); - newValue = true; - } - if(this._created){ - this.set('checked', newValue, priorityChange); - } - }, - _getValueAttr: function(){ - // summary: - // Hook so attr('value') works. - // description: - // If the CheckBox is checked, returns the value attribute. - // Otherwise returns false. - return (this.checked ? this.value : false); - }, - - // Override dijit.form.Button._setLabelAttr() since we don't even have a containerNode. - // Normally users won't try to set label, except when CheckBox or RadioButton is the child of a dojox.layout.TabContainer - _setLabelAttr: undefined, - - postMixInProperties: function(){ - if(this.value == ""){ - this.value = "on"; - } - - // Need to set initial checked state as part of template, so that form submit works. - // dojo.attr(node, "checked", bool) doesn't work on IEuntil node has been attached - // to <body>, see #8666 - this.checkedAttrSetting = this.checked ? "checked" : ""; - - this.inherited(arguments); - }, - - _fillContent: function(/*DomNode*/ source){ - // Override Button::_fillContent() since it doesn't make sense for CheckBox, - // since CheckBox doesn't even have a container - }, - - reset: function(){ - // Override ToggleButton.reset() - - this._hasBeenBlurred = false; - - this.set('checked', this.params.checked || false); - - // Handle unlikely event that the <input type=checkbox> value attribute has changed - this.value = this.params.value || "on"; - dojo.attr(this.focusNode, 'value', this.value); - }, - - _onFocus: function(){ - if(this.id){ - dojo.query("label[for='"+this.id+"']").addClass("dijitFocusedLabel"); - } - this.inherited(arguments); - }, - - _onBlur: function(){ - if(this.id){ - dojo.query("label[for='"+this.id+"']").removeClass("dijitFocusedLabel"); - } - this.inherited(arguments); - }, - - _onClick: function(/*Event*/ e){ - // summary: - // Internal function to handle click actions - need to check - // readOnly, since button no longer does that check. - if(this.readOnly){ - return false; - } - return this.inherited(arguments); - } - } -); - -dojo.declare( - "dijit.form.RadioButton", - dijit.form.CheckBox, - { - // summary: - // Same as an HTML radio, but with fancy styling. - - type: "radio", - baseClass: "dijitRadio", - - _setCheckedAttr: function(/*Boolean*/ value){ - // If I am being checked then have to deselect currently checked radio button - this.inherited(arguments); - if(!this._created){ return; } - if(value){ - var _this = this; - // search for radio buttons with the same name that need to be unchecked - dojo.query("INPUT[type=radio]", this.focusNode.form || dojo.doc).forEach( // can't use name= since dojo.query doesn't support [] in the name - function(inputNode){ - if(inputNode.name == _this.name && inputNode != _this.focusNode && inputNode.form == _this.focusNode.form){ - var widget = dijit.getEnclosingWidget(inputNode); - if(widget && widget.checked){ - widget.set('checked', false); - } - } - } - ); - } - }, - - _clicked: function(/*Event*/ e){ - if(!this.checked){ - this.set('checked', true); - } - } - } -); - -} - -if(!dojo._hasResource["dijit.form.RadioButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.RadioButton"] = true; -dojo.provide("dijit.form.RadioButton"); - - -// TODO: for 2.0, move the RadioButton code into this file - -} - -if(!dojo._hasResource["dojo.cldr.monetary"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.cldr.monetary"] = true; -dojo.provide("dojo.cldr.monetary"); - -dojo.cldr.monetary.getData = function(/*String*/code){ -// summary: A mapping of currency code to currency-specific formatting information. Returns a unique object with properties: places, round. -// code: an [ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code - -// from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/currencyData/fractions - - var placesData = { - ADP:0,AFN:0,ALL:0,AMD:0,BHD:3,BIF:0,BYR:0,CLF:0,CLP:0, - COP:0,CRC:0,DJF:0,ESP:0,GNF:0,GYD:0,HUF:0,IDR:0,IQD:0, - IRR:3,ISK:0,ITL:0,JOD:3,JPY:0,KMF:0,KPW:0,KRW:0,KWD:3, - LAK:0,LBP:0,LUF:0,LYD:3,MGA:0,MGF:0,MMK:0,MNT:0,MRO:0, - MUR:0,OMR:3,PKR:0,PYG:0,RSD:0,RWF:0,SLL:0,SOS:0,STD:0, - SYP:0,TMM:0,TND:3,TRL:0,TZS:0,UGX:0,UZS:0,VND:0,VUV:0, - XAF:0,XOF:0,XPF:0,YER:0,ZMK:0,ZWD:0 - }; - - var roundingData = {CHF:5}; - - var places = placesData[code], round = roundingData[code]; - if(typeof places == "undefined"){ places = 2; } - if(typeof round == "undefined"){ round = 0; } - - return {places: places, round: round}; // Object -}; - -} - -if(!dojo._hasResource["dojo.currency"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.currency"] = true; -dojo.provide("dojo.currency"); - - - - - - -/*===== -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 = dojo.i18n.getLocalization("dojo.cldr", "currency", options.locale) || {}; - - // Mixin locale-independent currency data, like # of places - var iso = options.currency; - var data = dojo.cldr.monetary.getData(iso); - - dojo.forEach(["displayName","symbol","group","decimal"], function(prop){ - data[prop] = bundle[iso+"_"+prop]; - }); - - data.fractional = [true, false]; - - // Mixin with provided options - return dojo.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 dojo.number.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 dojo.number.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 dojo.number.parse(expression, dojo.currency._mixInDefaults(options)); -} - -} - -if(!dojo._hasResource["dijit.form.NumberTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.NumberTextBox"] = true; -dojo.provide("dijit.form.NumberTextBox"); - - - - -/*===== -dojo.declare( - "dijit.form.NumberTextBox.__Constraints", - [dijit.form.RangeBoundTextBox.__Constraints, dojo.number.__FormatOptions, dojo.number.__ParseOptions], { - // summary: - // Specifies both the rules on valid/invalid values (minimum, maximum, - // number of required decimal places), and also formatting options for - // displaying the value when the field is not focused. - // example: - // Minimum/maximum: - // To specify a field between 0 and 120: - // | {min:0,max:120} - // To specify a field that must be an integer: - // | {fractional:false} - // To specify a field where 0 to 3 decimal places are allowed on input, - // but after the field is blurred the value is displayed with 3 decimal places: - // | {places:'0,3'} -}); -=====*/ - -dojo.declare("dijit.form.NumberTextBoxMixin", - null, - { - // summary: - // A mixin for all number textboxes - // tags: - // protected - - // Override ValidationTextBox.regExpGen().... we use a reg-ex generating function rather - // than a straight regexp to deal with locale (plus formatting options too?) - regExpGen: dojo.number.regexp, - - /*===== - // constraints: dijit.form.NumberTextBox.__Constraints - // Despite the name, this parameter specifies both constraints on the input - // (including minimum/maximum allowed values) as well as - // formatting options like places (the number of digits to display after - // the decimal point). See `dijit.form.NumberTextBox.__Constraints` for details. - constraints: {}, - ======*/ - - // value: Number - // The value of this NumberTextBox as a Javascript Number (i.e., not a String). - // If the displayed value is blank, the value is NaN, and if the user types in - // an gibberish value (like "hello world"), the value is undefined - // (i.e. attr('value') returns undefined). - // - // Symmetrically, attr('value', NaN) will clear the displayed value, - // whereas attr('value', undefined) will have no effect. - value: NaN, - - // editOptions: [protected] Object - // Properties to mix into constraints when the value is being edited. - // This is here because we edit the number in the format "12345", which is - // different than the display value (ex: "12,345") - editOptions: { pattern: '#.######' }, - - /*===== - _formatter: function(value, options){ - // summary: - // _formatter() is called by format(). It's the base routine for formatting a number, - // as a string, for example converting 12345 into "12,345". - // value: Number - // The number to be converted into a string. - // options: dojo.number.__FormatOptions? - // Formatting options - // tags: - // protected extension - - return "12345"; // String - }, - =====*/ - _formatter: dojo.number.format, - - _setConstraintsAttr: function(/* Object */ constraints){ - var places = typeof constraints.places == "number"? constraints.places : 0; - if(places){ places++; } // decimal rounding errors take away another digit of precision - if(typeof constraints.max != "number"){ - constraints.max = 9 * Math.pow(10, 15-places); - } - if(typeof constraints.min != "number"){ - constraints.min = -9 * Math.pow(10, 15-places); - } - this.inherited(arguments, [ constraints ]); - if(this.focusNode && this.focusNode.value && !isNaN(this.value)){ - this.set('value', this.value); - } - }, - - _onFocus: function(){ - if(this.disabled){ return; } - var val = this.get('value'); - if(typeof val == "number" && !isNaN(val)){ - var formattedValue = this.format(val, this.constraints); - if(formattedValue !== undefined){ - this.textbox.value = formattedValue; - } - } - this.inherited(arguments); - }, - - format: function(/*Number*/ value, /*dojo.number.__FormatOptions*/ constraints){ - // summary: - // Formats the value as a Number, according to constraints. - // tags: - // protected - - var formattedValue = String(value); - if(typeof value != "number"){ return formattedValue; } - if(isNaN(value)){ return ""; } - // check for exponential notation that dojo.number.format chokes on - if(!("rangeCheck" in this && this.rangeCheck(value, constraints)) && constraints.exponent !== false && /\de[-+]?\d/i.test(formattedValue)){ - return formattedValue; - } - if(this.editOptions && this._focused){ - constraints = dojo.mixin({}, constraints, this.editOptions); - } - return this._formatter(value, constraints); - }, - - /*===== - parse: function(value, constraints){ - // summary: - // Parses the string value as a Number, according to constraints. - // value: String - // String representing a number - // constraints: dojo.number.__ParseOptions - // Formatting options - // tags: - // protected - - return 123.45; // Number - }, - =====*/ - parse: dojo.number.parse, - - _getDisplayedValueAttr: function(){ - var v = this.inherited(arguments); - return isNaN(v) ? this.textbox.value : v; - }, - - filter: function(/*Number*/ value){ - // summary: - // This is called with both the display value (string), and the actual value (a number). - // When called with the actual value it does corrections so that '' etc. are represented as NaN. - // Otherwise it dispatches to the superclass's filter() method. - // - // See `dijit.form.TextBox.filter` for more details. - return (value === null || value === '' || value === undefined) ? NaN : this.inherited(arguments); // attr('value', null||''||undefined) should fire onChange(NaN) - }, - - serialize: function(/*Number*/ value, /*Object?*/options){ - // summary: - // Convert value (a Number) into a canonical string (ie, how the number literal is written in javascript/java/C/etc.) - // tags: - // protected - return (typeof value != "number" || isNaN(value)) ? '' : this.inherited(arguments); - }, - - _setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange, /*String?*/formattedValue){ - // summary: - // Hook so attr('value', ...) works. - if(value !== undefined && formattedValue === undefined){ - formattedValue = String(value); - if(typeof value == "number"){ - if(isNaN(value)){ formattedValue = '' } - // check for exponential notation that dojo.number.format chokes on - else if(("rangeCheck" in this && this.rangeCheck(value, this.constraints)) || this.constraints.exponent === false || !/\de[-+]?\d/i.test(formattedValue)){ - formattedValue = undefined; // lets format comnpute a real string value - } - }else if(!value){ // 0 processed in if branch above, ''|null|undefined flow thru here - formattedValue = ''; - value = NaN; - }else{ // non-numeric values - value = undefined; - } - } - this.inherited(arguments, [value, priorityChange, formattedValue]); - }, - - _getValueAttr: function(){ - // summary: - // Hook so attr('value') works. - // Returns Number, NaN for '', or undefined for unparsable text - var v = this.inherited(arguments); // returns Number for all values accepted by parse() or NaN for all other displayed values - - // If the displayed value of the textbox is gibberish (ex: "hello world"), this.inherited() above - // returns NaN; this if() branch converts the return value to undefined. - // Returning undefined prevents user text from being overwritten when doing _setValueAttr(_getValueAttr()). - // A blank displayed value is still returned as NaN. - if(isNaN(v) && this.textbox.value !== ''){ - if(this.constraints.exponent !== false && /\de[-+]?\d/i.test(this.textbox.value) && (new RegExp("^"+dojo.number._realNumberRegexp(dojo.mixin({}, this.constraints))+"$").test(this.textbox.value))){ // check for exponential notation that parse() rejected (erroneously?) - var n = Number(this.textbox.value); - return isNaN(n) ? undefined : n; // return exponential Number or undefined for random text (may not be possible to do with the above RegExp check) - }else{ - return undefined; // gibberish - } - }else{ - return v; // Number or NaN for '' - } - }, - - isValid: function(/*Boolean*/ isFocused){ - // Overrides dijit.form.RangeBoundTextBox.isValid to check that the editing-mode value is valid since - // it may not be formatted according to the regExp vaidation rules - if(!this._focused || this._isEmpty(this.textbox.value)){ - return this.inherited(arguments); - }else{ - var v = this.get('value'); - if(!isNaN(v) && this.rangeCheck(v, this.constraints)){ - if(this.constraints.exponent !== false && /\de[-+]?\d/i.test(this.textbox.value)){ // exponential, parse doesn't like it - return true; // valid exponential number in range - }else{ - return this.inherited(arguments); - } - }else{ - return false; - } - } - } - } -); - -dojo.declare("dijit.form.NumberTextBox", - [dijit.form.RangeBoundTextBox,dijit.form.NumberTextBoxMixin], - { - // summary: - // A TextBox for entering numbers, with formatting and range checking - // description: - // NumberTextBox is a textbox for entering and displaying numbers, supporting - // the following main features: - // - // 1. Enforce minimum/maximum allowed values (as well as enforcing that the user types - // a number rather than a random string) - // 2. NLS support (altering roles of comma and dot as "thousands-separator" and "decimal-point" - // depending on locale). - // 3. Separate modes for editing the value and displaying it, specifically that - // the thousands separator character (typically comma) disappears when editing - // but reappears after the field is blurred. - // 4. Formatting and constraints regarding the number of places (digits after the decimal point) - // allowed on input, and number of places displayed when blurred (see `constraints` parameter). - } -); - -} - -if(!dojo._hasResource["dijit.form.CurrencyTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.CurrencyTextBox"] = true; -dojo.provide("dijit.form.CurrencyTextBox"); - - - - -/*===== -dojo.declare( - "dijit.form.CurrencyTextBox.__Constraints", - [dijit.form.NumberTextBox.__Constraints, dojo.currency.__FormatOptions, dojo.currency.__ParseOptions], { - // summary: - // Specifies both the rules on valid/invalid values (minimum, maximum, - // number of required decimal places), and also formatting options for - // displaying the value when the field is not focused (currency symbol, - // etc.) - // description: - // Follows the pattern of `dijit.form.NumberTextBox.constraints`. - // In general developers won't need to set this parameter - // example: - // To ensure that the user types in the cents (for example, 1.00 instead of just 1): - // | {fractional:true} -}); -=====*/ - -dojo.declare( - "dijit.form.CurrencyTextBox", - dijit.form.NumberTextBox, - { - // summary: - // A validating currency textbox - // description: - // CurrencyTextBox is similar to `dijit.form.NumberTextBox` but has a few - // extra features related to currency: - // - // 1. After specifying the currency type (american dollars, euros, etc.) it automatically - // sets parse/format options such as how many decimal places to show. - // 2. The currency mark (dollar sign, euro mark, etc.) is displayed when the field is blurred - // but erased during editing, so that the user can just enter a plain number. - - // currency: [const] String - // the [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code, a three letter sequence like "USD" - currency: "", - - // constraints: dijit.form.CurrencyTextBox.__Constraints - // Despite the name, this parameter specifies both constraints on the input - // (including minimum/maximum allowed values) as well as - // formatting options. See `dijit.form.CurrencyTextBox.__Constraints` for details. - /*===== - constraints: {}, - ======*/ - - baseClass: "dijitTextBox dijitCurrencyTextBox", - - // Override regExpGen ValidationTextBox.regExpGen().... we use a reg-ex generating function rather - // than a straight regexp to deal with locale (plus formatting options too?) - regExpGen: function(constraints){ - // if focused, accept either currency data or NumberTextBox format - return '(' + (this._focused? this.inherited(arguments, [ dojo.mixin({}, constraints, this.editOptions) ]) + '|' : '') - + dojo.currency.regexp(constraints) + ')'; - }, - - // Override NumberTextBox._formatter to deal with currencies, ex: converts "123.45" to "$123.45" - _formatter: dojo.currency.format, - - parse: function(/* String */ value, /* Object */ constraints){ - // summary: - // Parses string value as a Currency, according to the constraints object - // tags: - // protected extension - var v = dojo.currency.parse(value, constraints); - if(isNaN(v) && /\d+/.test(value)){ // currency parse failed, but it could be because they are using NumberTextBox format so try its parse - return this.inherited(arguments, [ value, dojo.mixin({}, constraints, this.editOptions) ]); - } - return v; - }, - - _setConstraintsAttr: function(/* Object */ constraints){ - if(!constraints.currency && this.currency){ - constraints.currency = this.currency; - } - this.inherited(arguments, [ dojo.currency._mixInDefaults(dojo.mixin(constraints, { exponent: false })) ]); // get places - } - } -); - -} - -if(!dojo._hasResource["dojo.cldr.supplemental"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.cldr.supplemental"] = true; -dojo.provide("dojo.cldr.supplemental"); - - - -dojo.cldr.supplemental.getFirstDayOfWeek = function(/*String?*/locale){ -// summary: Returns a zero-based index for first day of the week -// description: -// Returns a zero-based index for first day of the week, as used by the local (Gregorian) calendar. -// e.g. Sunday (returns 0), or Monday (returns 1) - - // from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/weekData/firstDay - var firstDay = {/*default is 1=Monday*/ - mv:5, - af:6,bh:6,dj:6,dz:6,eg:6,er:6,et:6,iq:6,ir:6,jo:6,ke:6,kw:6, - ly:6,ma:6,om:6,qa:6,sa:6,sd:6,so:6,tn:6,ye:6, - ar:0,as:0,az:0,bw:0,ca:0,cn:0,fo:0,ge:0,gl:0,gu:0,hk:0,ie:0, - il:0,'in':0,is:0,jm:0,jp:0,kg:0,kr:0,la:0,mh:0,mn:0,mo:0,mp:0, - mt:0,nz:0,ph:0,pk:0,sg:0,sy:0,th:0,tt:0,tw:0,um:0,us:0,uz:0, - vi:0,zw:0 -// variant. do not use? gb:0, - }; - - var country = dojo.cldr.supplemental._region(locale); - var dow = firstDay[country]; - return (dow === undefined) ? 1 : dow; /*Number*/ -}; - -dojo.cldr.supplemental._region = function(/*String?*/locale){ - locale = dojo.i18n.normalizeLocale(locale); - var tags = locale.split('-'); - var region = tags[1]; - if(!region){ - // IE often gives language only (#2269) - // Arbitrary mappings of language-only locales to a country: - region = {de:"de", en:"us", es:"es", fi:"fi", fr:"fr", he:"il", hu:"hu", it:"it", - ja:"jp", ko:"kr", nl:"nl", pt:"br", sv:"se", zh:"cn"}[tags[0]]; - }else if(region.length == 4){ - // The ISO 3166 country code is usually in the second position, unless a - // 4-letter script is given. See http://www.ietf.org/rfc/rfc4646.txt - region = tags[2]; - } - return region; -} - -dojo.cldr.supplemental.getWeekend = function(/*String?*/locale){ -// summary: Returns a hash containing the start and end days of the weekend -// description: -// Returns a hash containing the start and end days of the weekend according to local custom using locale, -// or by default in the user's locale. -// e.g. {start:6, end:0} - - // from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/weekData/weekend{Start,End} - var weekendStart = {/*default is 6=Saturday*/ - 'in':0, - af:4,dz:4,ir:4,om:4,sa:4,ye:4, - ae:5,bh:5,eg:5,il:5,iq:5,jo:5,kw:5,ly:5,ma:5,qa:5,sd:5,sy:5,tn:5 - }; - - var weekendEnd = {/*default is 0=Sunday*/ - af:5,dz:5,ir:5,om:5,sa:5,ye:5, - ae:6,bh:5,eg:6,il:6,iq:6,jo:6,kw:6,ly:6,ma:6,qa:6,sd:6,sy:6,tn:6 - }; - - var country = dojo.cldr.supplemental._region(locale); - var start = weekendStart[country]; - var end = weekendEnd[country]; - if(start === undefined){start=6;} - if(end === undefined){end=0;} - return {start:start, end:end}; /*Object {start,end}*/ -}; - -} - -if(!dojo._hasResource["dojo.date"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.date"] = true; -dojo.provide("dojo.date"); - -/*===== -dojo.date = { - // summary: Date manipulation utilities -} -=====*/ - -dojo.date.getDaysInMonth = function(/*Date*/dateObject){ - // summary: - // Returns the number of days in the month used by dateObject - var month = dateObject.getMonth(); - var days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; - if(month == 1 && dojo.date.isLeapYear(dateObject)){ return 29; } // Number - return days[month]; // Number -} - -dojo.date.isLeapYear = function(/*Date*/dateObject){ - // summary: - // Determines if the year of the dateObject is a leap year - // description: - // Leap years are years with an additional day YYYY-02-29, where the - // year number is a multiple of four with the following exception: If - // a year is a multiple of 100, then it is only a leap year if it is - // also a multiple of 400. For example, 1900 was not a leap year, but - // 2000 is one. - - var year = dateObject.getFullYear(); - return !(year%400) || (!(year%4) && !!(year%100)); // Boolean -} - -// FIXME: This is not localized -dojo.date.getTimezoneName = function(/*Date*/dateObject){ - // summary: - // Get the user's time zone as provided by the browser - // dateObject: - // Needed because the timezone may vary with time (daylight savings) - // description: - // Try to get time zone info from toString or toLocaleString method of - // the Date object -- UTC offset is not a time zone. See - // http://www.twinsun.com/tz/tz-link.htm Note: results may be - // inconsistent across browsers. - - var str = dateObject.toString(); // Start looking in toString - var tz = ''; // The result -- return empty string if nothing found - var match; - - // First look for something in parentheses -- fast lookup, no regex - var pos = str.indexOf('('); - if(pos > -1){ - tz = str.substring(++pos, str.indexOf(')')); - }else{ - // If at first you don't succeed ... - // If IE knows about the TZ, it appears before the year - // Capital letters or slash before a 4-digit year - // at the end of string - var pat = /([A-Z\/]+) \d{4}$/; - if((match = str.match(pat))){ - tz = match[1]; - }else{ - // Some browsers (e.g. Safari) glue the TZ on the end - // of toLocaleString instead of putting it in toString - str = dateObject.toLocaleString(); - // Capital letters or slash -- end of string, - // after space - pat = / ([A-Z\/]+)$/; - if((match = str.match(pat))){ - tz = match[1]; - } - } - } - - // Make sure it doesn't somehow end up return AM or PM - return (tz == 'AM' || tz == 'PM') ? '' : tz; // String -} - -// Utility methods to do arithmetic calculations with Dates - -dojo.date.compare = function(/*Date*/date1, /*Date?*/date2, /*String?*/portion){ - // summary: - // Compare two date objects by date, time, or both. - // description: - // Returns 0 if equal, positive if a > b, else negative. - // date1: - // Date object - // date2: - // Date object. If not specified, the current Date is used. - // portion: - // A string indicating the "date" or "time" portion of a Date object. - // Compares both "date" and "time" by default. One of the following: - // "date", "time", "datetime" - - // Extra step required in copy for IE - see #3112 - date1 = new Date(+date1); - date2 = new Date(+(date2 || new Date())); - - if(portion == "date"){ - // Ignore times and compare dates. - date1.setHours(0, 0, 0, 0); - date2.setHours(0, 0, 0, 0); - }else if(portion == "time"){ - // Ignore dates and compare times. - date1.setFullYear(0, 0, 0); - date2.setFullYear(0, 0, 0); - } - - if(date1 > date2){ return 1; } // int - if(date1 < date2){ return -1; } // int - return 0; // int -}; - -dojo.date.add = function(/*Date*/date, /*String*/interval, /*int*/amount){ - // summary: - // Add to a Date in intervals of different size, from milliseconds to years - // date: Date - // Date object to start with - // interval: - // A string representing the interval. One of the following: - // "year", "month", "day", "hour", "minute", "second", - // "millisecond", "quarter", "week", "weekday" - // amount: - // How much to add to the date. - - var sum = new Date(+date); // convert to Number before copying to accomodate IE (#3112) - var fixOvershoot = false; - var property = "Date"; - - switch(interval){ - case "day": - break; - case "weekday": - //i18n FIXME: assumes Saturday/Sunday weekend, but this is not always true. see dojo.cldr.supplemental - - // Divide the increment time span into weekspans plus leftover days - // e.g., 8 days is one 5-day weekspan / and two leftover days - // Can't have zero leftover days, so numbers divisible by 5 get - // a days value of 5, and the remaining days make up the number of weeks - var days, weeks; - var mod = amount % 5; - if(!mod){ - days = (amount > 0) ? 5 : -5; - weeks = (amount > 0) ? ((amount-5)/5) : ((amount+5)/5); - }else{ - days = mod; - weeks = parseInt(amount/5); - } - // Get weekday value for orig date param - var strt = date.getDay(); - // Orig date is Sat / positive incrementer - // Jump over Sun - var adj = 0; - if(strt == 6 && amount > 0){ - adj = 1; - }else if(strt == 0 && amount < 0){ - // Orig date is Sun / negative incrementer - // Jump back over Sat - adj = -1; - } - // Get weekday val for the new date - var trgt = strt + days; - // New date is on Sat or Sun - if(trgt == 0 || trgt == 6){ - adj = (amount > 0) ? 2 : -2; - } - // Increment by number of weeks plus leftover days plus - // weekend adjustments - amount = (7 * weeks) + days + adj; - break; - case "year": - property = "FullYear"; - // Keep increment/decrement from 2/29 out of March - fixOvershoot = true; - break; - case "week": - amount *= 7; - break; - case "quarter": - // Naive quarter is just three months - amount *= 3; - // fallthrough... - case "month": - // Reset to last day of month if you overshoot - fixOvershoot = true; - property = "Month"; - break; -// case "hour": -// case "minute": -// case "second": -// case "millisecond": - default: - property = "UTC"+interval.charAt(0).toUpperCase() + interval.substring(1) + "s"; - } - - if(property){ - sum["set"+property](sum["get"+property]()+amount); - } - - if(fixOvershoot && (sum.getDate() < date.getDate())){ - sum.setDate(0); - } - - return sum; // Date -}; - -dojo.date.difference = function(/*Date*/date1, /*Date?*/date2, /*String?*/interval){ - // summary: - // Get the difference in a specific unit of time (e.g., number of - // months, weeks, days, etc.) between two dates, rounded to the - // nearest integer. - // date1: - // Date object - // date2: - // Date object. If not specified, the current Date is used. - // interval: - // A string representing the interval. One of the following: - // "year", "month", "day", "hour", "minute", "second", - // "millisecond", "quarter", "week", "weekday" - // Defaults to "day". - - date2 = date2 || new Date(); - interval = interval || "day"; - var yearDiff = date2.getFullYear() - date1.getFullYear(); - var delta = 1; // Integer return value - - switch(interval){ - case "quarter": - var m1 = date1.getMonth(); - var m2 = date2.getMonth(); - // Figure out which quarter the months are in - var q1 = Math.floor(m1/3) + 1; - var q2 = Math.floor(m2/3) + 1; - // Add quarters for any year difference between the dates - q2 += (yearDiff * 4); - delta = q2 - q1; - break; - case "weekday": - var days = Math.round(dojo.date.difference(date1, date2, "day")); - var weeks = parseInt(dojo.date.difference(date1, date2, "week")); - var mod = days % 7; - - // Even number of weeks - if(mod == 0){ - days = weeks*5; - }else{ - // Weeks plus spare change (< 7 days) - var adj = 0; - var aDay = date1.getDay(); - var bDay = date2.getDay(); - - weeks = parseInt(days/7); - mod = days % 7; - // Mark the date advanced by the number of - // round weeks (may be zero) - var dtMark = new Date(date1); - dtMark.setDate(dtMark.getDate()+(weeks*7)); - var dayMark = dtMark.getDay(); - - // Spare change days -- 6 or less - if(days > 0){ - switch(true){ - // Range starts on Sat - case aDay == 6: - adj = -1; - break; - // Range starts on Sun - case aDay == 0: - adj = 0; - break; - // Range ends on Sat - case bDay == 6: - adj = -1; - break; - // Range ends on Sun - case bDay == 0: - adj = -2; - break; - // Range contains weekend - case (dayMark + mod) > 5: - adj = -2; - } - }else if(days < 0){ - switch(true){ - // Range starts on Sat - case aDay == 6: - adj = 0; - break; - // Range starts on Sun - case aDay == 0: - adj = 1; - break; - // Range ends on Sat - case bDay == 6: - adj = 2; - break; - // Range ends on Sun - case bDay == 0: - adj = 1; - break; - // Range contains weekend - case (dayMark + mod) < 0: - adj = 2; - } - } - days += adj; - days -= (weeks*2); - } - delta = days; - break; - case "year": - delta = yearDiff; - break; - case "month": - delta = (date2.getMonth() - date1.getMonth()) + (yearDiff * 12); - break; - case "week": - // Truncate instead of rounding - // Don't use Math.floor -- value may be negative - delta = parseInt(dojo.date.difference(date1, date2, "day")/7); - break; - case "day": - delta /= 24; - // fallthrough - case "hour": - delta /= 60; - // fallthrough - case "minute": - delta /= 60; - // fallthrough - case "second": - delta /= 1000; - // fallthrough - case "millisecond": - delta *= date2.getTime() - date1.getTime(); - } - - // Round for fractional values and DST leaps - return Math.round(delta); // Number (integer) -}; - -} - -if(!dojo._hasResource["dojo.date.locale"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojo.date.locale"] = true; -dojo.provide("dojo.date.locale"); - -// Localization methods for Date. Honor local customs using locale-dependent dojo.cldr data. - - - - - - - -// Load the bundles containing localization information for -// names and formats - - -//NOTE: Everything in this module assumes Gregorian calendars. -// Other calendars will be implemented in separate modules. - -(function(){ - // Format a pattern without literals - function formatPattern(dateObject, bundle, options, pattern){ - return pattern.replace(/([a-z])\1*/ig, function(match){ - var s, pad, - c = match.charAt(0), - l = match.length, - widthList = ["abbr", "wide", "narrow"]; - switch(c){ - case 'G': - s = bundle[(l < 4) ? "eraAbbr" : "eraNames"][dateObject.getFullYear() < 0 ? 0 : 1]; - break; - case 'y': - s = dateObject.getFullYear(); - switch(l){ - case 1: - break; - case 2: - if(!options.fullYear){ - s = String(s); s = s.substr(s.length - 2); - break; - } - // fallthrough - default: - pad = true; - } - break; - case 'Q': - case 'q': - s = Math.ceil((dateObject.getMonth()+1)/3); -// switch(l){ -// case 1: case 2: - pad = true; -// break; -// case 3: case 4: // unimplemented -// } - break; - case 'M': - var m = dateObject.getMonth(); - if(l<3){ - s = m+1; pad = true; - }else{ - var propM = ["months", "format", widthList[l-3]].join("-"); - s = bundle[propM][m]; - } - break; - case 'w': - var firstDay = 0; - s = dojo.date.locale._getWeekOfYear(dateObject, firstDay); pad = true; - break; - case 'd': - s = dateObject.getDate(); pad = true; - break; - case 'D': - s = dojo.date.locale._getDayOfYear(dateObject); pad = true; - break; - case 'E': - var d = dateObject.getDay(); - if(l<3){ - s = d+1; pad = true; - }else{ - var propD = ["days", "format", widthList[l-3]].join("-"); - s = bundle[propD][d]; - } - break; - case 'a': - var timePeriod = (dateObject.getHours() < 12) ? 'am' : 'pm'; - s = bundle['dayPeriods-format-wide-' + timePeriod]; - break; - case 'h': - case 'H': - case 'K': - case 'k': - var h = dateObject.getHours(); - // strange choices in the date format make it impossible to write this succinctly - switch (c){ - case 'h': // 1-12 - s = (h % 12) || 12; - break; - case 'H': // 0-23 - s = h; - break; - case 'K': // 0-11 - s = (h % 12); - break; - case 'k': // 1-24 - s = h || 24; - break; - } - pad = true; - break; - case 'm': - s = dateObject.getMinutes(); pad = true; - break; - case 's': - s = dateObject.getSeconds(); pad = true; - break; - case 'S': - s = Math.round(dateObject.getMilliseconds() * Math.pow(10, l-3)); pad = true; - break; - case 'v': // FIXME: don't know what this is. seems to be same as z? - case 'z': - // We only have one timezone to offer; the one from the browser - s = dojo.date.locale._getZone(dateObject, true, options); - if(s){break;} - l=4; - // fallthrough... use GMT if tz not available - case 'Z': - var offset = dojo.date.locale._getZone(dateObject, false, options); - var tz = [ - (offset<=0 ? "+" : "-"), - dojo.string.pad(Math.floor(Math.abs(offset)/60), 2), - dojo.string.pad(Math.abs(offset)% 60, 2) - ]; - if(l==4){ - tz.splice(0, 0, "GMT"); - tz.splice(3, 0, ":"); - } - s = tz.join(""); - break; -// case 'Y': case 'u': case 'W': case 'F': case 'g': case 'A': case 'e': -// console.log(match+" modifier unimplemented"); - default: - throw new Error("dojo.date.locale.format: invalid pattern char: "+pattern); - } - if(pad){ s = dojo.string.pad(s, l); } - return s; - }); - } - -/*===== - dojo.date.locale.__FormatOptions = function(){ - // selector: String - // choice of 'time','date' (default: date and time) - // formatLength: String - // choice of long, short, medium or full (plus any custom additions). Defaults to 'short' - // datePattern:String - // override pattern with this string - // timePattern:String - // override pattern with this string - // am: String - // override strings for am in times - // pm: String - // override strings for pm in times - // locale: String - // override the locale used to determine formatting rules - // fullYear: Boolean - // (format only) use 4 digit years whenever 2 digit years are called for - // strict: Boolean - // (parse only) strict parsing, off by default - this.selector = selector; - this.formatLength = formatLength; - this.datePattern = datePattern; - this.timePattern = timePattern; - this.am = am; - this.pm = pm; - this.locale = locale; - this.fullYear = fullYear; - this.strict = strict; - } -=====*/ - -dojo.date.locale._getZone = function(/*Date*/dateObject, /*boolean*/getName, /*dojo.date.locale.__FormatOptions?*/options){ - // summary: - // Returns the zone (or offset) for the given date and options. This - // is broken out into a separate function so that it can be overridden - // by timezone-aware code. - // - // dateObject: - // the date and/or time being formatted. - // - // getName: - // Whether to return the timezone string (if true), or the offset (if false) - // - // options: - // The options being used for formatting - if(getName){ - return dojo.date.getTimezoneName(dateObject); - }else{ - return dateObject.getTimezoneOffset(); - } -}; - - -dojo.date.locale.format = function(/*Date*/dateObject, /*dojo.date.locale.__FormatOptions?*/options){ - // summary: - // Format a Date object as a String, using locale-specific settings. - // - // description: - // Create a string from a Date object using a known localized pattern. - // By default, this method formats both date and time from dateObject. - // Formatting patterns are chosen appropriate to the locale. Different - // formatting lengths may be chosen, with "full" used by default. - // Custom patterns may be used or registered with translations using - // the dojo.date.locale.addCustomFormats method. - // Formatting patterns are implemented using [the syntax described at - // unicode.org](http://www.unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns) - // - // dateObject: - // the date and/or time to be formatted. If a time only is formatted, - // the values in the year, month, and day fields are irrelevant. The - // opposite is true when formatting only dates. - - options = options || {}; - - var locale = dojo.i18n.normalizeLocale(options.locale), - formatLength = options.formatLength || 'short', - bundle = dojo.date.locale._getGregorianBundle(locale), - str = [], - sauce = dojo.hitch(this, formatPattern, dateObject, bundle, options); - if(options.selector == "year"){ - return _processPattern(bundle["dateFormatItem-yyyy"] || "yyyy", sauce); - } - var pattern; - if(options.selector != "date"){ - pattern = options.timePattern || bundle["timeFormat-"+formatLength]; - if(pattern){str.push(_processPattern(pattern, sauce));} - } - if(options.selector != "time"){ - pattern = options.datePattern || bundle["dateFormat-"+formatLength]; - if(pattern){str.push(_processPattern(pattern, sauce));} - } - - return str.length == 1 ? str[0] : bundle["dateTimeFormat-"+formatLength].replace(/\{(\d+)\}/g, - function(match, key){ return str[key]; }); // String -}; - -dojo.date.locale.regexp = function(/*dojo.date.locale.__FormatOptions?*/options){ - // summary: - // Builds the regular needed to parse a localized date - - return dojo.date.locale._parseInfo(options).regexp; // String -}; - -dojo.date.locale._parseInfo = function(/*dojo.date.locale.__FormatOptions?*/options){ - options = options || {}; - var locale = dojo.i18n.normalizeLocale(options.locale), - bundle = dojo.date.locale._getGregorianBundle(locale), - formatLength = options.formatLength || 'short', - datePattern = options.datePattern || bundle["dateFormat-" + formatLength], - timePattern = options.timePattern || bundle["timeFormat-" + formatLength], - pattern; - if(options.selector == 'date'){ - pattern = datePattern; - }else if(options.selector == 'time'){ - pattern = timePattern; - }else{ - pattern = bundle["dateTimeFormat-"+formatLength].replace(/\{(\d+)\}/g, - function(match, key){ return [timePattern, datePattern][key]; }); - } - - var tokens = [], - re = _processPattern(pattern, dojo.hitch(this, _buildDateTimeRE, tokens, bundle, options)); - return {regexp: re, tokens: tokens, bundle: bundle}; -}; - -dojo.date.locale.parse = function(/*String*/value, /*dojo.date.locale.__FormatOptions?*/options){ - // summary: - // Convert a properly formatted string to a primitive Date object, - // using locale-specific settings. - // - // description: - // Create a Date object from a string using a known localized pattern. - // By default, this method parses looking for both date and time in the string. - // Formatting patterns are chosen appropriate to the locale. Different - // formatting lengths may be chosen, with "full" used by default. - // Custom patterns may be used or registered with translations using - // the dojo.date.locale.addCustomFormats method. - // - // Formatting patterns are implemented using [the syntax described at - // unicode.org](http://www.unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns) - // When two digit years are used, a century is chosen according to a sliding - // window of 80 years before and 20 years after present year, for both `yy` and `yyyy` patterns. - // year < 100CE requires strict mode. - // - // value: - // A string representation of a date - - var info = dojo.date.locale._parseInfo(options), - tokens = info.tokens, bundle = info.bundle, - re = new RegExp("^" + info.regexp + "$", info.strict ? "" : "i"), - match = re.exec(value); - - if(!match){ return null; } // null - - var widthList = ['abbr', 'wide', 'narrow'], - result = [1970,0,1,0,0,0,0], // will get converted to a Date at the end - amPm = "", - valid = dojo.every(match, function(v, i){ - if(!i){return true;} - var token=tokens[i-1]; - var l=token.length; - switch(token.charAt(0)){ - case 'y': - if(l != 2 && options.strict){ - //interpret year literally, so '5' would be 5 A.D. - result[0] = v; - }else{ - if(v<100){ - v = Number(v); - //choose century to apply, according to a sliding window - //of 80 years before and 20 years after present year - var year = '' + new Date().getFullYear(), - century = year.substring(0, 2) * 100, - cutoff = Math.min(Number(year.substring(2, 4)) + 20, 99), - num = (v < cutoff) ? century + v : century - 100 + v; - result[0] = num; - }else{ - //we expected 2 digits and got more... - if(options.strict){ - return false; - } - //interpret literally, so '150' would be 150 A.D. - //also tolerate '1950', if 'yyyy' input passed to 'yy' format - result[0] = v; - } - } - break; - case 'M': - if(l>2){ - var months = bundle['months-format-' + widthList[l-3]].concat(); - if(!options.strict){ - //Tolerate abbreviating period in month part - //Case-insensitive comparison - v = v.replace(".","").toLowerCase(); - months = dojo.map(months, function(s){ return s.replace(".","").toLowerCase(); } ); - } - v = dojo.indexOf(months, v); - if(v == -1){ -// console.log("dojo.date.locale.parse: Could not parse month name: '" + v + "'."); - return false; - } - }else{ - v--; - } - result[1] = v; - break; - case 'E': - case 'e': - var days = bundle['days-format-' + widthList[l-3]].concat(); - if(!options.strict){ - //Case-insensitive comparison - v = v.toLowerCase(); - days = dojo.map(days, function(d){return d.toLowerCase();}); - } - v = dojo.indexOf(days, v); - if(v == -1){ -// console.log("dojo.date.locale.parse: Could not parse weekday name: '" + v + "'."); - return false; - } - - //TODO: not sure what to actually do with this input, - //in terms of setting something on the Date obj...? - //without more context, can't affect the actual date - //TODO: just validate? - break; - case 'D': - result[1] = 0; - // fallthrough... - case 'd': - result[2] = v; - break; - case 'a': //am/pm - var am = options.am || bundle['dayPeriods-format-wide-am'], - pm = options.pm || bundle['dayPeriods-format-wide-pm']; - if(!options.strict){ - var period = /\./g; - v = v.replace(period,'').toLowerCase(); - am = am.replace(period,'').toLowerCase(); - pm = pm.replace(period,'').toLowerCase(); - } - if(options.strict && v != am && v != pm){ -// console.log("dojo.date.locale.parse: Could not parse am/pm part."); - return false; - } - - // we might not have seen the hours field yet, so store the state and apply hour change later - amPm = (v == pm) ? 'p' : (v == am) ? 'a' : ''; - break; - case 'K': //hour (1-24) - if(v == 24){ v = 0; } - // fallthrough... - case 'h': //hour (1-12) - case 'H': //hour (0-23) - case 'k': //hour (0-11) - //TODO: strict bounds checking, padding - if(v > 23){ -// console.log("dojo.date.locale.parse: Illegal hours value"); - return false; - } - - //in the 12-hour case, adjusting for am/pm requires the 'a' part - //which could come before or after the hour, so we will adjust later - result[3] = v; - break; - case 'm': //minutes - result[4] = v; - break; - case 's': //seconds - result[5] = v; - break; - case 'S': //milliseconds - result[6] = v; -// break; -// case 'w': -//TODO var firstDay = 0; -// default: -//TODO: throw? -// console.log("dojo.date.locale.parse: unsupported pattern char=" + token.charAt(0)); - } - return true; - }); - - var hours = +result[3]; - if(amPm === 'p' && hours < 12){ - result[3] = hours + 12; //e.g., 3pm -> 15 - }else if(amPm === 'a' && hours == 12){ - result[3] = 0; //12am -> 0 - } - - //TODO: implement a getWeekday() method in order to test - //validity of input strings containing 'EEE' or 'EEEE'... - - var dateObject = new Date(result[0], result[1], result[2], result[3], result[4], result[5], result[6]); // Date - if(options.strict){ - dateObject.setFullYear(result[0]); - } - - // Check for overflow. The Date() constructor normalizes things like April 32nd... - //TODO: why isn't this done for times as well? - var allTokens = tokens.join(""), - dateToken = allTokens.indexOf('d') != -1, - monthToken = allTokens.indexOf('M') != -1; - - if(!valid || - (monthToken && dateObject.getMonth() > result[1]) || - (dateToken && dateObject.getDate() > result[2])){ - return null; - } - - // Check for underflow, due to DST shifts. See #9366 - // This assumes a 1 hour dst shift correction at midnight - // We could compare the timezone offset after the shift and add the difference instead. - if((monthToken && dateObject.getMonth() < result[1]) || - (dateToken && dateObject.getDate() < result[2])){ - dateObject = dojo.date.add(dateObject, "hour", 1); - } - - return dateObject; // Date -}; - -function _processPattern(pattern, applyPattern, applyLiteral, applyAll){ - //summary: Process a pattern with literals in it - - // Break up on single quotes, treat every other one as a literal, except '' which becomes ' - var identity = function(x){return x;}; - applyPattern = applyPattern || identity; - applyLiteral = applyLiteral || identity; - applyAll = applyAll || identity; - - //split on single quotes (which escape literals in date format strings) - //but preserve escaped single quotes (e.g., o''clock) - var chunks = pattern.match(/(''|[^'])+/g), - literal = pattern.charAt(0) == "'"; - - dojo.forEach(chunks, function(chunk, i){ - if(!chunk){ - chunks[i]=''; - }else{ - chunks[i]=(literal ? applyLiteral : applyPattern)(chunk.replace(/''/g, "'")); - literal = !literal; - } - }); - return applyAll(chunks.join('')); -} - -function _buildDateTimeRE(tokens, bundle, options, pattern){ - pattern = dojo.regexp.escapeString(pattern); - if(!options.strict){ pattern = pattern.replace(" a", " ?a"); } // kludge to tolerate no space before am/pm - return pattern.replace(/([a-z])\1*/ig, function(match){ - // Build a simple regexp. Avoid captures, which would ruin the tokens list - var s, - c = match.charAt(0), - l = match.length, - p2 = '', p3 = ''; - if(options.strict){ - if(l > 1){ p2 = '0' + '{'+(l-1)+'}'; } - if(l > 2){ p3 = '0' + '{'+(l-2)+'}'; } - }else{ - p2 = '0?'; p3 = '0{0,2}'; - } - switch(c){ - case 'y': - s = '\\d{2,4}'; - break; - case 'M': - s = (l>2) ? '\\S+?' : p2+'[1-9]|1[0-2]'; - break; - case 'D': - s = p2+'[1-9]|'+p3+'[1-9][0-9]|[12][0-9][0-9]|3[0-5][0-9]|36[0-6]'; - break; - case 'd': - s = '3[01]|[12]\\d|'+p2+'[1-9]'; - break; - case 'w': - s = p2+'[1-9]|[1-4][0-9]|5[0-3]'; - break; - case 'E': - s = '\\S+'; - break; - case 'h': //hour (1-12) - s = p2+'[1-9]|1[0-2]'; - break; - case 'k': //hour (0-11) - s = p2+'\\d|1[01]'; - break; - case 'H': //hour (0-23) - s = p2+'\\d|1\\d|2[0-3]'; - break; - case 'K': //hour (1-24) - s = p2+'[1-9]|1\\d|2[0-4]'; - break; - case 'm': - case 's': - s = '[0-5]\\d'; - break; - case 'S': - s = '\\d{'+l+'}'; - break; - case 'a': - var am = options.am || bundle['dayPeriods-format-wide-am'], - pm = options.pm || bundle['dayPeriods-format-wide-pm']; - if(options.strict){ - s = am + '|' + pm; - }else{ - s = am + '|' + pm; - if(am != am.toLowerCase()){ s += '|' + am.toLowerCase(); } - if(pm != pm.toLowerCase()){ s += '|' + pm.toLowerCase(); } - if(s.indexOf('.') != -1){ s += '|' + s.replace(/\./g, ""); } - } - s = s.replace(/\./g, "\\."); - break; - default: - // case 'v': - // case 'z': - // case 'Z': - s = ".*"; -// console.log("parse of date format, pattern=" + pattern); - } - - if(tokens){ tokens.push(match); } - - return "(" + s + ")"; // add capture - }).replace(/[\xa0 ]/g, "[\\s\\xa0]"); // normalize whitespace. Need explicit handling of \xa0 for IE. -} -})(); - -(function(){ -var _customFormats = []; -dojo.date.locale.addCustomFormats = function(/*String*/packageName, /*String*/bundleName){ - // summary: - // Add a reference to a bundle containing localized custom formats to be - // used by date/time formatting and parsing routines. - // - // description: - // The user may add custom localized formats where the bundle has properties following the - // same naming convention used by dojo.cldr: `dateFormat-xxxx` / `timeFormat-xxxx` - // The pattern string should match the format used by the CLDR. - // See dojo.date.locale.format() for details. - // The resources must be loaded by dojo.requireLocalization() prior to use - - _customFormats.push({pkg:packageName,name:bundleName}); -}; - -dojo.date.locale._getGregorianBundle = function(/*String*/locale){ - var gregorian = {}; - dojo.forEach(_customFormats, function(desc){ - var bundle = dojo.i18n.getLocalization(desc.pkg, desc.name, locale); - gregorian = dojo.mixin(gregorian, bundle); - }, this); - return gregorian; /*Object*/ -}; -})(); - -dojo.date.locale.addCustomFormats("dojo.cldr","gregorian"); - -dojo.date.locale.getNames = function(/*String*/item, /*String*/type, /*String?*/context, /*String?*/locale){ - // summary: - // Used to get localized strings from dojo.cldr for day or month names. - // - // item: - // 'months' || 'days' - // type: - // 'wide' || 'narrow' || 'abbr' (e.g. "Monday", "Mon", or "M" respectively, in English) - // context: - // 'standAlone' || 'format' (default) - // locale: - // override locale used to find the names - - var label, - lookup = dojo.date.locale._getGregorianBundle(locale), - props = [item, context, type]; - if(context == 'standAlone'){ - var key = props.join('-'); - label = lookup[key]; - // Fall back to 'format' flavor of name - if(label[0] == 1){ label = undefined; } // kludge, in the absence of real aliasing support in dojo.cldr - } - props[1] = 'format'; - - // return by copy so changes won't be made accidentally to the in-memory model - return (label || lookup[props.join('-')]).concat(); /*Array*/ -}; - -dojo.date.locale.isWeekend = function(/*Date?*/dateObject, /*String?*/locale){ - // summary: - // Determines if the date falls on a weekend, according to local custom. - - var weekend = dojo.cldr.supplemental.getWeekend(locale), - day = (dateObject || new Date()).getDay(); - if(weekend.end < weekend.start){ - weekend.end += 7; - if(day < weekend.start){ day += 7; } - } - return day >= weekend.start && day <= weekend.end; // Boolean -}; - -// These are used only by format and strftime. Do they need to be public? Which module should they go in? - -dojo.date.locale._getDayOfYear = function(/*Date*/dateObject){ - // summary: gets the day of the year as represented by dateObject - return dojo.date.difference(new Date(dateObject.getFullYear(), 0, 1, dateObject.getHours()), dateObject) + 1; // Number -}; - -dojo.date.locale._getWeekOfYear = function(/*Date*/dateObject, /*Number*/firstDayOfWeek){ - if(arguments.length == 1){ firstDayOfWeek = 0; } // Sunday - - var firstDayOfYear = new Date(dateObject.getFullYear(), 0, 1).getDay(), - adj = (firstDayOfYear - firstDayOfWeek + 7) % 7, - week = Math.floor((dojo.date.locale._getDayOfYear(dateObject) + adj - 1) / 7); - - // if year starts on the specified day, start counting weeks at 1 - if(firstDayOfYear == firstDayOfWeek){ week++; } - - return week; // Number -}; - -} - -if(!dojo._hasResource["dijit.Calendar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.Calendar"] = true; -dojo.provide("dijit.Calendar"); - - - - - - - - - -dojo.declare( - "dijit.Calendar", - [dijit._Widget, dijit._Templated, dijit._CssStateMixin], - { - // summary: - // A simple GUI for choosing a date in the context of a monthly calendar. - // - // description: - // A simple GUI for choosing a date in the context of a monthly calendar. - // This widget can't be used in a form because it doesn't serialize the date to an - // `<input>` field. For a form element, use dijit.form.DateTextBox instead. - // - // Note that the parser takes all dates attributes passed in the - // [RFC 3339 format](http://www.faqs.org/rfcs/rfc3339.html), e.g. `2005-06-30T08:05:00-07:00` - // so that they are serializable and locale-independent. - // - // example: - // | var calendar = new dijit.Calendar({}, dojo.byId("calendarNode")); - // - // example: - // | <div dojoType="dijit.Calendar"></div> - - templateString: dojo.cache("dijit", "templates/Calendar.html", "<table cellspacing=\"0\" cellpadding=\"0\" class=\"dijitCalendarContainer\" role=\"grid\" dojoAttachEvent=\"onkeypress: _onKeyPress\">\n\t<thead>\n\t\t<tr class=\"dijitReset dijitCalendarMonthContainer\" valign=\"top\">\n\t\t\t<th class='dijitReset dijitCalendarArrow' dojoAttachPoint=\"decrementMonth\">\n\t\t\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitCalendarIncrementControl dijitCalendarDecrease\" waiRole=\"presentation\"/>\n\t\t\t\t<span dojoAttachPoint=\"decreaseArrowNode\" class=\"dijitA11ySideArrow\">-</span>\n\t\t\t</th>\n\t\t\t<th class='dijitReset' colspan=\"5\">\n\t\t\t\t<div class=\"dijitVisible\">\n\t\t\t\t\t<div class=\"dijitPopup dijitMenu dijitMenuPassive dijitHidden\" dojoAttachPoint=\"monthDropDown\" dojoAttachEvent=\"onmouseup: _onMonthSelect, onmouseover: _onMenuHover, onmouseout: _onMenuHover\">\n\t\t\t\t\t\t<div class=\"dijitCalendarMonthLabelTemplate dijitCalendarMonthLabel\"></div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div dojoAttachPoint=\"monthLabelSpacer\" class=\"dijitSpacer\"></div>\n\t\t\t\t<div dojoAttachPoint=\"monthLabelNode\" class=\"dijitCalendarMonthLabel dijitInline dijitVisible\" dojoAttachEvent=\"onmousedown: _onMonthToggle\"></div>\n\t\t\t</th>\n\t\t\t<th class='dijitReset dijitCalendarArrow' dojoAttachPoint=\"incrementMonth\">\n\t\t\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitCalendarIncrementControl dijitCalendarIncrease\" waiRole=\"presentation\"/>\n\t\t\t\t<span dojoAttachPoint=\"increaseArrowNode\" class=\"dijitA11ySideArrow\">+</span>\n\t\t\t</th>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<th class=\"dijitReset dijitCalendarDayLabelTemplate\" role=\"columnheader\"><span class=\"dijitCalendarDayLabel\"></span></th>\n\t\t</tr>\n\t</thead>\n\t<tbody dojoAttachEvent=\"onclick: _onDayClick, onmouseover: _onDayMouseOver, onmouseout: _onDayMouseOut, onmousedown: _onDayMouseDown, onmouseup: _onDayMouseUp\" class=\"dijitReset dijitCalendarBodyContainer\">\n\t\t<tr class=\"dijitReset dijitCalendarWeekTemplate\" role=\"row\">\n\t\t\t<td class=\"dijitReset dijitCalendarDateTemplate\" role=\"gridcell\"><span class=\"dijitCalendarDateLabel\"></span></td>\n\t\t</tr>\n\t</tbody>\n\t<tfoot class=\"dijitReset dijitCalendarYearContainer\">\n\t\t<tr>\n\t\t\t<td class='dijitReset' valign=\"top\" colspan=\"7\">\n\t\t\t\t<h3 class=\"dijitCalendarYearLabel\">\n\t\t\t\t\t<span dojoAttachPoint=\"previousYearLabelNode\" class=\"dijitInline dijitCalendarPreviousYear\"></span>\n\t\t\t\t\t<span dojoAttachPoint=\"currentYearLabelNode\" class=\"dijitInline dijitCalendarSelectedYear\"></span>\n\t\t\t\t\t<span dojoAttachPoint=\"nextYearLabelNode\" class=\"dijitInline dijitCalendarNextYear\"></span>\n\t\t\t\t</h3>\n\t\t\t</td>\n\t\t</tr>\n\t</tfoot>\n</table>\n"), - - // value: Date - // The currently selected Date - value: new Date(), - - // datePackage: String - // JavaScript namespace to find Calendar routines. Uses Gregorian Calendar routines - // at dojo.date by default. - datePackage: "dojo.date", - - // dayWidth: String - // How to represent the days of the week in the calendar header. See dojo.date.locale - dayWidth: "narrow", - - // tabIndex: Integer - // Order fields are traversed when user hits the tab key - tabIndex: "0", - - baseClass:"dijitCalendar", - - // Set node classes for various mouse events, see dijit._CssStateMixin for more details - cssStateNodes: { - "decrementMonth": "dijitCalendarArrow", - "incrementMonth": "dijitCalendarArrow", - "previousYearLabelNode": "dijitCalendarPreviousYear", - "nextYearLabelNode": "dijitCalendarNextYear" - }, - - attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, { - tabIndex: "domNode" - }), - - setValue: function(/*Date*/ value){ - // summary: - // Deprecated. Used attr('value', ...) instead. - // tags: - // deprecated - dojo.deprecated("dijit.Calendar:setValue() is deprecated. Use set('value', ...) instead.", "", "2.0"); - this.set('value', value); - }, - - _getValueAttr: function(){ - // summary: - // Support getter attr('value') - var value = new this.dateClassObj(this.value); - value.setHours(0, 0, 0, 0); // return midnight, local time for back-compat - - // If daylight savings pushes midnight to the previous date, fix the Date - // object to point at 1am so it will represent the correct day. See #9366 - if(value.getDate() < this.value.getDate()){ - value = this.dateFuncObj.add(value, "hour", 1); - } - return value; - }, - - _setValueAttr: function(/*Date*/ value){ - // summary: - // Support setter attr("value", ...) - // description: - // Set the current date and update the UI. If the date is disabled, the value will - // not change, but the display will change to the corresponding month. - // tags: - // protected - if(!this.value || this.dateFuncObj.compare(value, this.value)){ - value = new this.dateClassObj(value); - value.setHours(1); // to avoid issues when DST shift occurs at midnight, see #8521, #9366 - this.displayMonth = new this.dateClassObj(value); - if(!this.isDisabledDate(value, this.lang)){ - this.value = value; - this.onChange(this.get('value')); - } - dojo.attr(this.domNode, "aria-label", - this.dateLocaleModule.format(value, - {selector:"date", formatLength:"full"})); - this._populateGrid(); - } - }, - - _setText: function(node, text){ - // summary: - // This just sets the content of node to the specified text. - // Can't do "node.innerHTML=text" because of an IE bug w/tables, see #3434. - // tags: - // private - while(node.firstChild){ - node.removeChild(node.firstChild); - } - node.appendChild(dojo.doc.createTextNode(text)); - }, - - _populateGrid: function(){ - // summary: - // Fills in the calendar grid with each day (1-31) - // tags: - // private - var month = this.displayMonth; - month.setDate(1); - var firstDay = month.getDay(), - daysInMonth = this.dateFuncObj.getDaysInMonth(month), - daysInPreviousMonth = this.dateFuncObj.getDaysInMonth(this.dateFuncObj.add(month, "month", -1)), - today = new this.dateClassObj(), - dayOffset = dojo.cldr.supplemental.getFirstDayOfWeek(this.lang); - if(dayOffset > firstDay){ dayOffset -= 7; } - - // Iterate through dates in the calendar and fill in date numbers and style info - dojo.query(".dijitCalendarDateTemplate", this.domNode).forEach(function(template, i){ - i += dayOffset; - var date = new this.dateClassObj(month), - number, clazz = "dijitCalendar", adj = 0; - - if(i < firstDay){ - number = daysInPreviousMonth - firstDay + i + 1; - adj = -1; - clazz += "Previous"; - }else if(i >= (firstDay + daysInMonth)){ - number = i - firstDay - daysInMonth + 1; - adj = 1; - clazz += "Next"; - }else{ - number = i - firstDay + 1; - clazz += "Current"; - } - - if(adj){ - date = this.dateFuncObj.add(date, "month", adj); - } - date.setDate(number); - - if(!this.dateFuncObj.compare(date, today, "date")){ - clazz = "dijitCalendarCurrentDate " + clazz; - } - - if(this._isSelectedDate(date, this.lang)){ - clazz = "dijitCalendarSelectedDate " + clazz; - } - - if(this.isDisabledDate(date, this.lang)){ - clazz = "dijitCalendarDisabledDate " + clazz; - } - - var clazz2 = this.getClassForDate(date, this.lang); - if(clazz2){ - clazz = clazz2 + " " + clazz; - } - - template.className = clazz + "Month dijitCalendarDateTemplate"; - template.dijitDateValue = date.valueOf(); - var label = dojo.query(".dijitCalendarDateLabel", template)[0], - text = date.getDateLocalized ? date.getDateLocalized(this.lang) : date.getDate(); - this._setText(label, text); - }, this); - - // Fill in localized month name - var monthNames = this.dateLocaleModule.getNames('months', 'wide', 'standAlone', this.lang, month); - this._setText(this.monthLabelNode, monthNames[month.getMonth()]); - // Repopulate month list based on current year (Hebrew calendar) - dojo.query(".dijitCalendarMonthLabelTemplate", this.domNode).forEach(function(node, i){ - dojo.toggleClass(node, "dijitHidden", !(i in monthNames)); // hide leap months (Hebrew) - this._setText(node, monthNames[i]); - }, this); - - // Fill in localized prev/current/next years - var y = month.getFullYear() - 1; - var d = new this.dateClassObj(); - dojo.forEach(["previous", "current", "next"], function(name){ - d.setFullYear(y++); - this._setText(this[name+"YearLabelNode"], - this.dateLocaleModule.format(d, {selector:'year', locale:this.lang})); - }, this); - - // Set up repeating mouse behavior - var _this = this; - var typematic = function(nodeProp, dateProp, adj){ -//FIXME: leaks (collects) listeners if populateGrid is called multiple times. Do this once? - _this._connects.push( - dijit.typematic.addMouseListener(_this[nodeProp], _this, function(count){ - if(count >= 0){ _this._adjustDisplay(dateProp, adj); } - }, 0.8, 500) - ); - }; - typematic("incrementMonth", "month", 1); - typematic("decrementMonth", "month", -1); - typematic("nextYearLabelNode", "year", 1); - typematic("previousYearLabelNode", "year", -1); - }, - - goToToday: function(){ - // summary: - // Sets calendar's value to today's date - this.set('value', new this.dateClassObj()); - }, - - constructor: function(/*Object*/args){ - var dateClass = (args.datePackage && (args.datePackage != "dojo.date"))? args.datePackage + ".Date" : "Date"; - this.dateClassObj = dojo.getObject(dateClass, false); - this.datePackage = args.datePackage || this.datePackage; - this.dateFuncObj = dojo.getObject(this.datePackage, false); - this.dateLocaleModule = dojo.getObject(this.datePackage + ".locale", false); - }, - - postMixInProperties: function(){ - // parser.instantiate sometimes passes in NaN for IE. Use default value in prototype instead. - if(isNaN(this.value)){ delete this.value; } - this.inherited(arguments); - }, - - postCreate: function(){ - this.inherited(arguments); - dojo.setSelectable(this.domNode, false); - - var cloneClass = dojo.hitch(this, function(clazz, n){ - var template = dojo.query(clazz, this.domNode)[0]; - for(var i=0; i<n; i++){ - template.parentNode.appendChild(template.cloneNode(true)); - } - }); - - // clone the day label and calendar day templates 6 times to make 7 columns - cloneClass(".dijitCalendarDayLabelTemplate", 6); - cloneClass(".dijitCalendarDateTemplate", 6); - - // now make 6 week rows - cloneClass(".dijitCalendarWeekTemplate", 5); - - // insert localized day names in the header - var dayNames = this.dateLocaleModule.getNames('days', this.dayWidth, 'standAlone', this.lang); - var dayOffset = dojo.cldr.supplemental.getFirstDayOfWeek(this.lang); - dojo.query(".dijitCalendarDayLabel", this.domNode).forEach(function(label, i){ - this._setText(label, dayNames[(i + dayOffset) % 7]); - }, this); - - var dateObj = new this.dateClassObj(this.value); - // Fill in spacer/month dropdown element with all the month names (invisible) so that the maximum width will affect layout - var monthNames = this.dateLocaleModule.getNames('months', 'wide', 'standAlone', this.lang, dateObj); - cloneClass(".dijitCalendarMonthLabelTemplate", monthNames.length-1); - dojo.query(".dijitCalendarMonthLabelTemplate", this.domNode).forEach(function(node, i){ - dojo.attr(node, "month", i); - if(i in monthNames){ this._setText(node, monthNames[i]); } - dojo.place(node.cloneNode(true), this.monthLabelSpacer); - }, this); - - this.value = null; - this.set('value', dateObj); - }, - - _onMenuHover: function(e){ - dojo.stopEvent(e); - dojo.toggleClass(e.target, "dijitMenuItemHover"); - }, - - _adjustDisplay: function(/*String*/ part, /*int*/ amount){ - // summary: - // Moves calendar forwards or backwards by months or years - // part: - // "month" or "year" - // amount: - // Number of months or years - // tags: - // private - this.displayMonth = this.dateFuncObj.add(this.displayMonth, part, amount); - this._populateGrid(); - }, - - _onMonthToggle: function(/*Event*/ evt){ - // summary: - // Handler for when user triggers or dismisses the month list - // tags: - // protected - dojo.stopEvent(evt); - - if(evt.type == "mousedown"){ - var coords = dojo.position(this.monthLabelNode); -// coords.y -= dojo.position(this.domNode, true).y; - // Size the dropdown's width to match the label in the widget - // so that they are horizontally aligned - var dim = { - width: coords.w + "px", - top: -this.displayMonth.getMonth() * coords.h + "px" - }; - if((dojo.isIE && dojo.isQuirks) || dojo.isIE < 7){ - dim.left = -coords.w/2 + "px"; - } - dojo.style(this.monthDropDown, dim); - this._popupHandler = this.connect(document, "onmouseup", "_onMonthToggle"); - }else{ - this.disconnect(this._popupHandler); - delete this._popupHandler; - } - - dojo.toggleClass(this.monthDropDown, "dijitHidden"); - dojo.toggleClass(this.monthLabelNode, "dijitVisible"); - }, - - _onMonthSelect: function(/*Event*/ evt){ - // summary: - // Handler for when user selects a month from a list - // tags: - // protected - this._onMonthToggle(evt); - this.displayMonth.setMonth(dojo.attr(evt.target, "month")); - this._populateGrid(); - }, - - _onDayClick: function(/*Event*/ evt){ - // summary: - // Handler for day clicks, selects the date if appropriate - // tags: - // protected - dojo.stopEvent(evt); - for(var node = evt.target; node && !node.dijitDateValue; node = node.parentNode); - if(node && !dojo.hasClass(node, "dijitCalendarDisabledDate")){ - this.set('value', node.dijitDateValue); - this.onValueSelected(this.get('value')); - } - }, - - _onDayMouseOver: function(/*Event*/ evt){ - // summary: - // Handler for mouse over events on days, sets hovered style - // tags: - // protected - - // event can occur on <td> or the <span> inside the td, - // set node to the <td>. - var node = - dojo.hasClass(evt.target, "dijitCalendarDateLabel") ? - evt.target.parentNode : - evt.target; - - if(node && (node.dijitDateValue || node == this.previousYearLabelNode || node == this.nextYearLabelNode) ){ - dojo.addClass(node, "dijitCalendarHoveredDate"); - this._currentNode = node; - } - }, - - _onDayMouseOut: function(/*Event*/ evt){ - // summary: - // Handler for mouse out events on days, clears hovered style - // tags: - // protected - - if(!this._currentNode){ return; } - - // if mouse out occurs moving from <td> to <span> inside <td>, ignore it - if(evt.relatedTarget && evt.relatedTarget.parentNode == this._currentNode){ return; } - - dojo.removeClass(this._currentNode, "dijitCalendarHoveredDate"); - if(dojo.hasClass(this._currentNode, "dijitCalendarActiveDate")) { - dojo.removeClass(this._currentNode, "dijitCalendarActiveDate"); - } - this._currentNode = null; - }, - - _onDayMouseDown: function(/*Event*/ evt){ - var node = evt.target.parentNode; - if(node && node.dijitDateValue){ - dojo.addClass(node, "dijitCalendarActiveDate"); - this._currentNode = node; - } - }, - - _onDayMouseUp: function(/*Event*/ evt){ - var node = evt.target.parentNode; - if(node && node.dijitDateValue){ - dojo.removeClass(node, "dijitCalendarActiveDate"); - } - }, - -//TODO: use typematic -//TODO: skip disabled dates without ending up in a loop -//TODO: could optimize by avoiding populate grid when month does not change - _onKeyPress: function(/*Event*/evt){ - // summary: - // Provides keyboard navigation of calendar - // tags: - // protected - var dk = dojo.keys, - increment = -1, - interval, - newValue = this.value; - switch(evt.keyCode){ - case dk.RIGHT_ARROW: - increment = 1; - //fallthrough... - case dk.LEFT_ARROW: - interval = "day"; - if(!this.isLeftToRight()){ increment *= -1; } - break; - case dk.DOWN_ARROW: - increment = 1; - //fallthrough... - case dk.UP_ARROW: - interval = "week"; - break; - case dk.PAGE_DOWN: - increment = 1; - //fallthrough... - case dk.PAGE_UP: - interval = evt.ctrlKey ? "year" : "month"; - break; - case dk.END: - // go to the next month - newValue = this.dateFuncObj.add(newValue, "month", 1); - // subtract a day from the result when we're done - interval = "day"; - //fallthrough... - case dk.HOME: - newValue = new Date(newValue).setDate(1); - break; - case dk.ENTER: - this.onValueSelected(this.get('value')); - break; - case dk.ESCAPE: - //TODO - default: - return; - } - dojo.stopEvent(evt); - - if(interval){ - newValue = this.dateFuncObj.add(newValue, interval, increment); - } - - this.set("value", newValue); - }, - - onValueSelected: function(/*Date*/ date){ - // summary: - // Notification that a date cell was selected. It may be the same as the previous value. - // description: - // Used by `dijit.form._DateTimeTextBox` (and thus `dijit.form.DateTextBox`) - // to get notification when the user has clicked a date. - // tags: - // protected - }, - - onChange: function(/*Date*/ date){ - // summary: - // Called only when the selected date has changed - }, - - _isSelectedDate: function(/*Date*/ dateObject, /*String?*/ locale){ - // summary: - // Extension point so developers can subclass Calendar to - // support multiple (concurrently) selected dates - // tags: - // protected extension - return !this.dateFuncObj.compare(dateObject, this.value, "date") - }, - - isDisabledDate: function(/*Date*/ dateObject, /*String?*/ locale){ - // summary: - // May be overridden to disable certain dates in the calendar e.g. `isDisabledDate=dojo.date.locale.isWeekend` - // tags: - // extension -/*===== - return false; // Boolean -=====*/ - }, - - getClassForDate: function(/*Date*/ dateObject, /*String?*/ locale){ - // summary: - // May be overridden to return CSS classes to associate with the date entry for the given dateObject, - // for example to indicate a holiday in specified locale. - // tags: - // extension - -/*===== - return ""; // String -=====*/ - } - } -); - -} - -if(!dojo._hasResource["dijit.form._DateTimeTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form._DateTimeTextBox"] = true; -dojo.provide("dijit.form._DateTimeTextBox"); - - - - - - -new Date("X"); // workaround for #11279, new Date("") == NaN - -/*===== -dojo.declare( - "dijit.form._DateTimeTextBox.__Constraints", - [dijit.form.RangeBoundTextBox.__Constraints, dojo.date.locale.__FormatOptions], { - // summary: - // Specifies both the rules on valid/invalid values (first/last date/time allowed), - // and also formatting options for how the date/time is displayed. - // example: - // To restrict to dates within 2004, displayed in a long format like "December 25, 2005": - // | {min:'2004-01-01',max:'2004-12-31', formatLength:'long'} -}); -=====*/ - -dojo.declare( - "dijit.form._DateTimeTextBox", - dijit.form.RangeBoundTextBox, - { - // summary: - // Base class for validating, serializable, range-bound date or time text box. - - // constraints: dijit.form._DateTimeTextBox.__Constraints - // Despite the name, this parameter specifies both constraints on the input - // (including starting/ending dates/times allowed) as well as - // formatting options like whether the date is displayed in long (ex: December 25, 2005) - // or short (ex: 12/25/2005) format. See `dijit.form._DateTimeTextBox.__Constraints` for details. - /*===== - constraints: {}, - ======*/ - - // Override ValidationTextBox.regExpGen().... we use a reg-ex generating function rather - // than a straight regexp to deal with locale (plus formatting options too?) - regExpGen: dojo.date.locale.regexp, - - // datePackage: String - // JavaScript namespace to find calendar routines. Uses Gregorian calendar routines - // at dojo.date, by default. - datePackage: "dojo.date", - - // Override _FormWidget.compare() to work for dates/times - compare: dojo.date.compare, - - format: function(/*Date*/ value, /*dojo.date.locale.__FormatOptions*/ constraints){ - // summary: - // Formats the value as a Date, according to specified locale (second argument) - // tags: - // protected - if(!value){ return ''; } - return this.dateLocaleModule.format(value, constraints); - }, - - parse: function(/*String*/ value, /*dojo.date.locale.__FormatOptions*/ constraints){ - // summary: - // Parses as string as a Date, according to constraints - // tags: - // protected - - return this.dateLocaleModule.parse(value, constraints) || (this._isEmpty(value) ? null : undefined); // Date - }, - - // Overrides ValidationTextBox.serialize() to serialize a date in canonical ISO format. - serialize: function(/*anything*/val, /*Object?*/options){ - if(val.toGregorian){ - val = val.toGregorian(); - } - return dojo.date.stamp.toISOString(val, options); - }, - - // value: Date - // The value of this widget as a JavaScript Date object. Use get("value") / set("value", val) to manipulate. - // When passed to the parser in markup, must be specified according to `dojo.date.stamp.fromISOString` - value: new Date(""), // value.toString()="NaN" - _blankValue: null, // used by filter() when the textbox is blank - - // popupClass: [protected extension] String - // Name of the popup widget class used to select a date/time. - // Subclasses should specify this. - popupClass: "", // default is no popup = text only - - - // _selector: [protected extension] String - // Specifies constraints.selector passed to dojo.date functions, should be either - // "date" or "time". - // Subclass must specify this. - _selector: "", - - constructor: function(/*Object*/args){ - var dateClass = args.datePackage ? args.datePackage + ".Date" : "Date"; - this.dateClassObj = dojo.getObject(dateClass, false); - this.value = new this.dateClassObj(""); - - this.datePackage = args.datePackage || this.datePackage; - this.dateLocaleModule = dojo.getObject(this.datePackage + ".locale", false); - this.regExpGen = this.dateLocaleModule.regexp; - }, - - _setConstraintsAttr: function(/* Object */ constraints){ - constraints.selector = this._selector; - constraints.fullYear = true; // see #5465 - always format with 4-digit years - var fromISO = dojo.date.stamp.fromISOString; - if(typeof constraints.min == "string"){ constraints.min = fromISO(constraints.min); } - if(typeof constraints.max == "string"){ constraints.max = fromISO(constraints.max); } - this.inherited(arguments, [constraints]); - }, - - _onFocus: function(/*Event*/ evt){ - // summary: - // open the popup - this._open(); - this.inherited(arguments); - }, - - _setValueAttr: function(/*Date*/ value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){ - // summary: - // Sets the date on this textbox. Note that `value` must be like a Javascript Date object. - if(value !== undefined){ - if(!value || value.toString() == dijit.form._DateTimeTextBox.prototype.value.toString()){ - value = null; - } - if(value instanceof Date && !(this.dateClassObj instanceof Date)){ - value = new this.dateClassObj(value); - } - } - this.inherited(arguments, [value, priorityChange, formattedValue]); - if(this._picker){ - // #3948: fix blank date on popup only - if(!value){value = new this.dateClassObj();} - this._picker.set('value', value); - } - }, - - _open: function(){ - // summary: - // opens the TimePicker, and sets the onValueSelected value - - if(this.disabled || this.readOnly || !this.popupClass){return;} - - var textBox = this; - - if(!this._picker){ - var PopupProto = dojo.getObject(this.popupClass, false); - this._picker = new PopupProto({ - onValueSelected: function(value){ - if(textBox._tabbingAway){ - delete textBox._tabbingAway; - }else{ - textBox.focus(); // focus the textbox before the popup closes to avoid reopening the popup - } - setTimeout(dojo.hitch(textBox, "_close"), 1); // allow focus time to take - - // this will cause InlineEditBox and other handlers to do stuff so make sure it's last - dijit.form._DateTimeTextBox.superclass._setValueAttr.call(textBox, value, true); - }, - id: this.id + "_popup", - dir: textBox.dir, - lang: textBox.lang, - value: this.get('value') || new this.dateClassObj(), - constraints: textBox.constraints, - - datePackage: textBox.datePackage, - - isDisabledDate: function(/*Date*/ date){ - // summary: - // disables dates outside of the min/max of the _DateTimeTextBox - var compare = dojo.date.compare; - var constraints = textBox.constraints; - return constraints && ( - (constraints.min && compare(constraints.min, date, textBox._selector) > 0) || - (constraints.max && compare(constraints.max, date, textBox._selector) < 0) - ); - } - }); - } - if(!this._opened){ - // Open drop down. Align left sides of input box and drop down, even in RTL mode, - // otherwise positioning thrown off when the drop down width is changed in marginBox call below (#10676) - dijit.popup.open({ - parent: this, - popup: this._picker, - orient: {'BL':'TL', 'TL':'BL'}, - around: this.domNode, - onCancel: dojo.hitch(this, this._close), - onClose: function(){ textBox._opened=false; } - }); - this._opened=true; - } - - dojo.marginBox(this._picker.domNode,{ w:this.domNode.offsetWidth }); - }, - - _close: function(){ - if(this._opened){ - dijit.popup.close(this._picker); - this._opened=false; - } - }, - - _onBlur: function(){ - // summary: - // Called magically when focus has shifted away from this widget and it's dropdown - this._close(); - if(this._picker){ - // teardown so that constraints will be rebuilt next time (redundant reference: #6002) - this._picker.destroy(); - delete this._picker; - } - this.inherited(arguments); - // don't focus on <input>. the user has explicitly focused on something else. - }, - - _getDisplayedValueAttr: function(){ - return this.textbox.value; - }, - - _setDisplayedValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange){ - this._setValueAttr(this.parse(value, this.constraints), priorityChange, value); - }, - - destroy: function(){ - if(this._picker){ - this._picker.destroy(); - delete this._picker; - } - this.inherited(arguments); - }, - - postCreate: function(){ - this.inherited(arguments); - this.connect(this.focusNode, 'onkeypress', this._onKeyPress); - this.connect(this.focusNode, 'onclick', this._open); - }, - - _onKeyPress: function(/*Event*/ e){ - // summary: - // Handler for keypress events - - var p = this._picker, dk = dojo.keys; - // Handle the key in the picker, if it has a handler. If the handler - // returns false, then don't handle any other keys. - if(p && this._opened && p.handleKey){ - if(p.handleKey(e) === false){ return; } - } - if(this._opened && e.charOrCode == dk.ESCAPE && !(e.shiftKey || e.ctrlKey || e.altKey || e.metaKey)){ - this._close(); - dojo.stopEvent(e); - }else if(!this._opened && e.charOrCode == dk.DOWN_ARROW){ - this._open(); - dojo.stopEvent(e); - }else if(e.charOrCode === dk.TAB){ - this._tabbingAway = true; - }else if(this._opened && (e.keyChar || e.charOrCode === dk.BACKSPACE || e.charOrCode == dk.DELETE)){ - // Replace the element - but do it after a delay to allow for - // filtering to occur - setTimeout(dojo.hitch(this, function(){ - if(this._picker && this._opened){ - dijit.placeOnScreenAroundElement(p.domNode.parentNode, this.domNode, {'BL':'TL', 'TL':'BL'}, p.orient ? dojo.hitch(p, "orient") : null); - } - }), 1); - } - } - } -); - -} - -if(!dojo._hasResource["dijit.form.DateTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.DateTextBox"] = true; -dojo.provide("dijit.form.DateTextBox"); - - - - -dojo.declare( - "dijit.form.DateTextBox", - dijit.form._DateTimeTextBox, - { - // summary: - // A validating, serializable, range-bound date text box with a drop down calendar - // - // Example: - // | new dijit.form.DateTextBox({value: new Date(2009, 0, 20)}) - // - // Example: - // | <input dojotype='dijit.form.DateTextBox' value='2009-01-20'> - - baseClass: "dijitTextBox dijitDateTextBox", - popupClass: "dijit.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 `dojo.date.stamp.fromISOString` - value: new Date("") // value.toString()="NaN" - } -); - -} - -if(!dojo._hasResource["dijit.form._Spinner"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form._Spinner"] = true; -dojo.provide("dijit.form._Spinner"); - - - -dojo.declare( - "dijit.form._Spinner", - dijit.form.RangeBoundTextBox, - { - // summary: - // Mixin for validation widgets with a spinner. - // description: - // This class basically (conceptually) extends `dijit.form.ValidationTextBox`. - // It modifies the template to have up/down arrows, and provides related handling code. - - // defaultTimeout: Number - // Number of milliseconds before a held arrow key or up/down button becomes typematic - defaultTimeout: 500, - - // minimumTimeout: Number - // minimum number of milliseconds that typematic event fires when held key or button is held - minimumTimeout: 10, - - // timeoutChangeRate: Number - // Fraction of time used to change the typematic timer between events. - // 1.0 means that each typematic event fires at defaultTimeout intervals. - // < 1.0 means that each typematic event fires at an increasing faster rate. - timeoutChangeRate: 0.90, - - // smallDelta: Number - // Adjust the value by this much when spinning using the arrow keys/buttons - smallDelta: 1, - - // largeDelta: Number - // Adjust the value by this much when spinning using the PgUp/Dn keys - largeDelta: 10, - - templateString: dojo.cache("dijit.form", "templates/Spinner.html", "<div class=\"dijit dijitReset dijitInlineTable dijitLeft\"\n\tid=\"widget_${id}\" waiRole=\"presentation\"\n\t><div class=\"dijitReset dijitButtonNode dijitSpinnerButtonContainer\"\n\t\t><input class=\"dijitReset dijitInputField dijitSpinnerButtonInner\" type=\"text\" tabIndex=\"-1\" readOnly waiRole=\"presentation\"\n\t\t/><div class=\"dijitReset dijitLeft dijitButtonNode dijitArrowButton dijitUpArrowButton\"\n\t\t\tdojoAttachPoint=\"upArrowNode\"\n\t\t\t><div class=\"dijitArrowButtonInner\"\n\t\t\t\t><input class=\"dijitReset dijitInputField\" value=\"▲\" type=\"text\" tabIndex=\"-1\" readOnly waiRole=\"presentation\"\n\t\t\t\t\t${_buttonInputDisabled}\n\t\t\t/></div\n\t\t></div\n\t\t><div class=\"dijitReset dijitLeft dijitButtonNode dijitArrowButton dijitDownArrowButton\"\n\t\t\tdojoAttachPoint=\"downArrowNode\"\n\t\t\t><div class=\"dijitArrowButtonInner\"\n\t\t\t\t><input class=\"dijitReset dijitInputField\" value=\"▼\" type=\"text\" tabIndex=\"-1\" readOnly waiRole=\"presentation\"\n\t\t\t\t\t${_buttonInputDisabled}\n\t\t\t/></div\n\t\t></div\n\t></div\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"Χ \" type=\"text\" tabIndex=\"-1\" readOnly waiRole=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class='dijitReset dijitInputInner' dojoAttachPoint=\"textbox,focusNode\" type=\"${type}\" dojoAttachEvent=\"onkeypress:_onKeyPress\"\n\t\t\twaiRole=\"spinbutton\" autocomplete=\"off\" ${!nameAttrSetting}\n\t/></div\n></div>\n"), - - baseClass: "dijitTextBox dijitSpinner", - - // Set classes like dijitUpArrowButtonHover or dijitDownArrowButtonActive depending on - // mouse action over specified node - cssStateNodes: { - "upArrowNode": "dijitUpArrowButton", - "downArrowNode": "dijitDownArrowButton" - }, - - adjust: function(/* Object */ val, /*Number*/ delta){ - // summary: - // Overridable function used to adjust a primitive value(Number/Date/...) by the delta amount specified. - // The val is adjusted in a way that makes sense to the object type. - // tags: - // protected extension - return val; - }, - - _arrowPressed: function(/*Node*/ nodePressed, /*Number*/ direction, /*Number*/ increment){ - // summary: - // Handler for arrow button or arrow key being pressed - if(this.disabled || this.readOnly){ return; } - this._setValueAttr(this.adjust(this.get('value'), direction*increment), false); - dijit.selectInputText(this.textbox, this.textbox.value.length); - }, - - _arrowReleased: function(/*Node*/ node){ - // summary: - // Handler for arrow button or arrow key being released - this._wheelTimer = null; - if(this.disabled || this.readOnly){ return; } - }, - - _typematicCallback: function(/*Number*/ count, /*DOMNode*/ node, /*Event*/ evt){ - var inc=this.smallDelta; - if(node == this.textbox){ - var k=dojo.keys; - var key = evt.charOrCode; - inc = (key == k.PAGE_UP || key == k.PAGE_DOWN) ? this.largeDelta : this.smallDelta; - node = (key == k.UP_ARROW || key == k.PAGE_UP) ? this.upArrowNode : this.downArrowNode; - } - if(count == -1){ this._arrowReleased(node); } - else{ this._arrowPressed(node, (node == this.upArrowNode) ? 1 : -1, inc); } - }, - - _wheelTimer: null, - _mouseWheeled: function(/*Event*/ evt){ - // summary: - // Mouse wheel listener where supported - - dojo.stopEvent(evt); - // FIXME: Safari bubbles - - // be nice to DOH and scroll as much as the event says to - var scrollAmount = evt.detail ? (evt.detail * -1) : (evt.wheelDelta / 120); - if(scrollAmount !== 0){ - var node = this[(scrollAmount > 0 ? "upArrowNode" : "downArrowNode" )]; - - this._arrowPressed(node, scrollAmount, this.smallDelta); - - if(!this._wheelTimer){ - clearTimeout(this._wheelTimer); - } - this._wheelTimer = setTimeout(dojo.hitch(this,"_arrowReleased",node), 50); - } - - }, - - postCreate: function(){ - this.inherited(arguments); - - // extra listeners - this.connect(this.domNode, !dojo.isMozilla ? "onmousewheel" : 'DOMMouseScroll', "_mouseWheeled"); - this._connects.push(dijit.typematic.addListener(this.upArrowNode, this.textbox, {charOrCode:dojo.keys.UP_ARROW,ctrlKey:false,altKey:false,shiftKey:false,metaKey:false}, this, "_typematicCallback", this.timeoutChangeRate, this.defaultTimeout, this.minimumTimeout)); - this._connects.push(dijit.typematic.addListener(this.downArrowNode, this.textbox, {charOrCode:dojo.keys.DOWN_ARROW,ctrlKey:false,altKey:false,shiftKey:false,metaKey:false}, this, "_typematicCallback", this.timeoutChangeRate, this.defaultTimeout, this.minimumTimeout)); - this._connects.push(dijit.typematic.addListener(this.upArrowNode, this.textbox, {charOrCode:dojo.keys.PAGE_UP,ctrlKey:false,altKey:false,shiftKey:false,metaKey:false}, this, "_typematicCallback", this.timeoutChangeRate, this.defaultTimeout, this.minimumTimeout)); - this._connects.push(dijit.typematic.addListener(this.downArrowNode, this.textbox, {charOrCode:dojo.keys.PAGE_DOWN,ctrlKey:false,altKey:false,shiftKey:false,metaKey:false}, this, "_typematicCallback", this.timeoutChangeRate, this.defaultTimeout, this.minimumTimeout)); - } -}); - -} - -if(!dojo._hasResource["dijit.form.NumberSpinner"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.NumberSpinner"] = true; -dojo.provide("dijit.form.NumberSpinner"); - - - - -dojo.declare("dijit.form.NumberSpinner", - [dijit.form._Spinner, dijit.form.NumberTextBoxMixin], - { - // 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 == dojo.keys.HOME || e.charOrCode == dojo.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 == dojo.keys.HOME ? "min" : "max")]; - if(typeof value == "number"){ - this._setValueAttr(value, false); - } - // eat home or end key whether we change the value or not - dojo.stopEvent(e); - } - } -}); - -} - -if(!dojo._hasResource["dijit.form.MultiSelect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.MultiSelect"] = true; -dojo.provide("dijit.form.MultiSelect"); - - - -dojo.declare("dijit.form.MultiSelect", dijit.form._FormValueWidget, { - // summary: - // Widget version of a <select multiple=true> element, - // for selecting multiple options. - - // size: Number - // Number of elements to display on a page - // NOTE: may be removed in version 2.0, since elements may have variable height; - // set the size via style="..." or CSS class names instead. - size: 7, - - templateString: "<select multiple='true' ${!nameAttrSetting} dojoAttachPoint='containerNode,focusNode' dojoAttachEvent='onchange: _onChange'></select>", - - attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, { - size: "focusNode" - }), - - reset: function(){ - // summary: - // Reset the widget's value to what it was at initialization time - - // TODO: once we inherit from FormValueWidget this won't be needed - this._hasBeenBlurred = false; - this._setValueAttr(this._resetValue, true); - }, - - addSelected: function(/* dijit.form.MultiSelect */ select){ - // summary: - // Move the selected nodes of a passed Select widget - // instance to this Select widget. - // - // example: - // | // move all the selected values from "bar" to "foo" - // | dijit.byId("foo").addSelected(dijit.byId("bar")); - - select.getSelected().forEach(function(n){ - this.containerNode.appendChild(n); - // scroll to bottom to see item - // cannot use scrollIntoView since <option> tags don't support all attributes - // does not work on IE due to a bug where <select> always shows scrollTop = 0 - this.domNode.scrollTop = this.domNode.offsetHeight; // overshoot will be ignored - // scrolling the source select is trickier esp. on safari who forgets to change the scrollbar size - var oldscroll = select.domNode.scrollTop; - select.domNode.scrollTop = 0; - select.domNode.scrollTop = oldscroll; - },this); - }, - - getSelected: function(){ - // summary: - // Access the NodeList of the selected options directly - return dojo.query("option",this.containerNode).filter(function(n){ - return n.selected; // Boolean - }); // dojo.NodeList - }, - - _getValueAttr: function(){ - // summary: - // Hook so attr('value') works. - // description: - // Returns an array of the selected options' values. - return this.getSelected().map(function(n){ - return n.value; - }); - }, - - multiple: true, // for Form - - _setValueAttr: function(/* Array */values){ - // summary: - // Hook so attr('value', values) works. - // description: - // Set the value(s) of this Select based on passed values - dojo.query("option",this.containerNode).forEach(function(n){ - n.selected = (dojo.indexOf(values,n.value) != -1); - }); - }, - - invertSelection: function(onChange){ - // summary: - // Invert the selection - // onChange: Boolean - // If null, onChange is not fired. - dojo.query("option",this.containerNode).forEach(function(n){ - n.selected = !n.selected; - }); - this._handleOnChange(this.get('value'), onChange == true); - }, - - _onChange: function(/*Event*/ e){ - this._handleOnChange(this.get('value'), true); - }, - - // for layout widgets: - resize: function(/* Object */size){ - if(size){ - dojo.marginBox(this.domNode, size); - } - }, - - postCreate: function(){ - this._onChange(); - } -}); - -} - -if(!dojo._hasResource["dijit.form.HorizontalSlider"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.HorizontalSlider"] = true; -dojo.provide("dijit.form.HorizontalSlider"); - - - - - - - - -dojo.declare( - "dijit.form.HorizontalSlider", - [dijit.form._FormValueWidget, dijit._Container], -{ - // summary: - // A form widget that allows one to select a value with a horizontally draggable handle - - templateString: dojo.cache("dijit.form", "templates/HorizontalSlider.html", "<table class=\"dijit dijitReset dijitSlider dijitSliderH\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" rules=\"none\" dojoAttachEvent=\"onkeypress:_onKeyPress,onkeyup:_onKeyUp\"\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\" colspan=\"2\"></td\n\t\t><td dojoAttachPoint=\"topDecoration\" class=\"dijitReset dijitSliderDecoration dijitSliderDecorationT dijitSliderDecorationH\"></td\n\t\t><td class=\"dijitReset\" colspan=\"2\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset dijitSliderButtonContainer dijitSliderButtonContainerH\"\n\t\t\t><div class=\"dijitSliderDecrementIconH\" tabIndex=\"-1\" style=\"display:none\" dojoAttachPoint=\"decrementButton\"><span class=\"dijitSliderButtonInner\">-</span></div\n\t\t></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><div class=\"dijitSliderBar dijitSliderBumper dijitSliderBumperH dijitSliderLeftBumper\" dojoAttachEvent=\"onmousedown:_onClkDecBumper\"></div\n\t\t></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><input dojoAttachPoint=\"valueNode\" type=\"hidden\" ${!nameAttrSetting}\n\t\t\t/><div class=\"dijitReset dijitSliderBarContainerH\" waiRole=\"presentation\" dojoAttachPoint=\"sliderBarContainer\"\n\t\t\t\t><div waiRole=\"presentation\" dojoAttachPoint=\"progressBar\" class=\"dijitSliderBar dijitSliderBarH dijitSliderProgressBar dijitSliderProgressBarH\" dojoAttachEvent=\"onmousedown:_onBarClick\"\n\t\t\t\t\t><div class=\"dijitSliderMoveable dijitSliderMoveableH\"\n\t\t\t\t\t\t><div dojoAttachPoint=\"sliderHandle,focusNode\" class=\"dijitSliderImageHandle dijitSliderImageHandleH\" dojoAttachEvent=\"onmousedown:_onHandleClick\" waiRole=\"slider\" valuemin=\"${minimum}\" valuemax=\"${maximum}\"></div\n\t\t\t\t\t></div\n\t\t\t\t></div\n\t\t\t\t><div waiRole=\"presentation\" dojoAttachPoint=\"remainingBar\" class=\"dijitSliderBar dijitSliderBarH dijitSliderRemainingBar dijitSliderRemainingBarH\" dojoAttachEvent=\"onmousedown:_onBarClick\"></div\n\t\t\t></div\n\t\t></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><div class=\"dijitSliderBar dijitSliderBumper dijitSliderBumperH dijitSliderRightBumper\" dojoAttachEvent=\"onmousedown:_onClkIncBumper\"></div\n\t\t></td\n\t\t><td class=\"dijitReset dijitSliderButtonContainer dijitSliderButtonContainerH\"\n\t\t\t><div class=\"dijitSliderIncrementIconH\" tabIndex=\"-1\" style=\"display:none\" dojoAttachPoint=\"incrementButton\"><span class=\"dijitSliderButtonInner\">+</span></div\n\t\t></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\" colspan=\"2\"></td\n\t\t><td dojoAttachPoint=\"containerNode,bottomDecoration\" class=\"dijitReset dijitSliderDecoration dijitSliderDecorationB dijitSliderDecorationH\"></td\n\t\t><td class=\"dijitReset\" colspan=\"2\"></td\n\t></tr\n></table>\n"), - - // Overrides FormValueWidget.value to indicate numeric value - value: 0, - - // showButtons: Boolean - // Show increment/decrement buttons at the ends of the slider? - showButtons: true, - - // minimum:: Integer - // The minimum value the slider can be set to. - minimum: 0, - - // maximum: Integer - // The maximum value the slider can be set to. - maximum: 100, - - // discreteValues: Integer - // If specified, indicates that the slider handle has only 'discreteValues' possible positions, - // and that after dragging the handle, it will snap to the nearest possible position. - // Thus, the slider has only 'discreteValues' possible values. - // - // For example, if minimum=10, maxiumum=30, and discreteValues=3, then the slider handle has - // three possible positions, representing values 10, 20, or 30. - // - // If discreteValues is not specified or if it's value is higher than the number of pixels - // in the slider bar, then the slider handle can be moved freely, and the slider's value will be - // computed/reported based on pixel position (in this case it will likely be fractional, - // such as 123.456789). - discreteValues: Infinity, - - // pageIncrement: Integer - // If discreteValues is also specified, this indicates the amount of clicks (ie, snap positions) - // that the slider handle is moved via pageup/pagedown keys. - // If discreteValues is not specified, it indicates the number of pixels. - pageIncrement: 2, - - // clickSelect: Boolean - // If clicking the slider bar changes the value or not - clickSelect: true, - - // slideDuration: Number - // The time in ms to take to animate the slider handle from 0% to 100%, - // when clicking the slider bar to make the handle move. - slideDuration: dijit.defaultDuration, - - // Flag to _Templated (TODO: why is this here? I see no widgets in the template.) - widgetsInTemplate: true, - - attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, { - id: "" - }), - - baseClass: "dijitSlider", - - // Apply CSS classes to up/down arrows and handle per mouse state - cssStateNodes: { - incrementButton: "dijitSliderIncrementButton", - decrementButton: "dijitSliderDecrementButton", - focusNode: "dijitSliderThumb" - }, - - _mousePixelCoord: "pageX", - _pixelCount: "w", - _startingPixelCoord: "x", - _startingPixelCount: "l", - _handleOffsetCoord: "left", - _progressPixelSize: "width", - - _onKeyUp: function(/*Event*/ e){ - if(this.disabled || this.readOnly || e.altKey || e.ctrlKey || e.metaKey){ return; } - this._setValueAttr(this.value, true); - }, - - _onKeyPress: function(/*Event*/ e){ - if(this.disabled || this.readOnly || e.altKey || e.ctrlKey || e.metaKey){ return; } - switch(e.charOrCode){ - case dojo.keys.HOME: - this._setValueAttr(this.minimum, false); - break; - case dojo.keys.END: - this._setValueAttr(this.maximum, false); - break; - // this._descending === false: if ascending vertical (min on top) - // (this._descending || this.isLeftToRight()): if left-to-right horizontal or descending vertical - case ((this._descending || this.isLeftToRight()) ? dojo.keys.RIGHT_ARROW : dojo.keys.LEFT_ARROW): - case (this._descending === false ? dojo.keys.DOWN_ARROW : dojo.keys.UP_ARROW): - case (this._descending === false ? dojo.keys.PAGE_DOWN : dojo.keys.PAGE_UP): - this.increment(e); - break; - case ((this._descending || this.isLeftToRight()) ? dojo.keys.LEFT_ARROW : dojo.keys.RIGHT_ARROW): - case (this._descending === false ? dojo.keys.UP_ARROW : dojo.keys.DOWN_ARROW): - case (this._descending === false ? dojo.keys.PAGE_UP : dojo.keys.PAGE_DOWN): - this.decrement(e); - break; - default: - return; - } - dojo.stopEvent(e); - }, - - _onHandleClick: function(e){ - if(this.disabled || this.readOnly){ return; } - if(!dojo.isIE){ - // make sure you get focus when dragging the handle - // (but don't do on IE because it causes a flicker on mouse up (due to blur then focus) - dijit.focus(this.sliderHandle); - } - dojo.stopEvent(e); - }, - - _isReversed: function(){ - // summary: - // Returns true if direction is from right to left - // tags: - // protected extension - return !this.isLeftToRight(); - }, - - _onBarClick: function(e){ - if(this.disabled || this.readOnly || !this.clickSelect){ return; } - dijit.focus(this.sliderHandle); - dojo.stopEvent(e); - var abspos = dojo.position(this.sliderBarContainer, true); - var pixelValue = e[this._mousePixelCoord] - abspos[this._startingPixelCoord]; - this._setPixelValue(this._isReversed() ? (abspos[this._pixelCount] - pixelValue) : pixelValue, abspos[this._pixelCount], true); - this._movable.onMouseDown(e); - }, - - _setPixelValue: function(/*Number*/ pixelValue, /*Number*/ maxPixels, /*Boolean, optional*/ priorityChange){ - if(this.disabled || this.readOnly){ return; } - pixelValue = pixelValue < 0 ? 0 : maxPixels < pixelValue ? maxPixels : pixelValue; - var count = this.discreteValues; - if(count <= 1 || count == Infinity){ count = maxPixels; } - count--; - var pixelsPerValue = maxPixels / count; - var wholeIncrements = Math.round(pixelValue / pixelsPerValue); - this._setValueAttr((this.maximum-this.minimum)*wholeIncrements/count + this.minimum, priorityChange); - }, - - _setValueAttr: function(/*Number*/ value, /*Boolean, optional*/ priorityChange){ - // summary: - // Hook so attr('value', value) works. - this.valueNode.value = this.value = value; - dijit.setWaiState(this.focusNode, "valuenow", value); - this.inherited(arguments); - var percent = (value - this.minimum) / (this.maximum - this.minimum); - var progressBar = (this._descending === false) ? this.remainingBar : this.progressBar; - var remainingBar = (this._descending === false) ? this.progressBar : this.remainingBar; - if(this._inProgressAnim && this._inProgressAnim.status != "stopped"){ - this._inProgressAnim.stop(true); - } - if(priorityChange && this.slideDuration > 0 && progressBar.style[this._progressPixelSize]){ - // animate the slider - var _this = this; - var props = {}; - var start = parseFloat(progressBar.style[this._progressPixelSize]); - var duration = this.slideDuration * (percent-start/100); - if(duration == 0){ return; } - if(duration < 0){ duration = 0 - duration; } - props[this._progressPixelSize] = { start: start, end: percent*100, units:"%" }; - this._inProgressAnim = dojo.animateProperty({ node: progressBar, duration: duration, - onAnimate: function(v){ remainingBar.style[_this._progressPixelSize] = (100-parseFloat(v[_this._progressPixelSize])) + "%"; }, - onEnd: function(){ delete _this._inProgressAnim; }, - properties: props - }) - this._inProgressAnim.play(); - } - else{ - progressBar.style[this._progressPixelSize] = (percent*100) + "%"; - remainingBar.style[this._progressPixelSize] = ((1-percent)*100) + "%"; - } - }, - - _bumpValue: function(signedChange, /*Boolean, optional*/ priorityChange){ - if(this.disabled || this.readOnly){ return; } - var s = dojo.getComputedStyle(this.sliderBarContainer); - var c = dojo._getContentBox(this.sliderBarContainer, s); - var count = this.discreteValues; - if(count <= 1 || count == Infinity){ count = c[this._pixelCount]; } - count--; - var value = (this.value - this.minimum) * count / (this.maximum - this.minimum) + signedChange; - if(value < 0){ value = 0; } - if(value > count){ value = count; } - value = value * (this.maximum - this.minimum) / count + this.minimum; - this._setValueAttr(value, priorityChange); - }, - - _onClkBumper: function(val){ - if(this.disabled || this.readOnly || !this.clickSelect){ return; } - this._setValueAttr(val, true); - }, - - _onClkIncBumper: function(){ - this._onClkBumper(this._descending === false ? this.minimum : this.maximum); - }, - - _onClkDecBumper: function(){ - this._onClkBumper(this._descending === false ? this.maximum : this.minimum); - }, - - decrement: function(/*Event*/ e){ - // summary: - // Decrement slider - // tags: - // private - this._bumpValue(e.charOrCode == dojo.keys.PAGE_DOWN ? -this.pageIncrement : -1); - }, - - increment: function(/*Event*/ e){ - // summary: - // Increment slider - // tags: - // private - this._bumpValue(e.charOrCode == dojo.keys.PAGE_UP ? this.pageIncrement : 1); - }, - - _mouseWheeled: function(/*Event*/ evt){ - // summary: - // Event handler for mousewheel where supported - dojo.stopEvent(evt); - var janky = !dojo.isMozilla; - var scroll = evt[(janky ? "wheelDelta" : "detail")] * (janky ? 1 : -1); - this._bumpValue(scroll < 0 ? -1 : 1, true); // negative scroll acts like a decrement - }, - - startup: function(){ - if(this._started){ return; } - - dojo.forEach(this.getChildren(), function(child){ - if(this[child.container] != this.containerNode){ - this[child.container].appendChild(child.domNode); - } - }, this); - - this.inherited(arguments); - }, - - _typematicCallback: function(/*Number*/ count, /*Object*/ button, /*Event*/ e){ - if(count == -1){ - this._setValueAttr(this.value, true); - }else{ - this[(button == (this._descending? this.incrementButton : this.decrementButton)) ? "decrement" : "increment"](e); - } - }, - - postCreate: function(){ - if(this.showButtons){ - this.incrementButton.style.display=""; - this.decrementButton.style.display=""; - this._connects.push(dijit.typematic.addMouseListener( - this.decrementButton, this, "_typematicCallback", 25, 500)); - this._connects.push(dijit.typematic.addMouseListener( - this.incrementButton, this, "_typematicCallback", 25, 500)); - } - this.connect(this.domNode, !dojo.isMozilla ? "onmousewheel" : "DOMMouseScroll", "_mouseWheeled"); - - // define a custom constructor for a SliderMover that points back to me - var mover = dojo.declare(dijit.form._SliderMover, { - widget: this - }); - - this._movable = new dojo.dnd.Moveable(this.sliderHandle, {mover: mover}); - // find any associated label element and add to slider focusnode. - var label=dojo.query('label[for="'+this.id+'"]'); - if(label.length){ - label[0].id = (this.id+"_label"); - dijit.setWaiState(this.focusNode, "labelledby", label[0].id); - } - dijit.setWaiState(this.focusNode, "valuemin", this.minimum); - dijit.setWaiState(this.focusNode, "valuemax", this.maximum); - - this.inherited(arguments); - this._layoutHackIE7(); - }, - - destroy: function(){ - this._movable.destroy(); - if(this._inProgressAnim && this._inProgressAnim.status != "stopped"){ - this._inProgressAnim.stop(true); - } - this._supportingWidgets = dijit.findWidgets(this.domNode); // tells destroy about pseudo-child widgets (ruler/labels) - this.inherited(arguments); - } -}); - -dojo.declare("dijit.form._SliderMover", - dojo.dnd.Mover, -{ - onMouseMove: function(e){ - var widget = this.widget; - var abspos = widget._abspos; - if(!abspos){ - abspos = widget._abspos = dojo.position(widget.sliderBarContainer, true); - widget._setPixelValue_ = dojo.hitch(widget, "_setPixelValue"); - widget._isReversed_ = widget._isReversed(); - } - var pixelValue = e[widget._mousePixelCoord] - abspos[widget._startingPixelCoord]; - widget._setPixelValue_(widget._isReversed_ ? (abspos[widget._pixelCount]-pixelValue) : pixelValue, abspos[widget._pixelCount], false); - }, - - destroy: function(e){ - dojo.dnd.Mover.prototype.destroy.apply(this, arguments); - var widget = this.widget; - widget._abspos = null; - widget._setValueAttr(widget.value, true); - } -}); - - - -} - -if(!dojo._hasResource["dijit.form.VerticalSlider"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.VerticalSlider"] = true; -dojo.provide("dijit.form.VerticalSlider"); - - - -dojo.declare( - "dijit.form.VerticalSlider", - dijit.form.HorizontalSlider, -{ - // summary: - // A form widget that allows one to select a value with a vertically draggable handle - - templateString: dojo.cache("dijit.form", "templates/VerticalSlider.html", "<table class=\"dijit dijitReset dijitSlider dijitSliderV\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" rules=\"none\" dojoAttachEvent=\"onkeypress:_onKeyPress,onkeyup:_onKeyUp\"\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\"></td\n\t\t><td class=\"dijitReset dijitSliderButtonContainer dijitSliderButtonContainerV\"\n\t\t\t><div class=\"dijitSliderIncrementIconV\" tabIndex=\"-1\" style=\"display:none\" dojoAttachPoint=\"decrementButton\"><span class=\"dijitSliderButtonInner\">+</span></div\n\t\t></td\n\t\t><td class=\"dijitReset\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\"></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><center><div class=\"dijitSliderBar dijitSliderBumper dijitSliderBumperV dijitSliderTopBumper\" dojoAttachEvent=\"onmousedown:_onClkIncBumper\"></div></center\n\t\t></td\n\t\t><td class=\"dijitReset\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td dojoAttachPoint=\"leftDecoration\" class=\"dijitReset dijitSliderDecoration dijitSliderDecorationL dijitSliderDecorationV\"></td\n\t\t><td class=\"dijitReset\" style=\"height:100%;\"\n\t\t\t><input dojoAttachPoint=\"valueNode\" type=\"hidden\" ${!nameAttrSetting}\n\t\t\t/><center class=\"dijitReset dijitSliderBarContainerV\" waiRole=\"presentation\" dojoAttachPoint=\"sliderBarContainer\"\n\t\t\t\t><div waiRole=\"presentation\" dojoAttachPoint=\"remainingBar\" class=\"dijitSliderBar dijitSliderBarV dijitSliderRemainingBar dijitSliderRemainingBarV\" dojoAttachEvent=\"onmousedown:_onBarClick\"><!--#5629--></div\n\t\t\t\t><div waiRole=\"presentation\" dojoAttachPoint=\"progressBar\" class=\"dijitSliderBar dijitSliderBarV dijitSliderProgressBar dijitSliderProgressBarV\" dojoAttachEvent=\"onmousedown:_onBarClick\"\n\t\t\t\t\t><div class=\"dijitSliderMoveable dijitSliderMoveableV\" style=\"vertical-align:top;\"\n\t\t\t\t\t\t><div dojoAttachPoint=\"sliderHandle,focusNode\" class=\"dijitSliderImageHandle dijitSliderImageHandleV\" dojoAttachEvent=\"onmousedown:_onHandleClick\" waiRole=\"slider\" valuemin=\"${minimum}\" valuemax=\"${maximum}\"></div\n\t\t\t\t\t></div\n\t\t\t\t></div\n\t\t\t></center\n\t\t></td\n\t\t><td dojoAttachPoint=\"containerNode,rightDecoration\" class=\"dijitReset dijitSliderDecoration dijitSliderDecorationR dijitSliderDecorationV\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\"></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><center><div class=\"dijitSliderBar dijitSliderBumper dijitSliderBumperV dijitSliderBottomBumper\" dojoAttachEvent=\"onmousedown:_onClkDecBumper\"></div></center\n\t\t></td\n\t\t><td class=\"dijitReset\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\"></td\n\t\t><td class=\"dijitReset dijitSliderButtonContainer dijitSliderButtonContainerV\"\n\t\t\t><div class=\"dijitSliderDecrementIconV\" tabIndex=\"-1\" style=\"display:none\" dojoAttachPoint=\"incrementButton\"><span class=\"dijitSliderButtonInner\">-</span></div\n\t\t></td\n\t\t><td class=\"dijitReset\"></td\n\t></tr\n></table>\n"), - _mousePixelCoord: "pageY", - _pixelCount: "h", - _startingPixelCoord: "y", - _startingPixelCount: "t", - _handleOffsetCoord: "top", - _progressPixelSize: "height", - - // _descending: Boolean - // Specifies if the slider values go from high-on-top (true), or low-on-top (false) - // TODO: expose this in 1.2 - the css progress/remaining bar classes need to be reversed - _descending: true, - - _isReversed: function(){ - // summary: - // Overrides HorizontalSlider._isReversed. - // Indicates if values are high on top (with low numbers on the bottom). - return this._descending; - } -}); - - -} - -if(!dojo._hasResource["dijit.form.HorizontalRule"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.HorizontalRule"] = true; -dojo.provide("dijit.form.HorizontalRule"); - - - - -dojo.declare("dijit.form.HorizontalRule", [dijit._Widget, dijit._Templated], -{ - // summary: - // Hash marks for `dijit.form.HorizontalSlider` - - templateString: '<div class="dijitRuleContainer dijitRuleContainerH"></div>', - - // count: Integer - // Number of hash marks to generate - count: 3, - - // container: String - // For HorizontalSlider, this is either "topDecoration" or "bottomDecoration", - // and indicates whether this rule goes above or below the slider. - container: "containerNode", - - // ruleStyle: String - // CSS style to apply to individual hash marks - ruleStyle: "", - - _positionPrefix: '<div class="dijitRuleMark dijitRuleMarkH" style="left:', - _positionSuffix: '%;', - _suffix: '"></div>', - - _genHTML: function(pos, ndx){ - return this._positionPrefix + pos + this._positionSuffix + this.ruleStyle + this._suffix; - }, - - // _isHorizontal: [protected extension] Boolean - // VerticalRule will override this... - _isHorizontal: true, - - postCreate: function(){ - var innerHTML; - if(this.count == 1){ - innerHTML = this._genHTML(50, 0); - }else{ - var i; - var interval = 100 / (this.count-1); - if(!this._isHorizontal || this.isLeftToRight()){ - innerHTML = this._genHTML(0, 0); - for(i=1; i < this.count-1; i++){ - innerHTML += this._genHTML(interval*i, i); - } - innerHTML += this._genHTML(100, this.count-1); - }else{ - innerHTML = this._genHTML(100, 0); - for(i=1; i < this.count-1; i++){ - innerHTML += this._genHTML(100-interval*i, i); - } - innerHTML += this._genHTML(0, this.count-1); - } - } - this.domNode.innerHTML = innerHTML; - } -}); - -} - -if(!dojo._hasResource["dijit.form.VerticalRule"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.VerticalRule"] = true; -dojo.provide("dijit.form.VerticalRule"); - - - -dojo.declare("dijit.form.VerticalRule", dijit.form.HorizontalRule, -{ - // summary: - // Hash marks for the `dijit.form.VerticalSlider` - - templateString: '<div class="dijitRuleContainer dijitRuleContainerV"></div>', - _positionPrefix: '<div class="dijitRuleMark dijitRuleMarkV" style="top:', - -/*===== - // container: String - // This is either "leftDecoration" or "rightDecoration", - // to indicate whether this rule goes to the left or to the right of the slider. - // Note that on RTL system, "leftDecoration" would actually go to the right, and vice-versa. - container: "", -=====*/ - - // Overrides HorizontalRule._isHorizontal - _isHorizontal: false - -}); - - -} - -if(!dojo._hasResource["dijit.form.HorizontalRuleLabels"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.HorizontalRuleLabels"] = true; -dojo.provide("dijit.form.HorizontalRuleLabels"); - - - -dojo.declare("dijit.form.HorizontalRuleLabels", dijit.form.HorizontalRule, -{ - // summary: - // Labels for `dijit.form.HorizontalSlider` - - templateString: '<div class="dijitRuleContainer dijitRuleContainerH dijitRuleLabelsContainer dijitRuleLabelsContainerH"></div>', - - // labelStyle: String - // CSS style to apply to individual text labels - labelStyle: "", - - // labels: String[]? - // Array of text labels to render - evenly spaced from left-to-right or bottom-to-top. - // Alternately, minimum and maximum can be specified, to get numeric labels. - labels: [], - - // numericMargin: Integer - // Number of generated numeric labels that should be rendered as '' on the ends when labels[] are not specified - numericMargin: 0, - - // numericMinimum: Integer - // Leftmost label value for generated numeric labels when labels[] are not specified - minimum: 0, - - // numericMaximum: Integer - // Rightmost label value for generated numeric labels when labels[] are not specified - maximum: 1, - - // constraints: Object - // pattern, places, lang, et al (see dojo.number) for generated numeric labels when labels[] are not specified - constraints: {pattern:"#%"}, - - _positionPrefix: '<div class="dijitRuleLabelContainer dijitRuleLabelContainerH" style="left:', - _labelPrefix: '"><div class="dijitRuleLabel dijitRuleLabelH">', - _suffix: '</div></div>', - - _calcPosition: function(pos){ - // summary: - // Returns the value to be used in HTML for the label as part of the left: attribute - // tags: - // protected extension - return pos; - }, - - _genHTML: function(pos, ndx){ - return this._positionPrefix + this._calcPosition(pos) + this._positionSuffix + this.labelStyle + this._labelPrefix + this.labels[ndx] + this._suffix; - }, - - getLabels: function(){ - // summary: - // Overridable function to return array of labels to use for this slider. - // Can specify a getLabels() method instead of a labels[] array, or min/max attributes. - // tags: - // protected extension - - // if the labels array was not specified directly, then see if <li> children were - var labels = this.labels; - if(!labels.length){ - // for markup creation, labels are specified as child elements - labels = dojo.query("> li", this.srcNodeRef).map(function(node){ - return String(node.innerHTML); - }); - } - this.srcNodeRef.innerHTML = ''; - // if the labels were not specified directly and not as <li> children, then calculate numeric labels - if(!labels.length && this.count > 1){ - var start = this.minimum; - var inc = (this.maximum - start) / (this.count-1); - for(var i=0; i < this.count; i++){ - labels.push((i < this.numericMargin || i >= (this.count-this.numericMargin)) ? '' : dojo.number.format(start, this.constraints)); - start += inc; - } - } - return labels; - }, - - postMixInProperties: function(){ - this.inherited(arguments); - this.labels = this.getLabels(); - this.count = this.labels.length; - } -}); - - - -} - -if(!dojo._hasResource["dijit.form.VerticalRuleLabels"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.VerticalRuleLabels"] = true; -dojo.provide("dijit.form.VerticalRuleLabels"); - - - -dojo.declare("dijit.form.VerticalRuleLabels", dijit.form.HorizontalRuleLabels, -{ - // summary: - // Labels for the `dijit.form.VerticalSlider` - - templateString: '<div class="dijitRuleContainer dijitRuleContainerV dijitRuleLabelsContainer dijitRuleLabelsContainerV"></div>', - - _positionPrefix: '<div class="dijitRuleLabelContainer dijitRuleLabelContainerV" style="top:', - _labelPrefix: '"><span class="dijitRuleLabel dijitRuleLabelV">', - - _calcPosition: function(pos){ - // Overrides HorizontalRuleLabel._calcPosition() - return 100-pos; - }, - - // needed to prevent labels from being reversed in RTL mode - _isHorizontal: false -}); - -} - -if(!dojo._hasResource["dijit.form.SimpleTextarea"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.SimpleTextarea"] = true; -dojo.provide("dijit.form.SimpleTextarea"); - - - -dojo.declare("dijit.form.SimpleTextarea", - dijit.form.TextBox, - { - // summary: - // A simple textarea that degrades, and responds to - // minimal LayoutContainer usage, and works with dijit.form.Form. - // Doesn't automatically size according to input, like Textarea. - // - // example: - // | <textarea dojoType="dijit.form.SimpleTextarea" name="foo" value="bar" rows=30 cols=40></textarea> - // - // example: - // | new dijit.form.SimpleTextarea({ rows:20, cols:30 }, "foo"); - - baseClass: "dijitTextBox dijitTextArea", - - attributeMap: dojo.delegate(dijit.form._FormValueWidget.prototype.attributeMap, { - rows:"textbox", cols: "textbox" - }), - - // rows: Number - // The number of rows of text. - rows: "3", - - // rows: Number - // The number of characters per line. - cols: "20", - - templateString: "<textarea ${!nameAttrSetting} dojoAttachPoint='focusNode,containerNode,textbox' autocomplete='off'></textarea>", - - postMixInProperties: function(){ - // Copy value from srcNodeRef, unless user specified a value explicitly (or there is no srcNodeRef) - if(!this.value && this.srcNodeRef){ - this.value = this.srcNodeRef.value; - } - this.inherited(arguments); - }, - - filter: function(/*String*/ value){ - // Override TextBox.filter to deal with newlines... specifically (IIRC) this is for IE which writes newlines - // as \r\n instead of just \n - if(value){ - value = value.replace(/\r/g,""); - } - return this.inherited(arguments); - }, - - postCreate: function(){ - this.inherited(arguments); - if(dojo.isIE && this.cols){ // attribute selectors is not supported in IE6 - dojo.addClass(this.textbox, "dijitTextAreaCols"); - } - }, - - _previousValue: "", - _onInput: function(/*Event?*/ e){ - // Override TextBox._onInput() to enforce maxLength restriction - if(this.maxLength){ - var maxLength = parseInt(this.maxLength); - var value = this.textbox.value.replace(/\r/g,''); - var overflow = value.length - maxLength; - if(overflow > 0){ - if(e){ dojo.stopEvent(e); } - var textarea = this.textbox; - if(textarea.selectionStart){ - var pos = textarea.selectionStart; - var cr = 0; - if(dojo.isOpera){ - cr = (this.textbox.value.substring(0,pos).match(/\r/g) || []).length; - } - this.textbox.value = value.substring(0,pos-overflow-cr)+value.substring(pos-cr); - textarea.setSelectionRange(pos-overflow, pos-overflow); - }else if(dojo.doc.selection){ //IE - textarea.focus(); - var range = dojo.doc.selection.createRange(); - // delete overflow characters - range.moveStart("character", -overflow); - range.text = ''; - // show cursor - range.select(); - } - } - this._previousValue = this.textbox.value; - } - this.inherited(arguments); - } -}); - -} - -if(!dojo._hasResource["dijit.form.Textarea"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.form.Textarea"] = true; -dojo.provide("dijit.form.Textarea"); - - - -dojo.declare( - "dijit.form.Textarea", - dijit.form.SimpleTextarea, - { - // summary: - // A textarea widget that adjusts it's height according to the amount of data. - // - // description: - // A textarea that dynamically expands/contracts (changing it's height) as - // the user types, to display all the text without requiring a scroll bar. - // - // Takes nearly all the parameters (name, value, etc.) that a vanilla textarea takes. - // Rows is not supported since this widget adjusts the height. - // - // example: - // | <textarea dojoType="dijit.form.TextArea">...</textarea> - - - // Override SimpleTextArea.cols to default to width:100%, for backward compatibility - cols: "", - - _previousNewlines: 0, - _strictMode: (dojo.doc.compatMode != 'BackCompat'), // not the same as !dojo.isQuirks - - _getHeight: function(textarea){ - var newH = textarea.scrollHeight; - if(dojo.isIE){ - newH += textarea.offsetHeight - textarea.clientHeight - ((dojo.isIE < 8 && this._strictMode) ? dojo._getPadBorderExtents(textarea).h : 0); - }else if(dojo.isMoz){ - newH += textarea.offsetHeight - textarea.clientHeight; // creates room for horizontal scrollbar - }else if(dojo.isWebKit && !(dojo.isSafari < 4)){ // Safari 4.0 && Chrome - newH += dojo._getBorderExtents(textarea).h; - }else{ // Safari 3.x and Opera 9.6 - newH += dojo._getPadBorderExtents(textarea).h; - } - return newH; - }, - - _estimateHeight: function(textarea){ - // summary: - // Approximate the height when the textarea is invisible with the number of lines in the text. - // Fails when someone calls setValue with a long wrapping line, but the layout fixes itself when the user clicks inside so . . . - // In IE, the resize event is supposed to fire when the textarea becomes visible again and that will correct the size automatically. - // - textarea.style.maxHeight = ""; - textarea.style.height = "auto"; - // #rows = #newlines+1 - // Note: on Moz, the following #rows appears to be 1 too many. - // Actually, Moz is reserving room for the scrollbar. - // If you increase the font size, this behavior becomes readily apparent as the last line gets cut off without the +1. - textarea.rows = (textarea.value.match(/\n/g) || []).length + 1; - }, - - _needsHelpShrinking: dojo.isMoz || dojo.isWebKit, - - _onInput: function(){ - // Override SimpleTextArea._onInput() to deal with height adjustment - this.inherited(arguments); - if(this._busyResizing){ return; } - this._busyResizing = true; - var textarea = this.textbox; - if(textarea.scrollHeight && textarea.offsetHeight && textarea.clientHeight){ - var newH = this._getHeight(textarea) + "px"; - if(textarea.style.height != newH){ - textarea.style.maxHeight = textarea.style.height = newH; - } - if(this._needsHelpShrinking){ - if(this._setTimeoutHandle){ - clearTimeout(this._setTimeoutHandle); - } - this._setTimeoutHandle = setTimeout(dojo.hitch(this, "_shrink"), 0); // try to collapse multiple shrinks into 1 - } - }else{ - // hidden content of unknown size - this._estimateHeight(textarea); - } - this._busyResizing = false; - }, - - _busyResizing: false, - _shrink: function(){ - // grow paddingBottom to see if scrollHeight shrinks (when it is unneccesarily big) - this._setTimeoutHandle = null; - if(this._needsHelpShrinking && !this._busyResizing){ - this._busyResizing = true; - var textarea = this.textbox; - var empty = false; - if(textarea.value == ''){ - textarea.value = ' '; // prevent collapse all the way back to 0 - empty = true; - } - var scrollHeight = textarea.scrollHeight; - if(!scrollHeight){ - this._estimateHeight(textarea); - }else{ - var oldPadding = textarea.style.paddingBottom; - var newPadding = dojo._getPadExtents(textarea); - newPadding = newPadding.h - newPadding.t; - textarea.style.paddingBottom = newPadding + 1 + "px"; // tweak padding to see if height can be reduced - var newH = this._getHeight(textarea) - 1 + "px"; // see if the height changed by the 1px added - if(textarea.style.maxHeight != newH){ // if can be reduced, so now try a big chunk - textarea.style.paddingBottom = newPadding + scrollHeight + "px"; - textarea.scrollTop = 0; - textarea.style.maxHeight = this._getHeight(textarea) - scrollHeight + "px"; // scrollHeight is the added padding - } - textarea.style.paddingBottom = oldPadding; - } - if(empty){ - textarea.value = ''; - } - this._busyResizing = false; - } - }, - - resize: function(){ - // summary: - // Resizes the textarea vertically (should be called after a style/value change) - this._onInput(); - }, - - _setValueAttr: function(){ - this.inherited(arguments); - this.resize(); - }, - - postCreate: function(){ - this.inherited(arguments); - // tweak textarea style to reduce browser differences - dojo.style(this.textbox, { overflowY: 'hidden', overflowX: 'auto', boxSizing: 'border-box', MsBoxSizing: 'border-box', WebkitBoxSizing: 'border-box', MozBoxSizing: 'border-box' }); - this.connect(this.textbox, "onscroll", this._onInput); - this.connect(this.textbox, "onresize", this._onInput); - this.connect(this.textbox, "onfocus", this._onInput); // useful when a previous estimate was off a bit - this._setTimeoutHandle = setTimeout(dojo.hitch(this, "resize"), 0); - }, - - uninitialize: function(){ - if(this._setTimeoutHandle){ - clearTimeout(this._setTimeoutHandle); - } - this.inherited(arguments); - } -}); - -} - -if(!dojo._hasResource["dijit.layout.StackController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.StackController"] = true; -dojo.provide("dijit.layout.StackController"); - - - - - - - -dojo.declare( - "dijit.layout.StackController", - [dijit._Widget, dijit._Templated, dijit._Container], - { - // summary: - // Set of buttons to select a page in a page list. - // description: - // Monitors the specified StackContainer, and whenever a page is - // added, deleted, or selected, updates itself accordingly. - - templateString: "<span wairole='tablist' dojoAttachEvent='onkeypress' class='dijitStackController'></span>", - - // containerId: [const] String - // The id of the page container that I point to - containerId: "", - - // buttonWidget: [const] String - // The name of the button widget to create to correspond to each page - buttonWidget: "dijit.layout._StackButton", - - postCreate: function(){ - dijit.setWaiRole(this.domNode, "tablist"); - - this.pane2button = {}; // mapping from pane id to buttons - this.pane2handles = {}; // mapping from pane id to this.connect() handles - - // Listen to notifications from StackContainer - this.subscribe(this.containerId+"-startup", "onStartup"); - this.subscribe(this.containerId+"-addChild", "onAddChild"); - this.subscribe(this.containerId+"-removeChild", "onRemoveChild"); - this.subscribe(this.containerId+"-selectChild", "onSelectChild"); - this.subscribe(this.containerId+"-containerKeyPress", "onContainerKeyPress"); - }, - - onStartup: function(/*Object*/ info){ - // summary: - // Called after StackContainer has finished initializing - // tags: - // private - dojo.forEach(info.children, this.onAddChild, this); - if(info.selected){ - // Show button corresponding to selected pane (unless selected - // is null because there are no panes) - this.onSelectChild(info.selected); - } - }, - - destroy: function(){ - for(var pane in this.pane2button){ - this.onRemoveChild(dijit.byId(pane)); - } - this.inherited(arguments); - }, - - onAddChild: function(/*dijit._Widget*/ page, /*Integer?*/ insertIndex){ - // summary: - // Called whenever a page is added to the container. - // Create button corresponding to the page. - // tags: - // private - - // create an instance of the button widget - var cls = dojo.getObject(this.buttonWidget); - var button = new cls({ - id: this.id + "_" + page.id, - label: page.title, - dir: page.dir, - lang: page.lang, - showLabel: page.showTitle, - iconClass: page.iconClass, - closeButton: page.closable, - title: page.tooltip - }); - dijit.setWaiState(button.focusNode,"selected", "false"); - this.pane2handles[page.id] = [ - this.connect(page, 'set', function(name, value){ - var buttonAttr = { - title: 'label', - showTitle: 'showLabel', - iconClass: 'iconClass', - closable: 'closeButton', - tooltip: 'title' - }[name]; - if(buttonAttr){ - button.set(buttonAttr, value); - } - }), - this.connect(button, 'onClick', dojo.hitch(this,"onButtonClick", page)), - this.connect(button, 'onClickCloseButton', dojo.hitch(this,"onCloseButtonClick", page)) - ]; - this.addChild(button, insertIndex); - this.pane2button[page.id] = button; - page.controlButton = button; // this value might be overwritten if two tabs point to same container - if(!this._currentChild){ // put the first child into the tab order - button.focusNode.setAttribute("tabIndex", "0"); - dijit.setWaiState(button.focusNode, "selected", "true"); - this._currentChild = page; - } - // make sure all tabs have the same length - if(!this.isLeftToRight() && dojo.isIE && this._rectifyRtlTabList){ - this._rectifyRtlTabList(); - } - }, - - onRemoveChild: function(/*dijit._Widget*/ page){ - // summary: - // Called whenever a page is removed from the container. - // Remove the button corresponding to the page. - // tags: - // private - - if(this._currentChild === page){ this._currentChild = null; } - dojo.forEach(this.pane2handles[page.id], this.disconnect, this); - delete this.pane2handles[page.id]; - var button = this.pane2button[page.id]; - if(button){ - this.removeChild(button); - delete this.pane2button[page.id]; - button.destroy(); - } - delete page.controlButton; - }, - - onSelectChild: function(/*dijit._Widget*/ page){ - // summary: - // Called when a page has been selected in the StackContainer, either by me or by another StackController - // tags: - // private - - if(!page){ return; } - - if(this._currentChild){ - var oldButton=this.pane2button[this._currentChild.id]; - oldButton.set('checked', false); - dijit.setWaiState(oldButton.focusNode, "selected", "false"); - oldButton.focusNode.setAttribute("tabIndex", "-1"); - } - - var newButton=this.pane2button[page.id]; - newButton.set('checked', true); - dijit.setWaiState(newButton.focusNode, "selected", "true"); - this._currentChild = page; - newButton.focusNode.setAttribute("tabIndex", "0"); - var container = dijit.byId(this.containerId); - dijit.setWaiState(container.containerNode, "labelledby", newButton.id); - }, - - onButtonClick: function(/*dijit._Widget*/ page){ - // summary: - // Called whenever one of my child buttons is pressed in an attempt to select a page - // tags: - // private - - var container = dijit.byId(this.containerId); - container.selectChild(page); - }, - - onCloseButtonClick: function(/*dijit._Widget*/ page){ - // summary: - // Called whenever one of my child buttons [X] is pressed in an attempt to close a page - // tags: - // private - - var container = dijit.byId(this.containerId); - container.closeChild(page); - if(this._currentChild){ - var b = this.pane2button[this._currentChild.id]; - if(b){ - dijit.focus(b.focusNode || b.domNode); - } - } - }, - - // TODO: this is a bit redundant with forward, back api in StackContainer - adjacent: function(/*Boolean*/ forward){ - // summary: - // Helper for onkeypress to find next/previous button - // tags: - // private - - if(!this.isLeftToRight() && (!this.tabPosition || /top|bottom/.test(this.tabPosition))){ forward = !forward; } - // find currently focused button in children array - var children = this.getChildren(); - var current = dojo.indexOf(children, this.pane2button[this._currentChild.id]); - // pick next button to focus on - var offset = forward ? 1 : children.length - 1; - return children[ (current + offset) % children.length ]; // dijit._Widget - }, - - onkeypress: function(/*Event*/ e){ - // summary: - // Handle keystrokes on the page list, for advancing to next/previous button - // and closing the current page if the page is closable. - // tags: - // private - - if(this.disabled || e.altKey ){ return; } - var forward = null; - if(e.ctrlKey || !e._djpage){ - var k = dojo.keys; - switch(e.charOrCode){ - case k.LEFT_ARROW: - case k.UP_ARROW: - if(!e._djpage){ forward = false; } - break; - case k.PAGE_UP: - if(e.ctrlKey){ forward = false; } - break; - case k.RIGHT_ARROW: - case k.DOWN_ARROW: - if(!e._djpage){ forward = true; } - break; - case k.PAGE_DOWN: - if(e.ctrlKey){ forward = true; } - break; - case k.DELETE: - if(this._currentChild.closable){ - this.onCloseButtonClick(this._currentChild); - } - dojo.stopEvent(e); - break; - default: - if(e.ctrlKey){ - if(e.charOrCode === k.TAB){ - this.adjacent(!e.shiftKey).onClick(); - dojo.stopEvent(e); - }else if(e.charOrCode == "w"){ - if(this._currentChild.closable){ - this.onCloseButtonClick(this._currentChild); - } - dojo.stopEvent(e); // avoid browser tab closing. - } - } - } - // handle page navigation - if(forward !== null){ - this.adjacent(forward).onClick(); - dojo.stopEvent(e); - } - } - }, - - onContainerKeyPress: function(/*Object*/ info){ - // summary: - // Called when there was a keypress on the container - // tags: - // private - info.e._djpage = info.page; - this.onkeypress(info.e); - } - }); - - -dojo.declare("dijit.layout._StackButton", - dijit.form.ToggleButton, - { - // summary: - // Internal widget used by StackContainer. - // description: - // The button-like or tab-like object you click to select or delete a page - // tags: - // private - - // Override _FormWidget.tabIndex. - // StackContainer buttons are not in the tab order by default. - // Probably we should be calling this.startupKeyNavChildren() instead. - tabIndex: "-1", - - postCreate: function(/*Event*/ evt){ - dijit.setWaiRole((this.focusNode || this.domNode), "tab"); - this.inherited(arguments); - }, - - onClick: function(/*Event*/ evt){ - // summary: - // This is for TabContainer where the tabs are <span> rather than button, - // so need to set focus explicitly (on some browsers) - // Note that you shouldn't override this method, but you can connect to it. - dijit.focus(this.focusNode); - - // ... now let StackController catch the event and tell me what to do - }, - - onClickCloseButton: function(/*Event*/ evt){ - // summary: - // StackContainer connects to this function; if your widget contains a close button - // then clicking it should call this function. - // Note that you shouldn't override this method, but you can connect to it. - evt.stopPropagation(); - } - }); - - -} - -if(!dojo._hasResource["dijit.layout.StackContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.StackContainer"] = true; -dojo.provide("dijit.layout.StackContainer"); - - - - - - -dojo.declare( - "dijit.layout.StackContainer", - dijit.layout._LayoutWidget, - { - // summary: - // A container that has multiple children, but shows only - // one child at a time - // - // description: - // A container for widgets (ContentPanes, for example) That displays - // only one Widget at a time. - // - // Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild - // - // Can be base class for container, Wizard, Show, etc. - - // doLayout: Boolean - // If true, change the size of my currently displayed child to match my size - doLayout: true, - - // persist: Boolean - // Remembers the selected child across sessions - persist: false, - - baseClass: "dijitStackContainer", - -/*===== - // selectedChildWidget: [readonly] dijit._Widget - // References the currently selected child widget, if any. - // Adjust selected child with selectChild() method. - selectedChildWidget: null, -=====*/ - - postCreate: function(){ - this.inherited(arguments); - dojo.addClass(this.domNode, "dijitLayoutContainer"); - dijit.setWaiRole(this.containerNode, "tabpanel"); - this.connect(this.domNode, "onkeypress", this._onKeyPress); - }, - - startup: function(){ - if(this._started){ return; } - - var children = this.getChildren(); - - // Setup each page panel to be initially hidden - dojo.forEach(children, this._setupChild, this); - - // Figure out which child to initially display, defaulting to first one - if(this.persist){ - this.selectedChildWidget = dijit.byId(dojo.cookie(this.id + "_selectedChild")); - }else{ - dojo.some(children, function(child){ - if(child.selected){ - this.selectedChildWidget = child; - } - return child.selected; - }, this); - } - var selected = this.selectedChildWidget; - if(!selected && children[0]){ - selected = this.selectedChildWidget = children[0]; - selected.selected = true; - } - - // Publish information about myself so any StackControllers can initialize. - // This needs to happen before this.inherited(arguments) so that for - // TabContainer, this._contentBox doesn't include the space for the tab labels. - dojo.publish(this.id+"-startup", [{children: children, selected: selected}]); - - // Startup each child widget, and do initial layout like setting this._contentBox, - // then calls this.resize() which does the initial sizing on the selected child. - this.inherited(arguments); - }, - - resize: function(){ - // Resize is called when we are first made visible (it's called from startup() - // if we are initially visible). If this is the first time we've been made - // visible then show our first child. - var selected = this.selectedChildWidget; - if(selected && !this._hasBeenShown){ - this._hasBeenShown = true; - this._showChild(selected); - } - this.inherited(arguments); - }, - - _setupChild: function(/*dijit._Widget*/ child){ - // Overrides _LayoutWidget._setupChild() - - this.inherited(arguments); - - dojo.removeClass(child.domNode, "dijitVisible"); - dojo.addClass(child.domNode, "dijitHidden"); - - // remove the title attribute so it doesn't show up when i hover - // over a node - child.domNode.title = ""; - }, - - addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){ - // Overrides _Container.addChild() to do layout and publish events - - this.inherited(arguments); - - if(this._started){ - dojo.publish(this.id+"-addChild", [child, insertIndex]); - - // in case the tab titles have overflowed from one line to two lines - // (or, if this if first child, from zero lines to one line) - // TODO: w/ScrollingTabController this is no longer necessary, although - // ScrollTabController.resize() does need to get called to show/hide - // the navigation buttons as appropriate, but that's handled in ScrollingTabController.onAddChild() - this.layout(); - - // if this is the first child, then select it - if(!this.selectedChildWidget){ - this.selectChild(child); - } - } - }, - - removeChild: function(/*dijit._Widget*/ page){ - // Overrides _Container.removeChild() to do layout and publish events - - this.inherited(arguments); - - if(this._started){ - // this will notify any tablists to remove a button; do this first because it may affect sizing - dojo.publish(this.id + "-removeChild", [page]); - } - - // If we are being destroyed than don't run the code below (to select another page), because we are deleting - // every page one by one - if(this._beingDestroyed){ return; } - - // Select new page to display, also updating TabController to show the respective tab. - // Do this before layout call because it can affect the height of the TabController. - if(this.selectedChildWidget === page){ - this.selectedChildWidget = undefined; - if(this._started){ - var children = this.getChildren(); - if(children.length){ - this.selectChild(children[0]); - } - } - } - - if(this._started){ - // In case the tab titles now take up one line instead of two lines - // (note though that ScrollingTabController never overflows to multiple lines), - // or the height has changed slightly because of addition/removal of tab which close icon - this.layout(); - } - }, - - selectChild: function(/*dijit._Widget|String*/ page, /*Boolean*/ animate){ - // summary: - // Show the given widget (which must be one of my children) - // page: - // Reference to child widget or id of child widget - - page = dijit.byId(page); - - if(this.selectedChildWidget != page){ - // Deselect old page and select new one - this._transition(page, this.selectedChildWidget, animate); - this.selectedChildWidget = page; - dojo.publish(this.id+"-selectChild", [page]); - - if(this.persist){ - dojo.cookie(this.id + "_selectedChild", this.selectedChildWidget.id); - } - } - }, - - _transition: function(/*dijit._Widget*/newWidget, /*dijit._Widget*/oldWidget){ - // summary: - // Hide the old widget and display the new widget. - // Subclasses should override this. - // tags: - // protected extension - if(oldWidget){ - this._hideChild(oldWidget); - } - this._showChild(newWidget); - - // Size the new widget, in case this is the first time it's being shown, - // or I have been resized since the last time it was shown. - // Note that page must be visible for resizing to work. - if(newWidget.resize){ - if(this.doLayout){ - newWidget.resize(this._containerContentBox || this._contentBox); - }else{ - // the child should pick it's own size but we still need to call resize() - // (with no arguments) to let the widget lay itself out - newWidget.resize(); - } - } - }, - - _adjacent: function(/*Boolean*/ forward){ - // summary: - // Gets the next/previous child widget in this container from the current selection. - var children = this.getChildren(); - var index = dojo.indexOf(children, this.selectedChildWidget); - index += forward ? 1 : children.length - 1; - return children[ index % children.length ]; // dijit._Widget - }, - - forward: function(){ - // summary: - // Advance to next page. - this.selectChild(this._adjacent(true), true); - }, - - back: function(){ - // summary: - // Go back to previous page. - this.selectChild(this._adjacent(false), true); - }, - - _onKeyPress: function(e){ - dojo.publish(this.id+"-containerKeyPress", [{ e: e, page: this}]); - }, - - layout: function(){ - // Implement _LayoutWidget.layout() virtual method. - if(this.doLayout && this.selectedChildWidget && this.selectedChildWidget.resize){ - this.selectedChildWidget.resize(this._containerContentBox || this._contentBox); - } - }, - - _showChild: function(/*dijit._Widget*/ page){ - // summary: - // Show the specified child by changing it's CSS, and call _onShow()/onShow() so - // it can do any updates it needs regarding loading href's etc. - var children = this.getChildren(); - page.isFirstChild = (page == children[0]); - page.isLastChild = (page == children[children.length-1]); - page.selected = true; - - dojo.removeClass(page.domNode, "dijitHidden"); - dojo.addClass(page.domNode, "dijitVisible"); - - page._onShow(); - }, - - _hideChild: function(/*dijit._Widget*/ page){ - // summary: - // Hide the specified child by changing it's CSS, and call _onHide() so - // it's notified. - page.selected=false; - dojo.removeClass(page.domNode, "dijitVisible"); - dojo.addClass(page.domNode, "dijitHidden"); - - page.onHide(); - }, - - closeChild: function(/*dijit._Widget*/ page){ - // summary: - // Callback when user clicks the [X] to remove a page. - // If onClose() returns true then remove and destroy the child. - // tags: - // private - var remove = page.onClose(this, page); - if(remove){ - this.removeChild(page); - // makes sure we can clean up executeScripts in ContentPane onUnLoad - page.destroyRecursive(); - } - }, - - destroyDescendants: function(/*Boolean*/preserveDom){ - dojo.forEach(this.getChildren(), function(child){ - this.removeChild(child); - child.destroyRecursive(preserveDom); - }, this); - } -}); - -// For back-compat, remove for 2.0 - - - -// These arguments can be specified for the children of a StackContainer. -// Since any widget can be specified as a StackContainer child, mix them -// into the base widget class. (This is a hack, but it's effective.) -dojo.extend(dijit._Widget, { - // selected: Boolean - // Parameter for children of `dijit.layout.StackContainer` or subclasses. - // Specifies that this widget should be the initially displayed pane. - // Note: to change the selected child use `dijit.layout.StackContainer.selectChild` - selected: false, - - // closable: Boolean - // Parameter for children of `dijit.layout.StackContainer` or subclasses. - // True if user can close (destroy) this child, such as (for example) clicking the X on the tab. - closable: false, - - // iconClass: String - // Parameter for children of `dijit.layout.StackContainer` or subclasses. - // CSS Class specifying icon to use in label associated with this pane. - iconClass: "", - - // showTitle: Boolean - // Parameter for children of `dijit.layout.StackContainer` or subclasses. - // When true, display title of this widget as tab label etc., rather than just using - // icon specified in iconClass - showTitle: true -}); - -} - -if(!dojo._hasResource["dijit.layout.AccordionPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.AccordionPane"] = true; -dojo.provide("dijit.layout.AccordionPane"); - - - -dojo.declare("dijit.layout.AccordionPane", dijit.layout.ContentPane, { - // summary: - // Deprecated widget. Use `dijit.layout.ContentPane` instead. - // tags: - // deprecated - - constructor: function(){ - dojo.deprecated("dijit.layout.AccordionPane deprecated, use ContentPane instead", "", "2.0"); - }, - - onSelected: function(){ - // summary: - // called when this pane is selected - } -}); - -} - -if(!dojo._hasResource["dijit.layout.AccordionContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.AccordionContainer"] = true; -dojo.provide("dijit.layout.AccordionContainer"); - - - - - - - - - - // for back compat, remove for 2.0 - -dojo.declare( - "dijit.layout.AccordionContainer", - dijit.layout.StackContainer, - { - // summary: - // Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time, - // and switching between panes is visualized by sliding the other panes up/down. - // example: - // | <div dojoType="dijit.layout.AccordionContainer"> - // | <div dojoType="dijit.layout.ContentPane" title="pane 1"> - // | </div> - // | <div dojoType="dijit.layout.ContentPane" title="pane 2"> - // | <p>This is some text</p> - // | </div> - // | </div> - - // duration: Integer - // Amount of time (in ms) it takes to slide panes - duration: dijit.defaultDuration, - - // buttonWidget: [const] String - // The name of the widget used to display the title of each pane - buttonWidget: "dijit.layout._AccordionButton", - - // _verticalSpace: Number - // Pixels of space available for the open pane - // (my content box size minus the cumulative size of all the title bars) - _verticalSpace: 0, - - baseClass: "dijitAccordionContainer", - - postCreate: function(){ - this.domNode.style.overflow = "hidden"; - this.inherited(arguments); - dijit.setWaiRole(this.domNode, "tablist"); - }, - - startup: function(){ - if(this._started){ return; } - this.inherited(arguments); - if(this.selectedChildWidget){ - var style = this.selectedChildWidget.containerNode.style; - style.display = ""; - style.overflow = "auto"; - this.selectedChildWidget._wrapperWidget.set("selected", true); - } - }, - - _getTargetHeight: function(/* Node */ node){ - // summary: - // For the given node, returns the height that should be - // set to achieve our vertical space (subtract any padding - // we may have). - // - // This is used by the animations. - // - // TODO: I don't think this works correctly in IE quirks when an elements - // style.height including padding and borders - var cs = dojo.getComputedStyle(node); - return Math.max(this._verticalSpace - dojo._getPadBorderExtents(node, cs).h - dojo._getMarginExtents(node, cs).h, 0); - }, - - layout: function(){ - // Implement _LayoutWidget.layout() virtual method. - // Set the height of the open pane based on what room remains. - - var openPane = this.selectedChildWidget; - - if(!openPane){ return;} - - var openPaneContainer = openPane._wrapperWidget.domNode, - openPaneContainerMargin = dojo._getMarginExtents(openPaneContainer), - openPaneContainerPadBorder = dojo._getPadBorderExtents(openPaneContainer), - mySize = this._contentBox; - - // get cumulative height of all the unselected title bars - var totalCollapsedHeight = 0; - dojo.forEach(this.getChildren(), function(child){ - if(child != openPane){ - totalCollapsedHeight += dojo.marginBox(child._wrapperWidget.domNode).h; - } - }); - this._verticalSpace = mySize.h - totalCollapsedHeight - openPaneContainerMargin.h - - openPaneContainerPadBorder.h - openPane._buttonWidget.getTitleHeight(); - - // Memo size to make displayed child - this._containerContentBox = { - h: this._verticalSpace, - w: this._contentBox.w - openPaneContainerMargin.w - openPaneContainerPadBorder.w - }; - - if(openPane){ - openPane.resize(this._containerContentBox); - } - }, - - _setupChild: function(child){ - // Overrides _LayoutWidget._setupChild(). - // Put wrapper widget around the child widget, showing title - - child._wrapperWidget = new dijit.layout._AccordionInnerContainer({ - contentWidget: child, - buttonWidget: this.buttonWidget, - id: child.id + "_wrapper", - dir: child.dir, - lang: child.lang, - parent: this - }); - - this.inherited(arguments); - }, - - addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){ - if(this._started){ - // Adding a child to a started Accordion is complicated because children have - // wrapper widgets. Default code path (calling this.inherited()) would add - // the new child inside another child's wrapper. - - // First add in child as a direct child of this AccordionContainer - dojo.place(child.domNode, this.containerNode, insertIndex); - - if(!child._started){ - child.startup(); - } - - // Then stick the wrapper widget around the child widget - this._setupChild(child); - - // Code below copied from StackContainer - dojo.publish(this.id+"-addChild", [child, insertIndex]); - this.layout(); - if(!this.selectedChildWidget){ - this.selectChild(child); - } - }else{ - // We haven't been started yet so just add in the child widget directly, - // and the wrapper will be created on startup() - this.inherited(arguments); - } - }, - - removeChild: function(child){ - // Overrides _LayoutWidget.removeChild(). - - // destroy wrapper widget first, before StackContainer.getChildren() call - child._wrapperWidget.destroy(); - delete child._wrapperWidget; - dojo.removeClass(child.domNode, "dijitHidden"); - - this.inherited(arguments); - }, - - getChildren: function(){ - // Overrides _Container.getChildren() to return content panes rather than internal AccordionInnerContainer panes - return dojo.map(this.inherited(arguments), function(child){ - return child.declaredClass == "dijit.layout._AccordionInnerContainer" ? child.contentWidget : child; - }, this); - }, - - destroy: function(){ - dojo.forEach(this.getChildren(), function(child){ - child._wrapperWidget.destroy(); - }); - this.inherited(arguments); - }, - - _transition: function(/*dijit._Widget?*/newWidget, /*dijit._Widget?*/oldWidget, /*Boolean*/ animate){ - // Overrides StackContainer._transition() to provide sliding of title bars etc. - -//TODO: should be able to replace this with calls to slideIn/slideOut - if(this._inTransition){ return; } - var animations = []; - var paneHeight = this._verticalSpace; - if(newWidget){ - newWidget._wrapperWidget.set("selected", true); - - this._showChild(newWidget); // prepare widget to be slid in - - // Size the new widget, in case this is the first time it's being shown, - // or I have been resized since the last time it was shown. - // Note that page must be visible for resizing to work. - if(this.doLayout && newWidget.resize){ - newWidget.resize(this._containerContentBox); - } - - var newContents = newWidget.domNode; - dojo.addClass(newContents, "dijitVisible"); - dojo.removeClass(newContents, "dijitHidden"); - - if(animate){ - var newContentsOverflow = newContents.style.overflow; - newContents.style.overflow = "hidden"; - animations.push(dojo.animateProperty({ - node: newContents, - duration: this.duration, - properties: { - height: { start: 1, end: this._getTargetHeight(newContents) } - }, - onEnd: function(){ - newContents.style.overflow = newContentsOverflow; - - // Kick IE to workaround layout bug, see #11415 - if(dojo.isIE){ - setTimeout(function(){ - dojo.removeClass(newContents.parentNode, "dijitAccordionInnerContainerFocused"); - setTimeout(function(){ - dojo.addClass(newContents.parentNode, "dijitAccordionInnerContainerFocused"); - }, 0); - }, 0); - } - } - })); - } - } - if(oldWidget){ - oldWidget._wrapperWidget.set("selected", false); - var oldContents = oldWidget.domNode; - if(animate){ - var oldContentsOverflow = oldContents.style.overflow; - oldContents.style.overflow = "hidden"; - animations.push(dojo.animateProperty({ - node: oldContents, - duration: this.duration, - properties: { - height: { start: this._getTargetHeight(oldContents), end: 1 } - }, - onEnd: function(){ - dojo.addClass(oldContents, "dijitHidden"); - dojo.removeClass(oldContents, "dijitVisible"); - oldContents.style.overflow = oldContentsOverflow; - if(oldWidget.onHide){ - oldWidget.onHide(); - } - } - })); - }else{ - dojo.addClass(oldContents, "dijitHidden"); - dojo.removeClass(oldContents, "dijitVisible"); - if(oldWidget.onHide){ - oldWidget.onHide(); - } - } - } - - if(animate){ - this._inTransition = true; - var combined = dojo.fx.combine(animations); - combined.onEnd = dojo.hitch(this, function(){ - delete this._inTransition; - }); - combined.play(); - } - }, - - // note: we are treating the container as controller here - _onKeyPress: function(/*Event*/ e, /*dijit._Widget*/ fromTitle){ - // summary: - // Handle keypress events - // description: - // This is called from a handler on AccordionContainer.domNode - // (setup in StackContainer), and is also called directly from - // the click handler for accordion labels - if(this._inTransition || this.disabled || e.altKey || !(fromTitle || e.ctrlKey)){ - if(this._inTransition){ - dojo.stopEvent(e); - } - return; - } - var k = dojo.keys, - c = e.charOrCode; - if((fromTitle && (c == k.LEFT_ARROW || c == k.UP_ARROW)) || - (e.ctrlKey && c == k.PAGE_UP)){ - this._adjacent(false)._buttonWidget._onTitleClick(); - dojo.stopEvent(e); - }else if((fromTitle && (c == k.RIGHT_ARROW || c == k.DOWN_ARROW)) || - (e.ctrlKey && (c == k.PAGE_DOWN || c == k.TAB))){ - this._adjacent(true)._buttonWidget._onTitleClick(); - dojo.stopEvent(e); - } - } - } -); - -dojo.declare("dijit.layout._AccordionInnerContainer", - [dijit._Widget, dijit._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: String - // Name of 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 - isContainer: true, - isLayoutContainer: true, - - buildRendering: function(){ - // Create wrapper div, placed where the child is now - this.domNode = dojo.place("<div class='" + this.baseClass + "'>", this.contentWidget.domNode, "after"); - - // wrapper div's first child is the button widget (ie, the title bar) - var child = this.contentWidget, - cls = dojo.getObject(this.buttonWidget); - this.button = child._buttonWidget = (new cls({ - contentWidget: child, - label: child.title, - title: child.tooltip, - dir: child.dir, - lang: child.lang, - iconClass: child.iconClass, - id: child.id + "_button", - parent: this.parent - })).placeAt(this.domNode); - - // and then the actual content widget (changing it from prior-sibling to last-child) - dojo.place(this.contentWidget.domNode, this.domNode); - }, - - postCreate: function(){ - this.inherited(arguments); - this.connect(this.contentWidget, 'set', function(name, value){ - var mappedName = {title: "label", tooltip: "title", iconClass: "iconClass"}[name]; - if(mappedName){ - this.button.set(mappedName, value); - } - }, this); - }, - - _setSelectedAttr: function(/*Boolean*/ isSelected){ - this.selected = isSelected; - this.button.set("selected", isSelected); - if(isSelected){ - var cw = this.contentWidget; - if(cw.onSelected){ cw.onSelected(); } - } - }, - - startup: function(){ - // Called by _Container.addChild() - this.contentWidget.startup(); - }, - - destroy: function(){ - this.button.destroyRecursive(); - - delete this.contentWidget._buttonWidget; - delete this.contentWidget._wrapperWidget; - - this.inherited(arguments); - }, - - destroyDescendants: function(){ - // since getChildren isn't working for me, have to code this manually - this.contentWidget.destroyRecursive(); - } -}); - -dojo.declare("dijit.layout._AccordionButton", - [dijit._Widget, dijit._Templated, dijit._CssStateMixin], - { - // summary: - // The title bar to click to open up an accordion pane. - // Internal widget used by AccordionContainer. - // tags: - // private - - templateString: dojo.cache("dijit.layout", "templates/AccordionButton.html", "<div dojoAttachEvent='onclick:_onTitleClick' class='dijitAccordionTitle'>\n\t<div dojoAttachPoint='titleNode,focusNode' dojoAttachEvent='onkeypress:_onTitleKeyPress'\n\t\t\tclass='dijitAccordionTitleFocus' wairole=\"tab\" waiState=\"expanded-false\"\n\t\t><span class='dijitInline dijitAccordionArrow' waiRole=\"presentation\"></span\n\t\t><span class='arrowTextUp' waiRole=\"presentation\">+</span\n\t\t><span class='arrowTextDown' waiRole=\"presentation\">-</span\n\t\t><img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon\" dojoAttachPoint='iconNode' style=\"vertical-align: middle\" waiRole=\"presentation\"/>\n\t\t<span waiRole=\"presentation\" dojoAttachPoint='titleTextNode' class='dijitAccordionText'></span>\n\t</div>\n</div>\n"), - attributeMap: dojo.mixin(dojo.clone(dijit.layout.ContentPane.prototype.attributeMap), { - label: {node: "titleTextNode", type: "innerHTML" }, - title: {node: "titleTextNode", type: "attribute", attribute: "title"}, - iconClass: { node: "iconNode", type: "class" } - }), - - baseClass: "dijitAccordionTitle", - - getParent: function(){ - // summary: - // Returns the AccordionContainer parent. - // tags: - // private - return this.parent; - }, - - postCreate: function(){ - this.inherited(arguments); - dojo.setSelectable(this.domNode, false); - var titleTextNodeId = dojo.attr(this.domNode,'id').replace(' ','_'); - dojo.attr(this.titleTextNode, "id", titleTextNodeId+"_title"); - dijit.setWaiState(this.focusNode, "labelledby", dojo.attr(this.titleTextNode, "id")); - }, - - getTitleHeight: function(){ - // summary: - // Returns the height of the title dom node. - return dojo.marginBox(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(); - if(!parent._inTransition){ - parent.selectChild(this.contentWidget, true); - dijit.focus(this.focusNode); - } - }, - - _onTitleKeyPress: function(/*Event*/ evt){ - return this.getParent()._onKeyPress(evt, this.contentWidget); - }, - - _setSelectedAttr: function(/*Boolean*/ isSelected){ - this.selected = isSelected; - dijit.setWaiState(this.focusNode, "expanded", isSelected); - dijit.setWaiState(this.focusNode, "selected", isSelected); - this.focusNode.setAttribute("tabIndex", isSelected ? "0" : "-1"); - } -}); - -} - -if(!dojo._hasResource["dijit.layout.BorderContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.BorderContainer"] = true; -dojo.provide("dijit.layout.BorderContainer"); - - - - -dojo.declare( - "dijit.layout.BorderContainer", - dijit.layout._LayoutWidget, -{ - // summary: - // Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides. - // - // description: - // A BorderContainer is a box with a specified size, such as style="width: 500px; height: 500px;", - // that contains a child widget marked region="center" and optionally children widgets marked - // region equal to "top", "bottom", "leading", "trailing", "left" or "right". - // Children along the edges will be laid out according to width or height dimensions and may - // include optional splitters (splitter="true") to make them resizable by the user. The remaining - // space is designated for the center region. - // - // NOTE: Splitters must not be more than 50 pixels in width. - // - // The outer size must be specified on the BorderContainer node. Width must be specified for the sides - // and height for the top and bottom, respectively. No dimensions should be specified on the center; - // it will fill the remaining space. Regions named "leading" and "trailing" may be used just like - // "left" and "right" except that they will be reversed in right-to-left environments. - // - // example: - // | <div dojoType="dijit.layout.BorderContainer" design="sidebar" gutters="false" - // | style="width: 400px; height: 300px;"> - // | <div dojoType="ContentPane" region="top">header text</div> - // | <div dojoType="ContentPane" region="right" splitter="true" style="width: 200px;">table of contents</div> - // | <div dojoType="ContentPane" region="center">client area</div> - // | </div> - - // design: String - // Which design is used for the layout: - // - "headline" (default) where the top and bottom extend - // the full width of the container - // - "sidebar" where the left and right sides extend from top to bottom. - design: "headline", - - // gutters: Boolean - // Give each pane a border and margin. - // Margin determined by domNode.paddingLeft. - // When false, only resizable panes have a gutter (i.e. draggable splitter) for resizing. - gutters: true, - - // liveSplitters: Boolean - // Specifies whether splitters resize as you drag (true) or only upon mouseup (false) - liveSplitters: true, - - // persist: Boolean - // Save splitter positions in a cookie. - persist: false, - - baseClass: "dijitBorderContainer", - - // _splitterClass: String - // Optional hook to override the default Splitter widget used by BorderContainer - _splitterClass: "dijit.layout._Splitter", - - postMixInProperties: function(){ - // change class name to indicate that BorderContainer is being used purely for - // layout (like LayoutContainer) rather than for pretty formatting. - if(!this.gutters){ - this.baseClass += "NoGutter"; - } - this.inherited(arguments); - }, - - postCreate: function(){ - this.inherited(arguments); - - this._splitters = {}; - this._splitterThickness = {}; - }, - - startup: function(){ - if(this._started){ return; } - dojo.forEach(this.getChildren(), this._setupChild, this); - this.inherited(arguments); - }, - - _setupChild: function(/*dijit._Widget*/ child){ - // Override _LayoutWidget._setupChild(). - - var region = child.region; - if(region){ - this.inherited(arguments); - - dojo.addClass(child.domNode, this.baseClass+"Pane"); - - var ltr = this.isLeftToRight(); - if(region == "leading"){ region = ltr ? "left" : "right"; } - if(region == "trailing"){ region = ltr ? "right" : "left"; } - - //FIXME: redundant? - this["_"+region] = child.domNode; - this["_"+region+"Widget"] = child; - - // Create draggable splitter for resizing pane, - // or alternately if splitter=false but BorderContainer.gutters=true then - // insert dummy div just for spacing - if((child.splitter || this.gutters) && !this._splitters[region]){ - var _Splitter = dojo.getObject(child.splitter ? this._splitterClass : "dijit.layout._Gutter"); - var splitter = new _Splitter({ - id: child.id + "_splitter", - container: this, - child: child, - region: region, - live: this.liveSplitters - }); - splitter.isSplitter = true; - this._splitters[region] = splitter.domNode; - dojo.place(this._splitters[region], child.domNode, "after"); - - // Splitters arent added as Contained children, so we need to call startup explicitly - splitter.startup(); - } - child.region = region; - } - }, - - _computeSplitterThickness: function(region){ - this._splitterThickness[region] = this._splitterThickness[region] || - dojo.marginBox(this._splitters[region])[(/top|bottom/.test(region) ? 'h' : 'w')]; - }, - - layout: function(){ - // Implement _LayoutWidget.layout() virtual method. - for(var region in this._splitters){ this._computeSplitterThickness(region); } - this._layoutChildren(); - }, - - addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){ - // Override _LayoutWidget.addChild(). - this.inherited(arguments); - if(this._started){ - this.layout(); //OPT - } - }, - - removeChild: function(/*dijit._Widget*/ child){ - // Override _LayoutWidget.removeChild(). - var region = child.region; - var splitter = this._splitters[region]; - if(splitter){ - dijit.byNode(splitter).destroy(); - delete this._splitters[region]; - delete this._splitterThickness[region]; - } - this.inherited(arguments); - delete this["_"+region]; - delete this["_" +region+"Widget"]; - if(this._started){ - this._layoutChildren(); - } - dojo.removeClass(child.domNode, this.baseClass+"Pane"); - }, - - getChildren: function(){ - // Override _LayoutWidget.getChildren() to only return real children, not the splitters. - return dojo.filter(this.inherited(arguments), function(widget){ - return !widget.isSplitter; - }); - }, - - getSplitter: function(/*String*/region){ - // summary: - // Returns the widget responsible for rendering the splitter associated with region - var splitter = this._splitters[region]; - return splitter ? dijit.byNode(splitter) : null; - }, - - resize: function(newSize, currentSize){ - // Overrides _LayoutWidget.resize(). - - // resetting potential padding to 0px to provide support for 100% width/height + padding - // TODO: this hack doesn't respect the box model and is a temporary fix - if(!this.cs || !this.pe){ - var node = this.domNode; - this.cs = dojo.getComputedStyle(node); - this.pe = dojo._getPadExtents(node, this.cs); - this.pe.r = dojo._toPixelValue(node, this.cs.paddingRight); - this.pe.b = dojo._toPixelValue(node, this.cs.paddingBottom); - - dojo.style(node, "padding", "0px"); - } - - this.inherited(arguments); - }, - - _layoutChildren: function(/*String?*/changedRegion, /*Number?*/ changedRegionSize){ - // summary: - // This is the main routine for setting size/position of each child. - // description: - // With no arguments, measures the height of top/bottom panes, the width - // of left/right panes, and then sizes all panes accordingly. - // - // With changedRegion specified (as "left", "top", "bottom", or "right"), - // it changes that region's width/height to changedRegionSize and - // then resizes other regions that were affected. - // changedRegion: - // The region should be changed because splitter was dragged. - // "left", "right", "top", or "bottom". - // changedRegionSize: - // The new width/height (in pixels) to make changedRegion - - if(!this._borderBox || !this._borderBox.h){ - // We are currently hidden, or we haven't been sized by our parent yet. - // Abort. Someone will resize us later. - return; - } - - var sidebarLayout = (this.design == "sidebar"); - var topHeight = 0, bottomHeight = 0, leftWidth = 0, rightWidth = 0; - var topStyle = {}, leftStyle = {}, rightStyle = {}, bottomStyle = {}, - centerStyle = (this._center && this._center.style) || {}; - - var changedSide = /left|right/.test(changedRegion); - - var layoutSides = !changedRegion || (!changedSide && !sidebarLayout); - var layoutTopBottom = !changedRegion || (changedSide && sidebarLayout); - - // Ask browser for width/height of side panes. - // Would be nice to cache this but height can change according to width - // (because words wrap around). I don't think width will ever change though - // (except when the user drags a splitter). - if(this._top){ - topStyle = (changedRegion == "top" || layoutTopBottom) && this._top.style; - topHeight = changedRegion == "top" ? changedRegionSize : dojo.marginBox(this._top).h; - } - if(this._left){ - leftStyle = (changedRegion == "left" || layoutSides) && this._left.style; - leftWidth = changedRegion == "left" ? changedRegionSize : dojo.marginBox(this._left).w; - } - if(this._right){ - rightStyle = (changedRegion == "right" || layoutSides) && this._right.style; - rightWidth = changedRegion == "right" ? changedRegionSize : dojo.marginBox(this._right).w; - } - if(this._bottom){ - bottomStyle = (changedRegion == "bottom" || layoutTopBottom) && this._bottom.style; - bottomHeight = changedRegion == "bottom" ? changedRegionSize : dojo.marginBox(this._bottom).h; - } - - var splitters = this._splitters; - var topSplitter = splitters.top, bottomSplitter = splitters.bottom, - leftSplitter = splitters.left, rightSplitter = splitters.right; - var splitterThickness = this._splitterThickness; - var topSplitterThickness = splitterThickness.top || 0, - leftSplitterThickness = splitterThickness.left || 0, - rightSplitterThickness = splitterThickness.right || 0, - bottomSplitterThickness = splitterThickness.bottom || 0; - - // Check for race condition where CSS hasn't finished loading, so - // the splitter width == the viewport width (#5824) - if(leftSplitterThickness > 50 || rightSplitterThickness > 50){ - setTimeout(dojo.hitch(this, function(){ - // Results are invalid. Clear them out. - this._splitterThickness = {}; - - for(var region in this._splitters){ - this._computeSplitterThickness(region); - } - this._layoutChildren(); - }), 50); - return false; - } - - var pe = this.pe; - - var splitterBounds = { - left: (sidebarLayout ? leftWidth + leftSplitterThickness: 0) + pe.l + "px", - right: (sidebarLayout ? rightWidth + rightSplitterThickness: 0) + pe.r + "px" - }; - - if(topSplitter){ - dojo.mixin(topSplitter.style, splitterBounds); - topSplitter.style.top = topHeight + pe.t + "px"; - } - - if(bottomSplitter){ - dojo.mixin(bottomSplitter.style, splitterBounds); - bottomSplitter.style.bottom = bottomHeight + pe.b + "px"; - } - - splitterBounds = { - top: (sidebarLayout ? 0 : topHeight + topSplitterThickness) + pe.t + "px", - bottom: (sidebarLayout ? 0 : bottomHeight + bottomSplitterThickness) + pe.b + "px" - }; - - if(leftSplitter){ - dojo.mixin(leftSplitter.style, splitterBounds); - leftSplitter.style.left = leftWidth + pe.l + "px"; - } - - if(rightSplitter){ - dojo.mixin(rightSplitter.style, splitterBounds); - rightSplitter.style.right = rightWidth + pe.r + "px"; - } - - dojo.mixin(centerStyle, { - top: pe.t + topHeight + topSplitterThickness + "px", - left: pe.l + leftWidth + leftSplitterThickness + "px", - right: pe.r + rightWidth + rightSplitterThickness + "px", - bottom: pe.b + bottomHeight + bottomSplitterThickness + "px" - }); - - var bounds = { - top: sidebarLayout ? pe.t + "px" : centerStyle.top, - bottom: sidebarLayout ? pe.b + "px" : centerStyle.bottom - }; - dojo.mixin(leftStyle, bounds); - dojo.mixin(rightStyle, bounds); - leftStyle.left = pe.l + "px"; rightStyle.right = pe.r + "px"; topStyle.top = pe.t + "px"; bottomStyle.bottom = pe.b + "px"; - if(sidebarLayout){ - topStyle.left = bottomStyle.left = leftWidth + leftSplitterThickness + pe.l + "px"; - topStyle.right = bottomStyle.right = rightWidth + rightSplitterThickness + pe.r + "px"; - }else{ - topStyle.left = bottomStyle.left = pe.l + "px"; - topStyle.right = bottomStyle.right = pe.r + "px"; - } - - // More calculations about sizes of panes - var containerHeight = this._borderBox.h - pe.t - pe.b, - middleHeight = containerHeight - ( topHeight + topSplitterThickness + bottomHeight + bottomSplitterThickness), - sidebarHeight = sidebarLayout ? containerHeight : middleHeight; - - var containerWidth = this._borderBox.w - pe.l - pe.r, - middleWidth = containerWidth - (leftWidth + leftSplitterThickness + rightWidth + rightSplitterThickness), - sidebarWidth = sidebarLayout ? middleWidth : containerWidth; - - // New margin-box size of each pane - var dim = { - top: { w: sidebarWidth, h: topHeight }, - bottom: { w: sidebarWidth, h: bottomHeight }, - left: { w: leftWidth, h: sidebarHeight }, - right: { w: rightWidth, h: sidebarHeight }, - center: { h: middleHeight, w: middleWidth } - }; - - if(changedRegion){ - // Respond to splitter drag event by changing changedRegion's width or height - var child = this["_" + changedRegion + "Widget"], - mb = {}; - mb[ /top|bottom/.test(changedRegion) ? "h" : "w"] = changedRegionSize; - child.resize ? child.resize(mb, dim[child.region]) : dojo.marginBox(child.domNode, mb); - } - - // Nodes in IE<8 don't respond to t/l/b/r, and TEXTAREA doesn't respond in any browser - var janky = dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.some(this.getChildren(), function(child){ - return child.domNode.tagName == "TEXTAREA" || child.domNode.tagName == "INPUT"; - }); - if(janky){ - // Set the size of the children the old fashioned way, by setting - // CSS width and height - - var resizeWidget = function(widget, changes, result){ - if(widget){ - (widget.resize ? widget.resize(changes, result) : dojo.marginBox(widget.domNode, changes)); - } - }; - - if(leftSplitter){ leftSplitter.style.height = sidebarHeight; } - if(rightSplitter){ rightSplitter.style.height = sidebarHeight; } - resizeWidget(this._leftWidget, {h: sidebarHeight}, dim.left); - resizeWidget(this._rightWidget, {h: sidebarHeight}, dim.right); - - if(topSplitter){ topSplitter.style.width = sidebarWidth; } - if(bottomSplitter){ bottomSplitter.style.width = sidebarWidth; } - resizeWidget(this._topWidget, {w: sidebarWidth}, dim.top); - resizeWidget(this._bottomWidget, {w: sidebarWidth}, dim.bottom); - - resizeWidget(this._centerWidget, dim.center); - }else{ - // Calculate which panes need a notification that their size has been changed - // (we've already set style.top/bottom/left/right on those other panes). - var notifySides = !changedRegion || (/top|bottom/.test(changedRegion) && this.design != "sidebar"), - notifyTopBottom = !changedRegion || (/left|right/.test(changedRegion) && this.design == "sidebar"), - notifyList = { - center: true, - left: notifySides, - right: notifySides, - top: notifyTopBottom, - bottom: notifyTopBottom - }; - - // Send notification to those panes that have changed size - dojo.forEach(this.getChildren(), function(child){ - if(child.resize && notifyList[child.region]){ - child.resize(null, dim[child.region]); - } - }, this); - } - }, - - destroy: function(){ - for(var region in this._splitters){ - var splitter = this._splitters[region]; - dijit.byNode(splitter).destroy(); - dojo.destroy(splitter); - } - delete this._splitters; - delete this._splitterThickness; - this.inherited(arguments); - } -}); - -// This argument can be specified for the children of a BorderContainer. -// Since any widget can be specified as a LayoutContainer child, mix it -// into the base widget class. (This is a hack, but it's effective.) -dojo.extend(dijit._Widget, { - // region: [const] String - // Parameter for children of `dijit.layout.BorderContainer`. - // Values: "top", "bottom", "leading", "trailing", "left", "right", "center". - // See the `dijit.layout.BorderContainer` description for details. - region: '', - - // splitter: [const] Boolean - // Parameter for child of `dijit.layout.BorderContainer` where region != "center". - // If true, enables user to resize the widget by putting a draggable splitter between - // this widget and the region=center widget. - splitter: false, - - // minSize: [const] Number - // Parameter for children of `dijit.layout.BorderContainer`. - // Specifies a minimum size (in pixels) for this widget when resized by a splitter. - minSize: 0, - - // maxSize: [const] Number - // Parameter for children of `dijit.layout.BorderContainer`. - // Specifies a maximum size (in pixels) for this widget when resized by a splitter. - maxSize: Infinity -}); - - - -dojo.declare("dijit.layout._Splitter", [ dijit._Widget, dijit._Templated ], -{ - // summary: - // A draggable spacer between two items in a `dijit.layout.BorderContainer`. - // description: - // This is instantiated by `dijit.layout.BorderContainer`. Users should not - // create it directly. - // tags: - // private - -/*===== - // container: [const] dijit.layout.BorderContainer - // Pointer to the parent BorderContainer - container: null, - - // child: [const] dijit.layout._LayoutWidget - // Pointer to the pane associated with this splitter - child: null, - - // region: String - // Region of pane associated with this splitter. - // "top", "bottom", "left", "right". - region: null, -=====*/ - - // live: [const] Boolean - // If true, the child's size changes and the child widget is redrawn as you drag the splitter; - // otherwise, the size doesn't change until you drop the splitter (by mouse-up) - live: true, - - templateString: '<div class="dijitSplitter" dojoAttachEvent="onkeypress:_onKeyPress,onmousedown:_startDrag,onmouseenter:_onMouse,onmouseleave:_onMouse" tabIndex="0" waiRole="separator"><div class="dijitSplitterThumb"></div></div>', - - postCreate: function(){ - this.inherited(arguments); - this.horizontal = /top|bottom/.test(this.region); - dojo.addClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V")); -// dojo.addClass(this.child.domNode, "dijitSplitterPane"); -// dojo.setSelectable(this.domNode, false); //TODO is this necessary? - - this._factor = /top|left/.test(this.region) ? 1 : -1; - - this._cookieName = this.container.id + "_" + this.region; - if(this.container.persist){ - // restore old size - var persistSize = dojo.cookie(this._cookieName); - if(persistSize){ - this.child.domNode.style[this.horizontal ? "height" : "width"] = persistSize; - } - } - }, - - _computeMaxSize: function(){ - // summary: - // Compute the maximum size that my corresponding pane can be set to - - var dim = this.horizontal ? 'h' : 'w', - thickness = this.container._splitterThickness[this.region]; - - // Get DOMNode of opposite pane, if an opposite pane exists. - // Ex: if I am the _Splitter for the left pane, then get the right pane. - var flip = {left:'right', right:'left', top:'bottom', bottom:'top', leading:'trailing', trailing:'leading'}, - oppNode = this.container["_" + flip[this.region]]; - - // I can expand up to the edge of the opposite pane, or if there's no opposite pane, then to - // edge of BorderContainer - var available = dojo.contentBox(this.container.domNode)[dim] - - (oppNode ? dojo.marginBox(oppNode)[dim] : 0) - - 20 - thickness * 2; - - return Math.min(this.child.maxSize, available); - }, - - _startDrag: function(e){ - if(!this.cover){ - this.cover = dojo.doc.createElement('div'); - dojo.addClass(this.cover, "dijitSplitterCover"); - dojo.place(this.cover, this.child.domNode, "after"); - } - dojo.addClass(this.cover, "dijitSplitterCoverActive"); - - // Safeguard in case the stop event was missed. Shouldn't be necessary if we always get the mouse up. - if(this.fake){ dojo.destroy(this.fake); } - if(!(this._resize = this.live)){ //TODO: disable live for IE6? - // create fake splitter to display at old position while we drag - (this.fake = this.domNode.cloneNode(true)).removeAttribute("id"); - dojo.addClass(this.domNode, "dijitSplitterShadow"); - dojo.place(this.fake, this.domNode, "after"); - } - dojo.addClass(this.domNode, "dijitSplitterActive"); - dojo.addClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Active"); - if(this.fake){ - dojo.removeClass(this.fake, "dijitSplitterHover"); - dojo.removeClass(this.fake, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover"); - } - - //Performance: load data info local vars for onmousevent function closure - var factor = this._factor, - max = this._computeMaxSize(), - min = this.child.minSize || 20, - isHorizontal = this.horizontal, - axis = isHorizontal ? "pageY" : "pageX", - pageStart = e[axis], - splitterStyle = this.domNode.style, - dim = isHorizontal ? 'h' : 'w', - childStart = dojo.marginBox(this.child.domNode)[dim], - region = this.region, - splitterStart = parseInt(this.domNode.style[region], 10), - resize = this._resize, - childNode = this.child.domNode, - layoutFunc = dojo.hitch(this.container, this.container._layoutChildren), - de = dojo.doc; - - this._handlers = (this._handlers || []).concat([ - dojo.connect(de, "onmousemove", this._drag = function(e, forceResize){ - var delta = e[axis] - pageStart, - childSize = factor * delta + childStart, - boundChildSize = Math.max(Math.min(childSize, max), min); - - if(resize || forceResize){ - layoutFunc(region, boundChildSize); - } - splitterStyle[region] = factor * delta + splitterStart + (boundChildSize - childSize) + "px"; - }), - dojo.connect(de, "ondragstart", dojo.stopEvent), - dojo.connect(dojo.body(), "onselectstart", dojo.stopEvent), - dojo.connect(de, "onmouseup", this, "_stopDrag") - ]); - dojo.stopEvent(e); - }, - - _onMouse: function(e){ - var o = (e.type == "mouseover" || e.type == "mouseenter"); - dojo.toggleClass(this.domNode, "dijitSplitterHover", o); - dojo.toggleClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover", o); - }, - - _stopDrag: function(e){ - try{ - if(this.cover){ - dojo.removeClass(this.cover, "dijitSplitterCoverActive"); - } - if(this.fake){ dojo.destroy(this.fake); } - dojo.removeClass(this.domNode, "dijitSplitterActive"); - dojo.removeClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Active"); - dojo.removeClass(this.domNode, "dijitSplitterShadow"); - this._drag(e); //TODO: redundant with onmousemove? - this._drag(e, true); - }finally{ - this._cleanupHandlers(); - delete this._drag; - } - - if(this.container.persist){ - dojo.cookie(this._cookieName, this.child.domNode.style[this.horizontal ? "height" : "width"], {expires:365}); - } - }, - - _cleanupHandlers: function(){ - dojo.forEach(this._handlers, dojo.disconnect); - delete this._handlers; - }, - - _onKeyPress: function(/*Event*/ e){ - // should we apply typematic to this? - this._resize = true; - var horizontal = this.horizontal; - var tick = 1; - var dk = dojo.keys; - switch(e.charOrCode){ - case horizontal ? dk.UP_ARROW : dk.LEFT_ARROW: - tick *= -1; -// break; - case horizontal ? dk.DOWN_ARROW : dk.RIGHT_ARROW: - break; - default: -// this.inherited(arguments); - return; - } - var childSize = dojo.marginBox(this.child.domNode)[ horizontal ? 'h' : 'w' ] + this._factor * tick; - this.container._layoutChildren(this.region, Math.max(Math.min(childSize, this._computeMaxSize()), this.child.minSize)); - dojo.stopEvent(e); - }, - - destroy: function(){ - this._cleanupHandlers(); - delete this.child; - delete this.container; - delete this.cover; - delete this.fake; - this.inherited(arguments); - } -}); - -dojo.declare("dijit.layout._Gutter", [dijit._Widget, dijit._Templated ], -{ - // summary: - // Just a spacer div to separate side pane from center pane. - // Basically a trick to lookup the gutter/splitter width from the theme. - // description: - // Instantiated by `dijit.layout.BorderContainer`. Users should not - // create directly. - // tags: - // private - - templateString: '<div class="dijitGutter" waiRole="presentation"></div>', - - postCreate: function(){ - this.horizontal = /top|bottom/.test(this.region); - dojo.addClass(this.domNode, "dijitGutter" + (this.horizontal ? "H" : "V")); - } -}); - -} - -if(!dojo._hasResource["dijit.layout.LayoutContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.LayoutContainer"] = true; -dojo.provide("dijit.layout.LayoutContainer"); - - - -dojo.declare("dijit.layout.LayoutContainer", - dijit.layout._LayoutWidget, - { - // summary: - // Deprecated. Use `dijit.layout.BorderContainer` instead. - // - // description: - // Provides Delphi-style panel layout semantics. - // - // A LayoutContainer is a box with a specified size (like style="width: 500px; height: 500px;"), - // that contains children widgets marked with "layoutAlign" of "left", "right", "bottom", "top", and "client". - // It takes it's children marked as left/top/bottom/right, and lays them out along the edges of the box, - // and then it takes the child marked "client" and puts it into the remaining space in the middle. - // - // Left/right positioning is similar to CSS's "float: left" and "float: right", - // and top/bottom positioning would be similar to "float: top" and "float: bottom", if there were such - // CSS. - // - // Note that there can only be one client element, but there can be multiple left, right, top, - // or bottom elements. - // - // example: - // | <style> - // | html, body{ height: 100%; width: 100%; } - // | </style> - // | <div dojoType="dijit.layout.LayoutContainer" style="width: 100%; height: 100%"> - // | <div dojoType="dijit.layout.ContentPane" layoutAlign="top">header text</div> - // | <div dojoType="dijit.layout.ContentPane" layoutAlign="left" style="width: 200px;">table of contents</div> - // | <div dojoType="dijit.layout.ContentPane" layoutAlign="client">client area</div> - // | </div> - // - // Lays out each child in the natural order the children occur in. - // Basically each child is laid out into the "remaining space", where "remaining space" is initially - // the content area of this widget, but is reduced to a smaller rectangle each time a child is added. - // tags: - // deprecated - - baseClass: "dijitLayoutContainer", - - constructor: function(){ - dojo.deprecated("dijit.layout.LayoutContainer is deprecated", "use BorderContainer instead", 2.0); - }, - - layout: function(){ - dijit.layout.layoutChildren(this.domNode, this._contentBox, this.getChildren()); - }, - - addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){ - this.inherited(arguments); - if(this._started){ - dijit.layout.layoutChildren(this.domNode, this._contentBox, this.getChildren()); - } - }, - - removeChild: function(/*dijit._Widget*/ widget){ - this.inherited(arguments); - if(this._started){ - dijit.layout.layoutChildren(this.domNode, this._contentBox, this.getChildren()); - } - } -}); - -// This argument can be specified for the children of a LayoutContainer. -// Since any widget can be specified as a LayoutContainer child, mix it -// into the base widget class. (This is a hack, but it's effective.) -dojo.extend(dijit._Widget, { - // layoutAlign: String - // "none", "left", "right", "bottom", "top", and "client". - // See the LayoutContainer description for details on this parameter. - layoutAlign: 'none' -}); - -} - -if(!dojo._hasResource["dijit.layout.LinkPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.LinkPane"] = true; -dojo.provide("dijit.layout.LinkPane"); - - - - -dojo.declare("dijit.layout.LinkPane", - [dijit.layout.ContentPane, dijit._Templated], - { - // summary: - // A ContentPane with an href where (when declared in markup) - // the title is specified as innerHTML rather than as a title attribute. - // description: - // LinkPane is just a ContentPane that is declared in markup similarly - // to an anchor. The anchor's body (the words between `<a>` and `</a>`) - // become the title of the widget (used for TabContainer, AccordionContainer, etc.) - // example: - // | <a href="foo.html">my title</a> - - // I'm using a template because the user may specify the input as - // <a href="foo.html">title</a>, in which case we need to get rid of the - // <a> because we don't want a link. - templateString: '<div class="dijitLinkPane" dojoAttachPoint="containerNode"></div>', - - postMixInProperties: function(){ - // If user has specified node contents, they become the title - // (the link must be plain text) - if(this.srcNodeRef){ - this.title += this.srcNodeRef.innerHTML; - } - this.inherited(arguments); - }, - - _fillContent: function(/*DomNode*/ source){ - // Overrides _Templated._fillContent(). - - // _Templated._fillContent() relocates srcNodeRef innerHTML to templated container node, - // but in our case the srcNodeRef innerHTML is the title, so shouldn't be - // copied - } -}); - -} - -if(!dojo._hasResource["dijit.layout.SplitContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.SplitContainer"] = true; -dojo.provide("dijit.layout.SplitContainer"); - -// -// FIXME: make it prettier -// FIXME: active dragging upwards doesn't always shift other bars (direction calculation is wrong in this case) -// - - - - -dojo.declare("dijit.layout.SplitContainer", - dijit.layout._LayoutWidget, - { - // summary: - // Deprecated. Use `dijit.layout.BorderContainer` instead. - // description: - // A Container widget with sizing handles in-between each child. - // Contains multiple children widgets, all of which are displayed side by side - // (either horizontally or vertically); there's a bar between each of the children, - // and you can adjust the relative size of each child by dragging the bars. - // - // You must specify a size (width and height) for the SplitContainer. - // tags: - // deprecated - - constructor: function(){ - dojo.deprecated("dijit.layout.SplitContainer is deprecated", "use BorderContainer with splitter instead", 2.0); - }, - - // activeSizing: Boolean - // If true, the children's size changes as you drag the bar; - // otherwise, the sizes don't change until you drop the bar (by mouse-up) - activeSizing: false, - - // sizerWidth: Integer - // Size in pixels of the bar between each child - sizerWidth: 7, // FIXME: this should be a CSS attribute (at 7 because css wants it to be 7 until we fix to css) - - // orientation: String - // either 'horizontal' or vertical; indicates whether the children are - // arranged side-by-side or up/down. - orientation: 'horizontal', - - // persist: Boolean - // Save splitter positions in a cookie - persist: true, - - baseClass: "dijitSplitContainer", - - postMixInProperties: function(){ - this.inherited("postMixInProperties",arguments); - this.isHorizontal = (this.orientation == 'horizontal'); - }, - - postCreate: function(){ - this.inherited(arguments); - this.sizers = []; - - // overflow has to be explicitly hidden for splitContainers using gekko (trac #1435) - // to keep other combined css classes from inadvertantly making the overflow visible - if(dojo.isMozilla){ - this.domNode.style.overflow = '-moz-scrollbars-none'; // hidden doesn't work - } - - // create the fake dragger - if(typeof this.sizerWidth == "object"){ - try{ //FIXME: do this without a try/catch - this.sizerWidth = parseInt(this.sizerWidth.toString()); - }catch(e){ this.sizerWidth = 7; } - } - var sizer = dojo.doc.createElement('div'); - this.virtualSizer = sizer; - sizer.style.position = 'relative'; - - // #1681: work around the dreaded 'quirky percentages in IE' layout bug - // If the splitcontainer's dimensions are specified in percentages, it - // will be resized when the virtualsizer is displayed in _showSizingLine - // (typically expanding its bounds unnecessarily). This happens because - // we use position: relative for .dijitSplitContainer. - // The workaround: instead of changing the display style attribute, - // switch to changing the zIndex (bring to front/move to back) - - sizer.style.zIndex = 10; - sizer.className = this.isHorizontal ? 'dijitSplitContainerVirtualSizerH' : 'dijitSplitContainerVirtualSizerV'; - this.domNode.appendChild(sizer); - dojo.setSelectable(sizer, false); - }, - - destroy: function(){ - delete this.virtualSizer; - dojo.forEach(this._ownconnects, dojo.disconnect); - this.inherited(arguments); - }, - startup: function(){ - if(this._started){ return; } - - dojo.forEach(this.getChildren(), function(child, i, children){ - // attach the children and create the draggers - this._setupChild(child); - - if(i < children.length-1){ - this._addSizer(); - } - }, this); - - if(this.persist){ - this._restoreState(); - } - - this.inherited(arguments); - }, - - _setupChild: function(/*dijit._Widget*/ child){ - this.inherited(arguments); - child.domNode.style.position = "absolute"; - dojo.addClass(child.domNode, "dijitSplitPane"); - }, - - _onSizerMouseDown: function(e){ - if(e.target.id){ - for(var i=0;i<this.sizers.length;i++){ - if(this.sizers[i].id == e.target.id){ - break; - } - } - if(i<this.sizers.length){ - this.beginSizing(e,i); - } - } - }, - _addSizer: function(index){ - index = index === undefined ? this.sizers.length : index; - - // TODO: use a template for this!!! - var sizer = dojo.doc.createElement('div'); - sizer.id=dijit.getUniqueId('dijit_layout_SplitterContainer_Splitter'); - this.sizers.splice(index,0,sizer); - this.domNode.appendChild(sizer); - - sizer.className = this.isHorizontal ? 'dijitSplitContainerSizerH' : 'dijitSplitContainerSizerV'; - - // add the thumb div - var thumb = dojo.doc.createElement('div'); - thumb.className = 'thumb'; - sizer.appendChild(thumb); - - // FIXME: are you serious? why aren't we using mover start/stop combo? - this.connect(sizer, "onmousedown", '_onSizerMouseDown'); - - dojo.setSelectable(sizer, false); - }, - - removeChild: function(widget){ - // summary: - // Remove sizer, but only if widget is really our child and - // we have at least one sizer to throw away - if(this.sizers.length){ - var i=dojo.indexOf(this.getChildren(), widget) - if(i != -1){ - if(i == this.sizers.length){ - i--; - } - dojo.destroy(this.sizers[i]); - this.sizers.splice(i,1); - } - } - - // Remove widget and repaint - this.inherited(arguments); - if(this._started){ - this.layout(); - } - }, - - addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){ - // summary: - // Add a child widget to the container - // child: - // a widget to add - // insertIndex: - // postion in the "stack" to add the child widget - - this.inherited(arguments); - - if(this._started){ - // Do the stuff that startup() does for each widget - var children = this.getChildren(); - if(children.length > 1){ - this._addSizer(insertIndex); - } - - // and then reposition (ie, shrink) every pane to make room for the new guy - this.layout(); - } - }, - - layout: function(){ - // summary: - // Do layout of panels - - // base class defines this._contentBox on initial creation and also - // on resize - this.paneWidth = this._contentBox.w; - this.paneHeight = this._contentBox.h; - - var children = this.getChildren(); - if(!children.length){ return; } - - // - // calculate space - // - - var space = this.isHorizontal ? this.paneWidth : this.paneHeight; - if(children.length > 1){ - space -= this.sizerWidth * (children.length - 1); - } - - // - // calculate total of SizeShare values - // - var outOf = 0; - dojo.forEach(children, function(child){ - outOf += child.sizeShare; - }); - - // - // work out actual pixels per sizeshare unit - // - var pixPerUnit = space / outOf; - - // - // set the SizeActual member of each pane - // - var totalSize = 0; - dojo.forEach(children.slice(0, children.length - 1), function(child){ - var size = Math.round(pixPerUnit * child.sizeShare); - child.sizeActual = size; - totalSize += size; - }); - - children[children.length-1].sizeActual = space - totalSize; - - // - // make sure the sizes are ok - // - this._checkSizes(); - - // - // now loop, positioning each pane and letting children resize themselves - // - - var pos = 0; - var size = children[0].sizeActual; - this._movePanel(children[0], pos, size); - children[0].position = pos; - pos += size; - - // if we don't have any sizers, our layout method hasn't been called yet - // so bail until we are called..TODO: REVISIT: need to change the startup - // algorithm to guaranteed the ordering of calls to layout method - if(!this.sizers){ - return; - } - - dojo.some(children.slice(1), function(child, i){ - // error-checking - if(!this.sizers[i]){ - return true; - } - // first we position the sizing handle before this pane - this._moveSlider(this.sizers[i], pos, this.sizerWidth); - this.sizers[i].position = pos; - pos += this.sizerWidth; - - size = child.sizeActual; - this._movePanel(child, pos, size); - child.position = pos; - pos += size; - }, this); - }, - - _movePanel: function(panel, pos, size){ - if(this.isHorizontal){ - panel.domNode.style.left = pos + 'px'; // TODO: resize() takes l and t parameters too, don't need to set manually - panel.domNode.style.top = 0; - var box = {w: size, h: this.paneHeight}; - if(panel.resize){ - panel.resize(box); - }else{ - dojo.marginBox(panel.domNode, box); - } - }else{ - panel.domNode.style.left = 0; // TODO: resize() takes l and t parameters too, don't need to set manually - panel.domNode.style.top = pos + 'px'; - var box = {w: this.paneWidth, h: size}; - if(panel.resize){ - panel.resize(box); - }else{ - dojo.marginBox(panel.domNode, box); - } - } - }, - - _moveSlider: function(slider, pos, size){ - if(this.isHorizontal){ - slider.style.left = pos + 'px'; - slider.style.top = 0; - dojo.marginBox(slider, { w: size, h: this.paneHeight }); - }else{ - slider.style.left = 0; - slider.style.top = pos + 'px'; - dojo.marginBox(slider, { w: this.paneWidth, h: size }); - } - }, - - _growPane: function(growth, pane){ - if(growth > 0){ - if(pane.sizeActual > pane.sizeMin){ - if((pane.sizeActual - pane.sizeMin) > growth){ - - // stick all the growth in this pane - pane.sizeActual = pane.sizeActual - growth; - growth = 0; - }else{ - // put as much growth in here as we can - growth -= pane.sizeActual - pane.sizeMin; - pane.sizeActual = pane.sizeMin; - } - } - } - return growth; - }, - - _checkSizes: function(){ - - var totalMinSize = 0; - var totalSize = 0; - var children = this.getChildren(); - - dojo.forEach(children, function(child){ - totalSize += child.sizeActual; - totalMinSize += child.sizeMin; - }); - - // only make adjustments if we have enough space for all the minimums - - if(totalMinSize <= totalSize){ - - var growth = 0; - - dojo.forEach(children, function(child){ - if(child.sizeActual < child.sizeMin){ - growth += child.sizeMin - child.sizeActual; - child.sizeActual = child.sizeMin; - } - }); - - if(growth > 0){ - var list = this.isDraggingLeft ? children.reverse() : children; - dojo.forEach(list, function(child){ - growth = this._growPane(growth, child); - }, this); - } - }else{ - dojo.forEach(children, function(child){ - child.sizeActual = Math.round(totalSize * (child.sizeMin / totalMinSize)); - }); - } - }, - - beginSizing: function(e, i){ - var children = this.getChildren(); - this.paneBefore = children[i]; - this.paneAfter = children[i+1]; - - this.isSizing = true; - this.sizingSplitter = this.sizers[i]; - - if(!this.cover){ - this.cover = dojo.create('div', { - style: { - position:'absolute', - zIndex:5, - top: 0, - left: 0, - width: "100%", - height: "100%" - } - }, this.domNode); - }else{ - this.cover.style.zIndex = 5; - } - this.sizingSplitter.style.zIndex = 6; - - // TODO: REVISIT - we want MARGIN_BOX and core hasn't exposed that yet (but can't we use it anyway if we pay attention? we do elsewhere.) - this.originPos = dojo.position(children[0].domNode, true); - if(this.isHorizontal){ - var client = e.layerX || e.offsetX || 0; - var screen = e.pageX; - this.originPos = this.originPos.x; - }else{ - var client = e.layerY || e.offsetY || 0; - var screen = e.pageY; - this.originPos = this.originPos.y; - } - this.startPoint = this.lastPoint = screen; - this.screenToClientOffset = screen - client; - this.dragOffset = this.lastPoint - this.paneBefore.sizeActual - this.originPos - this.paneBefore.position; - - if(!this.activeSizing){ - this._showSizingLine(); - } - - // - // attach mouse events - // - this._ownconnects = []; - this._ownconnects.push(dojo.connect(dojo.doc.documentElement, "onmousemove", this, "changeSizing")); - this._ownconnects.push(dojo.connect(dojo.doc.documentElement, "onmouseup", this, "endSizing")); - - dojo.stopEvent(e); - }, - - changeSizing: function(e){ - if(!this.isSizing){ return; } - this.lastPoint = this.isHorizontal ? e.pageX : e.pageY; - this.movePoint(); - if(this.activeSizing){ - this._updateSize(); - }else{ - this._moveSizingLine(); - } - dojo.stopEvent(e); - }, - - endSizing: function(e){ - if(!this.isSizing){ return; } - if(this.cover){ - this.cover.style.zIndex = -1; - } - if(!this.activeSizing){ - this._hideSizingLine(); - } - - this._updateSize(); - - this.isSizing = false; - - if(this.persist){ - this._saveState(this); - } - - dojo.forEach(this._ownconnects, dojo.disconnect); - }, - - movePoint: function(){ - - // make sure lastPoint is a legal point to drag to - var p = this.lastPoint - this.screenToClientOffset; - - var a = p - this.dragOffset; - a = this.legaliseSplitPoint(a); - p = a + this.dragOffset; - - this.lastPoint = p + this.screenToClientOffset; - }, - - legaliseSplitPoint: function(a){ - - a += this.sizingSplitter.position; - - this.isDraggingLeft = !!(a > 0); - - if(!this.activeSizing){ - var min = this.paneBefore.position + this.paneBefore.sizeMin; - if(a < min){ - a = min; - } - - var max = this.paneAfter.position + (this.paneAfter.sizeActual - (this.sizerWidth + this.paneAfter.sizeMin)); - if(a > max){ - a = max; - } - } - - a -= this.sizingSplitter.position; - - this._checkSizes(); - - return a; - }, - - _updateSize: function(){ - //FIXME: sometimes this.lastPoint is NaN - var pos = this.lastPoint - this.dragOffset - this.originPos; - - var start_region = this.paneBefore.position; - var end_region = this.paneAfter.position + this.paneAfter.sizeActual; - - this.paneBefore.sizeActual = pos - start_region; - this.paneAfter.position = pos + this.sizerWidth; - this.paneAfter.sizeActual = end_region - this.paneAfter.position; - - dojo.forEach(this.getChildren(), function(child){ - child.sizeShare = child.sizeActual; - }); - - if(this._started){ - this.layout(); - } - }, - - _showSizingLine: function(){ - - this._moveSizingLine(); - - dojo.marginBox(this.virtualSizer, - this.isHorizontal ? { w: this.sizerWidth, h: this.paneHeight } : { w: this.paneWidth, h: this.sizerWidth }); - - this.virtualSizer.style.display = 'block'; - }, - - _hideSizingLine: function(){ - this.virtualSizer.style.display = 'none'; - }, - - _moveSizingLine: function(){ - var pos = (this.lastPoint - this.startPoint) + this.sizingSplitter.position; - dojo.style(this.virtualSizer,(this.isHorizontal ? "left" : "top"),pos+"px"); - // this.virtualSizer.style[ this.isHorizontal ? "left" : "top" ] = pos + 'px'; // FIXME: remove this line if the previous is better - }, - - _getCookieName: function(i){ - return this.id + "_" + i; - }, - - _restoreState: function(){ - dojo.forEach(this.getChildren(), function(child, i){ - var cookieName = this._getCookieName(i); - var cookieValue = dojo.cookie(cookieName); - if(cookieValue){ - var pos = parseInt(cookieValue); - if(typeof pos == "number"){ - child.sizeShare = pos; - } - } - }, this); - }, - - _saveState: function(){ - if(!this.persist){ - return; - } - dojo.forEach(this.getChildren(), function(child, i){ - dojo.cookie(this._getCookieName(i), child.sizeShare, {expires:365}); - }, this); - } -}); - -// These arguments can be specified for the children of a SplitContainer. -// Since any widget can be specified as a SplitContainer child, mix them -// into the base widget class. (This is a hack, but it's effective.) -dojo.extend(dijit._Widget, { - // sizeMin: [deprecated] Integer - // Deprecated. Parameter for children of `dijit.layout.SplitContainer`. - // Minimum size (width or height) of a child of a SplitContainer. - // The value is relative to other children's sizeShare properties. - sizeMin: 10, - - // sizeShare: [deprecated] Integer - // Deprecated. Parameter for children of `dijit.layout.SplitContainer`. - // Size (width or height) of a child of a SplitContainer. - // The value is relative to other children's sizeShare properties. - // For example, if there are two children and each has sizeShare=10, then - // each takes up 50% of the available space. - sizeShare: 10 -}); - -} - -if(!dojo._hasResource["dijit.layout._TabContainerBase"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout._TabContainerBase"] = true; -dojo.provide("dijit.layout._TabContainerBase"); - - - - -dojo.declare("dijit.layout._TabContainerBase", - [dijit.layout.StackContainer, dijit._Templated], - { - // summary: - // Abstract base class for TabContainer. Must define _makeController() to instantiate - // and return the widget that displays the tab labels - // description: - // A TabContainer is a container that has multiple panes, but shows only - // one pane at a time. There are a set of tabs corresponding to each pane, - // where each tab has the name (aka title) of the pane, and optionally a close button. - - // tabPosition: String - // Defines where tabs go relative to tab content. - // "top", "bottom", "left-h", "right-h" - tabPosition: "top", - - baseClass: "dijitTabContainer", - - // tabStrip: Boolean - // Defines whether the tablist gets an extra class for layouting, putting a border/shading - // around the set of tabs. - tabStrip: false, - - // nested: Boolean - // If true, use styling for a TabContainer nested inside another TabContainer. - // For tundra etc., makes tabs look like links, and hides the outer - // border since the outer TabContainer already has a border. - nested: false, - - templateString: dojo.cache("dijit.layout", "templates/TabContainer.html", "<div class=\"dijitTabContainer\">\n\t<div class=\"dijitTabListWrapper\" dojoAttachPoint=\"tablistNode\"></div>\n\t<div dojoAttachPoint=\"tablistSpacer\" class=\"dijitTabSpacer ${baseClass}-spacer\"></div>\n\t<div class=\"dijitTabPaneWrapper ${baseClass}-container\" dojoAttachPoint=\"containerNode\"></div>\n</div>\n"), - - postMixInProperties: function(){ - // set class name according to tab position, ex: dijitTabContainerTop - this.baseClass += this.tabPosition.charAt(0).toUpperCase() + this.tabPosition.substr(1).replace(/-.*/, ""); - - this.srcNodeRef && dojo.style(this.srcNodeRef, "visibility", "hidden"); - - this.inherited(arguments); - }, - - postCreate: function(){ - this.inherited(arguments); - - // Create the tab list that will have a tab (a.k.a. tab button) for each tab panel - this.tablist = this._makeController(this.tablistNode); - - if(!this.doLayout){ dojo.addClass(this.domNode, "dijitTabContainerNoLayout"); } - - if(this.nested){ - /* workaround IE's lack of support for "a > b" selectors by - * tagging each node in the template. - */ - dojo.addClass(this.domNode, "dijitTabContainerNested"); - dojo.addClass(this.tablist.containerNode, "dijitTabContainerTabListNested"); - dojo.addClass(this.tablistSpacer, "dijitTabContainerSpacerNested"); - dojo.addClass(this.containerNode, "dijitTabPaneWrapperNested"); - }else{ - dojo.addClass(this.domNode, "tabStrip-" + (this.tabStrip ? "enabled" : "disabled")); - } - }, - - _setupChild: function(/*dijit._Widget*/ tab){ - // Overrides StackContainer._setupChild(). - dojo.addClass(tab.domNode, "dijitTabPane"); - this.inherited(arguments); - }, - - startup: function(){ - if(this._started){ return; } - - // wire up the tablist and its tabs - this.tablist.startup(); - - this.inherited(arguments); - }, - - layout: function(){ - // Overrides StackContainer.layout(). - // Configure the content pane to take up all the space except for where the tabs are - - if(!this._contentBox || typeof(this._contentBox.l) == "undefined"){return;} - - var sc = this.selectedChildWidget; - - if(this.doLayout){ - // position and size the titles and the container node - var titleAlign = this.tabPosition.replace(/-h/, ""); - this.tablist.layoutAlign = titleAlign; - var children = [this.tablist, { - domNode: this.tablistSpacer, - layoutAlign: titleAlign - }, { - domNode: this.containerNode, - layoutAlign: "client" - }]; - dijit.layout.layoutChildren(this.domNode, this._contentBox, children); - - // Compute size to make each of my children. - // children[2] is the margin-box size of this.containerNode, set by layoutChildren() call above - this._containerContentBox = dijit.layout.marginBox2contentBox(this.containerNode, children[2]); - - if(sc && sc.resize){ - sc.resize(this._containerContentBox); - } - }else{ - // just layout the tab controller, so it can position left/right buttons etc. - if(this.tablist.resize){ - this.tablist.resize({w: dojo.contentBox(this.domNode).w}); - } - - // and call resize() on the selected pane just to tell it that it's been made visible - if(sc && sc.resize){ - sc.resize(); - } - } - }, - - destroy: function(){ - if(this.tablist){ - this.tablist.destroy(); - } - this.inherited(arguments); - } -}); - - -} - -if(!dojo._hasResource["dijit.layout.TabController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.TabController"] = true; -dojo.provide("dijit.layout.TabController"); - - - -// Menu is used for an accessible close button, would be nice to have a lighter-weight solution - - - - - -dojo.declare("dijit.layout.TabController", - dijit.layout.StackController, -{ - // summary: - // Set of tabs (the things with titles and a close button, that you click to show a tab panel). - // Used internally by `dijit.layout.TabContainer`. - // description: - // Lets the user select the currently shown pane in a TabContainer or StackContainer. - // TabController also monitors the TabContainer, and whenever a pane is - // added or deleted updates itself accordingly. - // tags: - // private - - templateString: "<div wairole='tablist' dojoAttachEvent='onkeypress:onkeypress'></div>", - - // tabPosition: String - // Defines where tabs go relative to the content. - // "top", "bottom", "left-h", "right-h" - tabPosition: "top", - - // buttonWidget: String - // The name of the tab widget to create to correspond to each page - buttonWidget: "dijit.layout._TabButton", - - _rectifyRtlTabList: function(){ - // summary: - // For left/right TabContainer when page is RTL mode, rectify the width of all tabs to be equal, otherwise the tab widths are different in IE - - if(0 >= this.tabPosition.indexOf('-h')){ return; } - if(!this.pane2button){ return; } - - var maxWidth = 0; - for(var pane in this.pane2button){ - var ow = this.pane2button[pane].innerDiv.scrollWidth; - maxWidth = Math.max(maxWidth, ow); - } - //unify the length of all the tabs - for(pane in this.pane2button){ - this.pane2button[pane].innerDiv.style.width = maxWidth + 'px'; - } - } -}); - -dojo.declare("dijit.layout._TabButton", - dijit.layout._StackButton, - { - // summary: - // A tab (the thing you click to select a pane). - // description: - // Contains the title of the pane, and optionally a close-button to destroy the pane. - // This is an internal widget and should not be instantiated directly. - // tags: - // private - - // baseClass: String - // The CSS class applied to the domNode. - baseClass: "dijitTab", - - // Apply dijitTabCloseButtonHover when close button is hovered - cssStateNodes: { - closeNode: "dijitTabCloseButton" - }, - - templateString: dojo.cache("dijit.layout", "templates/_TabButton.html", "<div waiRole=\"presentation\" dojoAttachPoint=\"titleNode\" dojoAttachEvent='onclick:onClick'>\n <div waiRole=\"presentation\" class='dijitTabInnerDiv' dojoAttachPoint='innerDiv'>\n <div waiRole=\"presentation\" class='dijitTabContent' dojoAttachPoint='tabContent'>\n \t<div waiRole=\"presentation\" dojoAttachPoint='focusNode'>\n\t\t <img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon\" dojoAttachPoint='iconNode' />\n\t\t <span dojoAttachPoint='containerNode' class='tabLabel'></span>\n\t\t <span class=\"dijitInline dijitTabCloseButton dijitTabCloseIcon\" dojoAttachPoint='closeNode'\n\t\t \t\tdojoAttachEvent='onclick: onClickCloseButton' waiRole=\"presentation\">\n\t\t <span dojoAttachPoint='closeText' class='dijitTabCloseText'>x</span\n\t\t ></span>\n\t\t\t</div>\n </div>\n </div>\n</div>\n"), - - // Override _FormWidget.scrollOnFocus. - // Don't scroll the whole tab container into view when the button is focused. - scrollOnFocus: false, - - postMixInProperties: function(){ - // Override blank iconClass from Button to do tab height adjustment on IE6, - // to make sure that tabs with and w/out close icons are same height - if(!this.iconClass){ - this.iconClass = "dijitTabButtonIcon"; - } - }, - - postCreate: function(){ - this.inherited(arguments); - dojo.setSelectable(this.containerNode, false); - - // If a custom icon class has not been set for the - // tab icon, set its width to one pixel. This ensures - // that the height styling of the tab is maintained, - // as it is based on the height of the icon. - // TODO: I still think we can just set dijitTabButtonIcon to 1px in CSS <Bill> - if(this.iconNode.className == "dijitTabButtonIcon"){ - dojo.style(this.iconNode, "width", "1px"); - } - }, - - startup: function(){ - this.inherited(arguments); - var n = this.domNode; - - // Required to give IE6 a kick, as it initially hides the - // tabs until they are focused on. - setTimeout(function(){ - n.className = n.className; - }, 1); - }, - - _setCloseButtonAttr: function(disp){ - this.closeButton = disp; - dojo.toggleClass(this.innerDiv, "dijitClosable", disp); - this.closeNode.style.display = disp ? "" : "none"; - if(disp){ - var _nlsResources = dojo.i18n.getLocalization("dijit", "common"); - if(this.closeNode){ - dojo.attr(this.closeNode,"title", _nlsResources.itemClose); - } - // add context menu onto title button - var _nlsResources = dojo.i18n.getLocalization("dijit", "common"); - this._closeMenu = new dijit.Menu({ - id: this.id+"_Menu", - dir: this.dir, - lang: this.lang, - targetNodeIds: [this.domNode] - }); - - this._closeMenu.addChild(new dijit.MenuItem({ - label: _nlsResources.itemClose, - dir: this.dir, - lang: this.lang, - onClick: dojo.hitch(this, "onClickCloseButton") - })); - }else{ - if(this._closeMenu){ - this._closeMenu.destroyRecursive(); - delete this._closeMenu; - } - } - }, - _setLabelAttr: function(/*String*/ content){ - // summary: - // Hook for attr('label', ...) to work. - // description: - // takes an HTML string. - // Inherited ToggleButton implementation will Set the label (text) of the button; - // Need to set the alt attribute of icon on tab buttons if no label displayed - this.inherited(arguments); - if(this.showLabel == false && !this.params.title){ - this.iconNode.alt = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || ''); - } - }, - - destroy: function(){ - if(this._closeMenu){ - this._closeMenu.destroyRecursive(); - delete this._closeMenu; - } - this.inherited(arguments); - } -}); - -} - -if(!dojo._hasResource["dijit.layout.ScrollingTabController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.ScrollingTabController"] = true; -dojo.provide("dijit.layout.ScrollingTabController"); - - - - -dojo.declare("dijit.layout.ScrollingTabController", - dijit.layout.TabController, - { - // 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 - - templateString: dojo.cache("dijit.layout", "templates/ScrollingTabController.html", "<div class=\"dijitTabListContainer-${tabPosition}\" style=\"visibility:hidden\">\n\t<div dojoType=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_menuBtn\" iconClass=\"dijitTabStripMenuIcon\"\n\t\t\tdojoAttachPoint=\"_menuBtn\" showLabel=false>▼</div>\n\t<div dojoType=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_leftBtn\" iconClass=\"dijitTabStripSlideLeftIcon\"\n\t\t\tdojoAttachPoint=\"_leftBtn\" dojoAttachEvent=\"onClick: doSlideLeft\" showLabel=false>◀</div>\n\t<div dojoType=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_rightBtn\" iconClass=\"dijitTabStripSlideRightIcon\"\n\t\t\tdojoAttachPoint=\"_rightBtn\" dojoAttachEvent=\"onClick: doSlideRight\" showLabel=false>▶</div>\n\t<div class='dijitTabListWrapper' dojoAttachPoint='tablistWrapper'>\n\t\t<div wairole='tablist' dojoAttachEvent='onkeypress:onkeypress'\n\t\t\t\tdojoAttachPoint='containerNode' class='nowrapTabStrip'></div>\n\t</div>\n</div>\n"), - - // 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: 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, - - attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, { - "class": "containerNode" - }), - - postCreate: 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"; - dojo.addClass(n, "tabStrip-disabled") - } - - dojo.addClass(this.tablistWrapper, this.tabStripClass); - }, - - onStartup: function(){ - this.inherited(arguments); - - // Do not show the TabController until the related - // StackController has added it's children. This gives - // a less visually jumpy instantiation. - dojo.style(this.domNode, "visibility", "visible"); - this._postStartup = true; - }, - - onAddChild: function(page, insertIndex){ - this.inherited(arguments); - var menuItem; - if(this.useMenu){ - var containerId = this.containerId; - menuItem = new dijit.MenuItem({ - id: page.id + "_stcMi", - label: page.title, - dir: page.dir, - lang: page.lang, - onClick: dojo.hitch(this, function(){ - var container = dijit.byId(containerId); - container.selectChild(page); - }) - }); - this._menuChildren[page.id] = menuItem; - this._menu.addChild(menuItem, insertIndex); - } - - // update the menuItem label when the button label is updated - this.pane2handles[page.id].push( - this.connect(this.pane2button[page.id], "set", function(name, value){ - if(this._postStartup){ - if(name == "label"){ - if(menuItem){ - menuItem.set(name, value); - } - - // The changed label will have changed the width of the - // buttons, so do a resize - if(this._dim){ - this.resize(this._dim); - } - } - } - }) - ); - - // 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. - dojo.style(this.containerNode, "width", - (dojo.style(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; - } - - // delete menu entry corresponding to pane that was removed from TabContainer - if(this.useMenu && page && page.id && this._menuChildren[page.id]){ - this._menu.removeChild(this._menuChildren[page.id]); - this._menuChildren[page.id].destroy(); - delete this._menuChildren[page.id]; - } - - 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. - this._menuChildren = {}; - - // 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 = dojo.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 += dojo.marginBox(btn).w; - return true; - }else{ - dojo.style(btn, "display", "none"); - return false; - } - }, this); - - if(this.useMenu){ - // Create the menu that is used to select tabs. - this._menu = new dijit.Menu({ - id: this.id + "_menu", - dir: this.dir, - lang: this.lang, - targetNodeIds: [this._menuBtn.domNode], - leftClickToOpen: true, - refocus: false // selecting a menu item sets focus to a TabButton - }); - this._supportingWidgets.push(this._menu); - } - }, - - _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 + dojo.style(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 || dojo.style(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. - - if(this.domNode.offsetWidth == 0){ - return; - } - - // 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"; - this._contentBox = dijit.layout.marginBox2contentBox(this.domNode, {h: 0, w: dim.w}); - this._contentBox.h = this.scrollNode.offsetHeight; - dojo.contentBox(this.domNode, this._contentBox); - - // 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"; - dijit.layout.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(); - } - var w = this.scrollNode, - sl = this._convertToScrollLeft(this._getScrollForSelectedTab()); - w.scrollLeft = sl; - } - - // Enable/disabled left right buttons depending on whether or not user can scroll to left or right - this._setButtonClass(this._getScroll()); - - this._postResize = true; - }, - - _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" - var sl = (this.isLeftToRight() || dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.isWebKit) ? this.scrollNode.scrollLeft : - dojo.style(this.containerNode, "width") - dojo.style(this.scrollNode, "width") - + (dojo.isIE == 8 ? -1 : 1) * this.scrollNode.scrollLeft; - return sl; - }, - - _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() || dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.isWebKit){ - return val; - }else{ - var maxScroll = dojo.style(this.containerNode, "width") - dojo.style(this.scrollNode, "width"); - return (dojo.isIE == 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;} - - // Scroll to the selected tab, except on startup, when scrolling is handled in resize() - var node = tab.domNode; - if(this._postResize && node != this._selectedTab){ - this._selectedTab = node; - - var sl = this._getScroll(); - - if(sl > node.offsetLeft || - sl + dojo.style(this.scrollNode, "width") < - node.offsetLeft + dojo.style(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 = dojo.style(this.scrollNode, "width"), // about 500px - containerWidth = dojo.style(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 + dojo.style(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 = dojo.style(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 + dojo.style(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 dojo._Animation({ - beforeBegin: function(){ - if(this.curve){ delete this.curve; } - var oldS = w.scrollLeft, - newS = self._convertToScrollLeft(x); - anim.curve = new dojo._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(e){ - // summary: - // Gets a button DOM node from a mouse click event. - // e: - // The mouse click event. - var n = e.target; - while(n && !dojo.hasClass(n, "tabStripButton")){ - n = n.parentNode; - } - return n; - }, - - doSlideRight: function(e){ - // summary: - // Scrolls the menu to the right. - // e: - // The mouse click event. - this.doSlide(1, this._getBtnNode(e)); - }, - - doSlideLeft: function(e){ - // summary: - // Scrolls the menu to the left. - // e: - // The mouse click event. - this.doSlide(-1,this._getBtnNode(e)); - }, - - doSlide: function(direction, 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 && dojo.hasClass(node, "dijitTabDisabled")){return;} - - var sWidth = dojo.style(this.scrollNode, "width"); - var d = (sWidth * 0.75) * direction; - - var to = this._getScroll() + d; - - this._setButtonClass(to); - - this.createSmoothScroll(to).play(); - }, - - _setButtonClass: function(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); - } -}); - -dojo.declare("dijit.layout._ScrollingTabControllerButton", - dijit.form.Button, - { - baseClass: "dijitTab tabStripButton", - - templateString: dojo.cache("dijit.layout", "templates/_ScrollingTabControllerButton.html", "<div dojoAttachEvent=\"onclick:_onButtonClick\">\n\t<div waiRole=\"presentation\" class=\"dijitTabInnerDiv\" dojoattachpoint=\"innerDiv,focusNode\">\n\t\t<div waiRole=\"presentation\" class=\"dijitTabContent dijitButtonContents\" dojoattachpoint=\"tabContent\">\n\t\t\t<img waiRole=\"presentation\" alt=\"\" src=\"${_blankGif}\" class=\"dijitTabStripIcon\" dojoAttachPoint=\"iconNode\"/>\n\t\t\t<span dojoAttachPoint=\"containerNode,titleNode\" class=\"dijitButtonText\"></span>\n\t\t</div>\n\t</div>\n</div>\n"), - - // Override inherited tabIndex: 0 from dijit.form.Button, because user shouldn't be - // able to tab to the left/right/menu buttons - tabIndex: "-1" - } -); - -} - -if(!dojo._hasResource["dijit.layout.TabContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.TabContainer"] = true; -dojo.provide("dijit.layout.TabContainer"); - - - - - -dojo.declare("dijit.layout.TabContainer", - dijit.layout._TabContainerBase, - { - // summary: - // A Container with tabs to select each child (only one of which is displayed at a time). - // description: - // A TabContainer is a container that has multiple panes, but shows only - // one pane at a time. There are a set of tabs corresponding to each pane, - // where each tab has the name (aka title) of the pane, and optionally a close button. - - // useMenu: [const] Boolean - // True if a menu should be used to select tabs when they are too - // wide to fit the TabContainer, false otherwise. - useMenu: true, - - // useSlider: [const] Boolean - // True if a slider should be used to select tabs when they are too - // wide to fit the TabContainer, false otherwise. - useSlider: true, - - // controllerWidget: String - // An optional parameter to override the widget used to display the tab labels - controllerWidget: "", - - _makeController: function(/*DomNode*/ srcNode){ - // summary: - // Instantiate tablist controller widget and return reference to it. - // Callback from _TabContainerBase.postCreate(). - // tags: - // protected extension - - var cls = this.baseClass + "-tabs" + (this.doLayout ? "" : " dijitTabNoLayout"), - TabController = dojo.getObject(this.controllerWidget); - - return new TabController({ - id: this.id + "_tablist", - dir: this.dir, - lang: this.lang, - tabPosition: this.tabPosition, - doLayout: this.doLayout, - containerId: this.id, - "class": cls, - nested: this.nested, - useMenu: this.useMenu, - useSlider: this.useSlider, - tabStripClass: this.tabStrip ? this.baseClass + (this.tabStrip ? "":"No") + "Strip": null - }, srcNode); - }, - - postMixInProperties: function(){ - this.inherited(arguments); - - // Scrolling controller only works for horizontal non-nested tabs - if(!this.controllerWidget){ - this.controllerWidget = (this.tabPosition == "top" || this.tabPosition == "bottom") && !this.nested ? - "dijit.layout.ScrollingTabController" : "dijit.layout.TabController"; - } - } -}); - - -} - -if(!dojo._hasResource["dijit.dijit-all"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.dijit-all"] = true; -console.warn("dijit-all may include much more code than your application actually requires. We strongly recommend that you investigate a custom build or the web build tool"); -dojo.provide("dijit.dijit-all"); - -/*===== -dijit["dijit-all"] = { - // summary: - // A rollup that includes every dijit. You probably don't need this. -}; -=====*/ - - - - - - - - - - -// Editor - - - - - - - - - - - - - - - - - - - -// Form widgets - - -// Button widgets - - - - - - - -// TextBox widgets - - - - - - - -// Select widgets - - - - - -// Slider widgets - - - - - - - -// Textarea widgets - - - -// Layout widgets - - - - //deprecated - - //deprecated - - - -} - - -dojo.i18n._preloadLocalizations("dijit.nls.dijit-all", ["ROOT","ar","ca","cs","da","de","de-de","el","en","en-gb","en-us","es","es-es","fi","fi-fi","fr","fr-fr","he","he-il","hu","it","it-it","ja","ja-jp","ko","ko-kr","nb","nl","nl-nl","pl","pt","pt-br","pt-pt","ru","sk","sl","sv","th","tr","xx","zh","zh-cn","zh-tw"]); |