]> git.wh0rd.org - tt-rss.git/blame - lib/dijit/dijit-all.js.uncompressed.js
add dijit/dojo stuff; initial ui mockup
[tt-rss.git] / lib / dijit / dijit-all.js.uncompressed.js
CommitLineData
2f01fe57
AD
1/*
2 Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
3 Available via Academic Free License >= 2.1 OR the modified BSD license.
4 see: http://dojotoolkit.org/license for details
5*/
6
7/*
8 This is an optimized version of Dojo, built for deployment and not for
9 development. To get sources and documentation, please visit:
10
11 http://dojotoolkit.org
12*/
13
14if(!dojo._hasResource["dojo.colors"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15dojo._hasResource["dojo.colors"] = true;
16dojo.provide("dojo.colors");
17
18//TODO: this module appears to break naming conventions
19
20/*=====
21dojo.colors = {
22 // summary: Color utilities
23}
24=====*/
25
26(function(){
27 // this is a standard conversion prescribed by the CSS3 Color Module
28 var hue2rgb = function(m1, m2, h){
29 if(h < 0){ ++h; }
30 if(h > 1){ --h; }
31 var h6 = 6 * h;
32 if(h6 < 1){ return m1 + (m2 - m1) * h6; }
33 if(2 * h < 1){ return m2; }
34 if(3 * h < 2){ return m1 + (m2 - m1) * (2 / 3 - h) * 6; }
35 return m1;
36 };
37
38 dojo.colorFromRgb = function(/*String*/ color, /*dojo.Color?*/ obj){
39 // summary:
40 // get rgb(a) array from css-style color declarations
41 // description:
42 // this function can handle all 4 CSS3 Color Module formats: rgb,
43 // rgba, hsl, hsla, including rgb(a) with percentage values.
44 var m = color.toLowerCase().match(/^(rgba?|hsla?)\(([\s\.\-,%0-9]+)\)/);
45 if(m){
46 var c = m[2].split(/\s*,\s*/), l = c.length, t = m[1], a;
47 if((t == "rgb" && l == 3) || (t == "rgba" && l == 4)){
48 var r = c[0];
49 if(r.charAt(r.length - 1) == "%"){
50 // 3 rgb percentage values
51 a = dojo.map(c, function(x){
52 return parseFloat(x) * 2.56;
53 });
54 if(l == 4){ a[3] = c[3]; }
55 return dojo.colorFromArray(a, obj); // dojo.Color
56 }
57 return dojo.colorFromArray(c, obj); // dojo.Color
58 }
59 if((t == "hsl" && l == 3) || (t == "hsla" && l == 4)){
60 // normalize hsl values
61 var H = ((parseFloat(c[0]) % 360) + 360) % 360 / 360,
62 S = parseFloat(c[1]) / 100,
63 L = parseFloat(c[2]) / 100,
64 // calculate rgb according to the algorithm
65 // recommended by the CSS3 Color Module
66 m2 = L <= 0.5 ? L * (S + 1) : L + S - L * S,
67 m1 = 2 * L - m2;
68 a = [
69 hue2rgb(m1, m2, H + 1 / 3) * 256,
70 hue2rgb(m1, m2, H) * 256,
71 hue2rgb(m1, m2, H - 1 / 3) * 256,
72 1
73 ];
74 if(l == 4){ a[3] = c[3]; }
75 return dojo.colorFromArray(a, obj); // dojo.Color
76 }
77 }
78 return null; // dojo.Color
79 };
80
81 var confine = function(c, low, high){
82 // summary:
83 // sanitize a color component by making sure it is a number,
84 // and clamping it to valid values
85 c = Number(c);
86 return isNaN(c) ? high : c < low ? low : c > high ? high : c; // Number
87 };
88
89 dojo.Color.prototype.sanitize = function(){
90 // summary: makes sure that the object has correct attributes
91 var t = this;
92 t.r = Math.round(confine(t.r, 0, 255));
93 t.g = Math.round(confine(t.g, 0, 255));
94 t.b = Math.round(confine(t.b, 0, 255));
95 t.a = confine(t.a, 0, 1);
96 return this; // dojo.Color
97 };
98})();
99
100
101dojo.colors.makeGrey = function(/*Number*/ g, /*Number?*/ a){
102 // summary: creates a greyscale color with an optional alpha
103 return dojo.colorFromArray([g, g, g, a]);
104};
105
106// mixin all CSS3 named colors not already in _base, along with SVG 1.0 variant spellings
107dojo.mixin(dojo.Color.named, {
108 aliceblue: [240,248,255],
109 antiquewhite: [250,235,215],
110 aquamarine: [127,255,212],
111 azure: [240,255,255],
112 beige: [245,245,220],
113 bisque: [255,228,196],
114 blanchedalmond: [255,235,205],
115 blueviolet: [138,43,226],
116 brown: [165,42,42],
117 burlywood: [222,184,135],
118 cadetblue: [95,158,160],
119 chartreuse: [127,255,0],
120 chocolate: [210,105,30],
121 coral: [255,127,80],
122 cornflowerblue: [100,149,237],
123 cornsilk: [255,248,220],
124 crimson: [220,20,60],
125 cyan: [0,255,255],
126 darkblue: [0,0,139],
127 darkcyan: [0,139,139],
128 darkgoldenrod: [184,134,11],
129 darkgray: [169,169,169],
130 darkgreen: [0,100,0],
131 darkgrey: [169,169,169],
132 darkkhaki: [189,183,107],
133 darkmagenta: [139,0,139],
134 darkolivegreen: [85,107,47],
135 darkorange: [255,140,0],
136 darkorchid: [153,50,204],
137 darkred: [139,0,0],
138 darksalmon: [233,150,122],
139 darkseagreen: [143,188,143],
140 darkslateblue: [72,61,139],
141 darkslategray: [47,79,79],
142 darkslategrey: [47,79,79],
143 darkturquoise: [0,206,209],
144 darkviolet: [148,0,211],
145 deeppink: [255,20,147],
146 deepskyblue: [0,191,255],
147 dimgray: [105,105,105],
148 dimgrey: [105,105,105],
149 dodgerblue: [30,144,255],
150 firebrick: [178,34,34],
151 floralwhite: [255,250,240],
152 forestgreen: [34,139,34],
153 gainsboro: [220,220,220],
154 ghostwhite: [248,248,255],
155 gold: [255,215,0],
156 goldenrod: [218,165,32],
157 greenyellow: [173,255,47],
158 grey: [128,128,128],
159 honeydew: [240,255,240],
160 hotpink: [255,105,180],
161 indianred: [205,92,92],
162 indigo: [75,0,130],
163 ivory: [255,255,240],
164 khaki: [240,230,140],
165 lavender: [230,230,250],
166 lavenderblush: [255,240,245],
167 lawngreen: [124,252,0],
168 lemonchiffon: [255,250,205],
169 lightblue: [173,216,230],
170 lightcoral: [240,128,128],
171 lightcyan: [224,255,255],
172 lightgoldenrodyellow: [250,250,210],
173 lightgray: [211,211,211],
174 lightgreen: [144,238,144],
175 lightgrey: [211,211,211],
176 lightpink: [255,182,193],
177 lightsalmon: [255,160,122],
178 lightseagreen: [32,178,170],
179 lightskyblue: [135,206,250],
180 lightslategray: [119,136,153],
181 lightslategrey: [119,136,153],
182 lightsteelblue: [176,196,222],
183 lightyellow: [255,255,224],
184 limegreen: [50,205,50],
185 linen: [250,240,230],
186 magenta: [255,0,255],
187 mediumaquamarine: [102,205,170],
188 mediumblue: [0,0,205],
189 mediumorchid: [186,85,211],
190 mediumpurple: [147,112,219],
191 mediumseagreen: [60,179,113],
192 mediumslateblue: [123,104,238],
193 mediumspringgreen: [0,250,154],
194 mediumturquoise: [72,209,204],
195 mediumvioletred: [199,21,133],
196 midnightblue: [25,25,112],
197 mintcream: [245,255,250],
198 mistyrose: [255,228,225],
199 moccasin: [255,228,181],
200 navajowhite: [255,222,173],
201 oldlace: [253,245,230],
202 olivedrab: [107,142,35],
203 orange: [255,165,0],
204 orangered: [255,69,0],
205 orchid: [218,112,214],
206 palegoldenrod: [238,232,170],
207 palegreen: [152,251,152],
208 paleturquoise: [175,238,238],
209 palevioletred: [219,112,147],
210 papayawhip: [255,239,213],
211 peachpuff: [255,218,185],
212 peru: [205,133,63],
213 pink: [255,192,203],
214 plum: [221,160,221],
215 powderblue: [176,224,230],
216 rosybrown: [188,143,143],
217 royalblue: [65,105,225],
218 saddlebrown: [139,69,19],
219 salmon: [250,128,114],
220 sandybrown: [244,164,96],
221 seagreen: [46,139,87],
222 seashell: [255,245,238],
223 sienna: [160,82,45],
224 skyblue: [135,206,235],
225 slateblue: [106,90,205],
226 slategray: [112,128,144],
227 slategrey: [112,128,144],
228 snow: [255,250,250],
229 springgreen: [0,255,127],
230 steelblue: [70,130,180],
231 tan: [210,180,140],
232 thistle: [216,191,216],
233 tomato: [255,99,71],
234 transparent: [0, 0, 0, 0],
235 turquoise: [64,224,208],
236 violet: [238,130,238],
237 wheat: [245,222,179],
238 whitesmoke: [245,245,245],
239 yellowgreen: [154,205,50]
240});
241
242}
243
244if(!dojo._hasResource["dojo.i18n"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
245dojo._hasResource["dojo.i18n"] = true;
246dojo.provide("dojo.i18n");
247
248/*=====
249dojo.i18n = {
250 // summary: Utility classes to enable loading of resources for internationalization (i18n)
251};
252=====*/
253
254dojo.i18n.getLocalization = function(/*String*/packageName, /*String*/bundleName, /*String?*/locale){
255 // summary:
256 // Returns an Object containing the localization for a given resource
257 // bundle in a package, matching the specified locale.
258 // description:
259 // Returns a hash containing name/value pairs in its prototypesuch
260 // that values can be easily overridden. Throws an exception if the
261 // bundle is not found. Bundle must have already been loaded by
262 // `dojo.requireLocalization()` or by a build optimization step. NOTE:
263 // try not to call this method as part of an object property
264 // definition (`var foo = { bar: dojo.i18n.getLocalization() }`). In
265 // some loading situations, the bundle may not be available in time
266 // for the object definition. Instead, call this method inside a
267 // function that is run after all modules load or the page loads (like
268 // in `dojo.addOnLoad()`), or in a widget lifecycle method.
269 // packageName:
270 // package which is associated with this resource
271 // bundleName:
272 // the base filename of the resource bundle (without the ".js" suffix)
273 // locale:
274 // the variant to load (optional). By default, the locale defined by
275 // the host environment: dojo.locale
276
277 locale = dojo.i18n.normalizeLocale(locale);
278
279 // look for nearest locale match
280 var elements = locale.split('-');
281 var module = [packageName,"nls",bundleName].join('.');
282 var bundle = dojo._loadedModules[module];
283 if(bundle){
284 var localization;
285 for(var i = elements.length; i > 0; i--){
286 var loc = elements.slice(0, i).join('_');
287 if(bundle[loc]){
288 localization = bundle[loc];
289 break;
290 }
291 }
292 if(!localization){
293 localization = bundle.ROOT;
294 }
295
296 // make a singleton prototype so that the caller won't accidentally change the values globally
297 if(localization){
298 var clazz = function(){};
299 clazz.prototype = localization;
300 return new clazz(); // Object
301 }
302 }
303
304 throw new Error("Bundle not found: " + bundleName + " in " + packageName+" , locale=" + locale);
305};
306
307dojo.i18n.normalizeLocale = function(/*String?*/locale){
308 // summary:
309 // Returns canonical form of locale, as used by Dojo.
310 //
311 // description:
312 // All variants are case-insensitive and are separated by '-' as specified in [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt).
313 // If no locale is specified, the dojo.locale is returned. dojo.locale is defined by
314 // the user agent's locale unless overridden by djConfig.
315
316 var result = locale ? locale.toLowerCase() : dojo.locale;
317 if(result == "root"){
318 result = "ROOT";
319 }
320 return result; // String
321};
322
323dojo.i18n._requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String?*/availableFlatLocales){
324 // summary:
325 // See dojo.requireLocalization()
326 // description:
327 // Called by the bootstrap, but factored out so that it is only
328 // included in the build when needed.
329
330 var targetLocale = dojo.i18n.normalizeLocale(locale);
331 var bundlePackage = [moduleName, "nls", bundleName].join(".");
332 // NOTE:
333 // When loading these resources, the packaging does not match what is
334 // on disk. This is an implementation detail, as this is just a
335 // private data structure to hold the loaded resources. e.g.
336 // `tests/hello/nls/en-us/salutations.js` is loaded as the object
337 // `tests.hello.nls.salutations.en_us={...}` The structure on disk is
338 // intended to be most convenient for developers and translators, but
339 // in memory it is more logical and efficient to store in a different
340 // order. Locales cannot use dashes, since the resulting path will
341 // not evaluate as valid JS, so we translate them to underscores.
342
343 //Find the best-match locale to load if we have available flat locales.
344 var bestLocale = "";
345 if(availableFlatLocales){
346 var flatLocales = availableFlatLocales.split(",");
347 for(var i = 0; i < flatLocales.length; i++){
348 //Locale must match from start of string.
349 //Using ["indexOf"] so customBase builds do not see
350 //this as a dojo._base.array dependency.
351 if(targetLocale["indexOf"](flatLocales[i]) == 0){
352 if(flatLocales[i].length > bestLocale.length){
353 bestLocale = flatLocales[i];
354 }
355 }
356 }
357 if(!bestLocale){
358 bestLocale = "ROOT";
359 }
360 }
361
362 //See if the desired locale is already loaded.
363 var tempLocale = availableFlatLocales ? bestLocale : targetLocale;
364 var bundle = dojo._loadedModules[bundlePackage];
365 var localizedBundle = null;
366 if(bundle){
367 if(dojo.config.localizationComplete && bundle._built){return;}
368 var jsLoc = tempLocale.replace(/-/g, '_');
369 var translationPackage = bundlePackage+"."+jsLoc;
370 localizedBundle = dojo._loadedModules[translationPackage];
371 }
372
373 if(!localizedBundle){
374 bundle = dojo["provide"](bundlePackage);
375 var syms = dojo._getModuleSymbols(moduleName);
376 var modpath = syms.concat("nls").join("/");
377 var parent;
378
379 dojo.i18n._searchLocalePath(tempLocale, availableFlatLocales, function(loc){
380 var jsLoc = loc.replace(/-/g, '_');
381 var translationPackage = bundlePackage + "." + jsLoc;
382 var loaded = false;
383 if(!dojo._loadedModules[translationPackage]){
384 // Mark loaded whether it's found or not, so that further load attempts will not be made
385 dojo["provide"](translationPackage);
386 var module = [modpath];
387 if(loc != "ROOT"){module.push(loc);}
388 module.push(bundleName);
389 var filespec = module.join("/") + '.js';
390 loaded = dojo._loadPath(filespec, null, function(hash){
391 // Use singleton with prototype to point to parent bundle, then mix-in result from loadPath
392 var clazz = function(){};
393 clazz.prototype = parent;
394 bundle[jsLoc] = new clazz();
395 for(var j in hash){ bundle[jsLoc][j] = hash[j]; }
396 });
397 }else{
398 loaded = true;
399 }
400 if(loaded && bundle[jsLoc]){
401 parent = bundle[jsLoc];
402 }else{
403 bundle[jsLoc] = parent;
404 }
405
406 if(availableFlatLocales){
407 //Stop the locale path searching if we know the availableFlatLocales, since
408 //the first call to this function will load the only bundle that is needed.
409 return true;
410 }
411 });
412 }
413
414 //Save the best locale bundle as the target locale bundle when we know the
415 //the available bundles.
416 if(availableFlatLocales && targetLocale != bestLocale){
417 bundle[targetLocale.replace(/-/g, '_')] = bundle[bestLocale.replace(/-/g, '_')];
418 }
419};
420
421(function(){
422 // If other locales are used, dojo.requireLocalization should load them as
423 // well, by default.
424 //
425 // Override dojo.requireLocalization to do load the default bundle, then
426 // iterate through the extraLocale list and load those translations as
427 // well, unless a particular locale was requested.
428
429 var extra = dojo.config.extraLocale;
430 if(extra){
431 if(!extra instanceof Array){
432 extra = [extra];
433 }
434
435 var req = dojo.i18n._requireLocalization;
436 dojo.i18n._requireLocalization = function(m, b, locale, availableFlatLocales){
437 req(m,b,locale, availableFlatLocales);
438 if(locale){return;}
439 for(var i=0; i<extra.length; i++){
440 req(m,b,extra[i], availableFlatLocales);
441 }
442 };
443 }
444})();
445
446dojo.i18n._searchLocalePath = function(/*String*/locale, /*Boolean*/down, /*Function*/searchFunc){
447 // summary:
448 // A helper method to assist in searching for locale-based resources.
449 // Will iterate through the variants of a particular locale, either up
450 // or down, executing a callback function. For example, "en-us" and
451 // true will try "en-us" followed by "en" and finally "ROOT".
452
453 locale = dojo.i18n.normalizeLocale(locale);
454
455 var elements = locale.split('-');
456 var searchlist = [];
457 for(var i = elements.length; i > 0; i--){
458 searchlist.push(elements.slice(0, i).join('-'));
459 }
460 searchlist.push(false);
461 if(down){searchlist.reverse();}
462
463 for(var j = searchlist.length - 1; j >= 0; j--){
464 var loc = searchlist[j] || "ROOT";
465 var stop = searchFunc(loc);
466 if(stop){ break; }
467 }
468};
469
470dojo.i18n._preloadLocalizations = function(/*String*/bundlePrefix, /*Array*/localesGenerated){
471 // summary:
472 // Load built, flattened resource bundles, if available for all
473 // locales used in the page. Only called by built layer files.
474
475 function preload(locale){
476 locale = dojo.i18n.normalizeLocale(locale);
477 dojo.i18n._searchLocalePath(locale, true, function(loc){
478 for(var i=0; i<localesGenerated.length;i++){
479 if(localesGenerated[i] == loc){
480 dojo["require"](bundlePrefix+"_"+loc);
481 return true; // Boolean
482 }
483 }
484 return false; // Boolean
485 });
486 }
487 preload();
488 var extra = dojo.config.extraLocale||[];
489 for(var i=0; i<extra.length; i++){
490 preload(extra[i]);
491 }
492};
493
494}
495
496if(!dojo._hasResource["dijit._PaletteMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
497dojo._hasResource["dijit._PaletteMixin"] = true;
498dojo.provide("dijit._PaletteMixin");
499
500
501dojo.declare("dijit._PaletteMixin",
502 [dijit._CssStateMixin],
503 {
504 // summary:
505 // A keyboard accessible palette, for picking a color/emoticon/etc.
506 // description:
507 // A mixin for a grid showing various entities, so the user can pick a certain entity.
508
509 // defaultTimeout: Number
510 // Number of milliseconds before a held key or button becomes typematic
511 defaultTimeout: 500,
512
513 // timeoutChangeRate: Number
514 // Fraction of time used to change the typematic timer between events
515 // 1.0 means that each typematic event fires at defaultTimeout intervals
516 // < 1.0 means that each typematic event fires at an increasing faster rate
517 timeoutChangeRate: 0.90,
518
519 // value: String
520 // Currently selected color/emoticon/etc.
521 value: null,
522
523 // _selectedCell: [private] Integer
524 // Index of the currently selected cell. Initially, none selected
525 _selectedCell: -1,
526
527 // _currentFocus: [private] DomNode
528 // The currently focused cell (if the palette itself has focus), or otherwise
529 // the cell to be focused when the palette itself gets focus.
530 // Different from value, which represents the selected (i.e. clicked) cell.
531/*=====
532 _currentFocus: null,
533=====*/
534
535 // _xDim: [protected] Integer
536 // This is the number of cells horizontally across.
537/*=====
538 _xDim: null,
539=====*/
540
541 // _yDim: [protected] Integer
542 // This is the number of cells vertically down.
543/*=====
544 _yDim: null,
545=====*/
546
547 // tabIndex: String
548 // Widget tab index.
549 tabIndex: "0",
550
551 // cellClass: [protected] String
552 // CSS class applied to each cell in the palette
553 cellClass: "dijitPaletteCell",
554
555 // dyeClass: [protected] String
556 // Name of javascript class for Object created for each cell of the palette.
557 // dyeClass should implements dijit.Dye interface
558 dyeClass: '',
559
560 _preparePalette: function(choices, titles) {
561 // summary:
562 // Subclass must call _preparePalette() from postCreate(), passing in the tooltip
563 // for each cell
564 // choices: String[][]
565 // id's for each cell of the palette, used to create Dye JS object for each cell
566 // titles: String[]
567 // Localized tooltip for each cell
568
569 this._cells = [];
570 var url = this._blankGif;
571
572 var dyeClassObj = dojo.getObject(this.dyeClass);
573
574 for(var row=0; row < choices.length; row++){
575 var rowNode = dojo.create("tr", {tabIndex: "-1"}, this.gridNode);
576 for(var col=0; col < choices[row].length; col++){
577 var value = choices[row][col];
578 if(value){
579 var cellObject = new dyeClassObj(value);
580
581 var cellNode = dojo.create("td", {
582 "class": this.cellClass,
583 tabIndex: "-1",
584 title: titles[value]
585 });
586
587 // prepare cell inner structure
588 cellObject.fillCell(cellNode, url);
589
590 this.connect(cellNode, "ondijitclick", "_onCellClick");
591 this._trackMouseState(cellNode, this.cellClass);
592
593 dojo.place(cellNode, rowNode);
594
595 cellNode.index = this._cells.length;
596
597 // save cell info into _cells
598 this._cells.push({node:cellNode, dye:cellObject});
599 }
600 }
601 }
602 this._xDim = choices[0].length;
603 this._yDim = choices.length;
604
605 // Now set all events
606 // The palette itself is navigated to with the tab key on the keyboard
607 // Keyboard navigation within the Palette is with the arrow keys
608 // Spacebar selects the cell.
609 // For the up key the index is changed by negative the x dimension.
610
611 var keyIncrementMap = {
612 UP_ARROW: -this._xDim,
613 // The down key the index is increase by the x dimension.
614 DOWN_ARROW: this._xDim,
615 // Right and left move the index by 1.
616 RIGHT_ARROW: this.isLeftToRight() ? 1 : -1,
617 LEFT_ARROW: this.isLeftToRight() ? -1 : 1
618 };
619 for(var key in keyIncrementMap){
620 this._connects.push(
621 dijit.typematic.addKeyListener(
622 this.domNode,
623 {charOrCode:dojo.keys[key], ctrlKey:false, altKey:false, shiftKey:false},
624 this,
625 function(){
626 var increment = keyIncrementMap[key];
627 return function(count){ this._navigateByKey(increment, count); };
628 }(),
629 this.timeoutChangeRate,
630 this.defaultTimeout
631 )
632 );
633 }
634 },
635
636 postCreate: function(){
637 this.inherited(arguments);
638
639 // Set initial navigable node.
640 this._setCurrent(this._cells[0].node);
641 },
642
643 focus: function(){
644 // summary:
645 // Focus this widget. Puts focus on the most recently focused cell.
646
647 // The cell already has tabIndex set, just need to set CSS and focus it
648 dijit.focus(this._currentFocus);
649 },
650
651 _onCellClick: function(/*Event*/ evt){
652 // summary:
653 // Handler for click, enter key & space key. Selects the cell.
654 // evt:
655 // The event.
656 // tags:
657 // private
658
659 var target = evt.currentTarget,
660 value = this._getDye(target).getValue();
661
662 // First focus the clicked cell, and then send onChange() notification.
663 // onChange() (via _setValueAttr) must be after the focus call, because
664 // it may trigger a refocus to somewhere else (like the Editor content area), and that
665 // second focus should win.
666 // Use setTimeout because IE doesn't like changing focus inside of an event handler.
667 this._setCurrent(target);
668 setTimeout(dojo.hitch(this, function(){
669 dijit.focus(target);
670 this._setValueAttr(value, true);
671 }));
672
673 // workaround bug where hover class is not removed on popup because the popup is
674 // closed and then there's no onblur event on the cell
675 dojo.removeClass(target, "dijitPaletteCellHover");
676
677 dojo.stopEvent(evt);
678 },
679
680 _setCurrent: function(/*DomNode*/ node){
681 // summary:
682 // Sets which node is the focused cell.
683 // description:
684 // At any point in time there's exactly one
685 // cell with tabIndex != -1. If focus is inside the palette then
686 // focus is on that cell.
687 //
688 // After calling this method, arrow key handlers and mouse click handlers
689 // should focus the cell in a setTimeout().
690 // tags:
691 // protected
692 if("_currentFocus" in this){
693 // Remove tabIndex on old cell
694 dojo.attr(this._currentFocus, "tabIndex", "-1");
695 }
696
697 // Set tabIndex of new cell
698 this._currentFocus = node;
699 if(node){
700 dojo.attr(node, "tabIndex", this.tabIndex);
701 }
702 },
703
704 _setValueAttr: function(value, priorityChange){
705 // summary:
706 // This selects a cell. It triggers the onChange event.
707 // value: String value of the cell to select
708 // tags:
709 // protected
710 // priorityChange:
711 // Optional parameter used to tell the select whether or not to fire
712 // onChange event.
713
714 // clear old value and selected cell
715 this.value = null;
716 if(this._selectedCell >= 0){
717 dojo.removeClass(this._cells[this._selectedCell].node, "dijitPaletteCellSelected");
718 }
719 this._selectedCell = -1;
720
721 // search for cell matching specified value
722 if(value){
723 for(var i = 0; i < this._cells.length; i++){
724 if(value == this._cells[i].dye.getValue()){
725 this._selectedCell = i;
726 this.value = value;
727
728 dojo.addClass(this._cells[i].node, "dijitPaletteCellSelected");
729
730 if(priorityChange || priorityChange === undefined){
731 this.onChange(value);
732 }
733
734 break;
735 }
736 }
737 }
738 },
739
740 onChange: function(value){
741 // summary:
742 // Callback when a cell is selected.
743 // value: String
744 // Value corresponding to cell.
745 },
746
747 _navigateByKey: function(increment, typeCount){
748 // summary:
749 // This is the callback for typematic.
750 // It changes the focus and the highlighed cell.
751 // increment:
752 // How much the key is navigated.
753 // typeCount:
754 // How many times typematic has fired.
755 // tags:
756 // private
757
758 // typecount == -1 means the key is released.
759 if(typeCount == -1){ return; }
760
761 var newFocusIndex = this._currentFocus.index + increment;
762 if(newFocusIndex < this._cells.length && newFocusIndex > -1){
763 var focusNode = this._cells[newFocusIndex].node;
764 this._setCurrent(focusNode);
765
766 // Actually focus the node, for the benefit of screen readers.
767 // Use setTimeout because IE doesn't like changing focus inside of an event handler
768 setTimeout(dojo.hitch(dijit, "focus", focusNode), 0);
769 }
770 },
771
772 _getDye: function(/*DomNode*/ cell){
773 // summary:
774 // Get JS object for given cell DOMNode
775
776 return this._cells[cell.index].dye;
777 }
778});
779
780/*=====
781dojo.declare("dijit.Dye",
782 null,
783 {
784 // summary:
785 // Interface for the JS Object associated with a palette cell (i.e. DOMNode)
786
787 constructor: function(alias){
788 // summary:
789 // Initialize according to value or alias like "white"
790 // alias: String
791 },
792
793 getValue: function(){
794 // summary:
795 // Return "value" of cell; meaning of "value" varies by subclass.
796 // description:
797 // For example color hex value, emoticon ascii value etc, entity hex value.
798 },
799
800 fillCell: function(cell, blankGif){
801 // summary:
802 // Add cell DOMNode inner structure
803 // cell: DomNode
804 // The surrounding cell
805 // blankGif: String
806 // URL for blank cell image
807 }
808 }
809);
810=====*/
811
812}
813
814if(!dojo._hasResource["dijit.ColorPalette"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
815dojo._hasResource["dijit.ColorPalette"] = true;
816dojo.provide("dijit.ColorPalette");
817
818
819
820
821
822
823
824
825
826
827dojo.declare("dijit.ColorPalette",
828 [dijit._Widget, dijit._Templated, dijit._PaletteMixin],
829 {
830 // summary:
831 // A keyboard accessible color-picking widget
832 // description:
833 // Grid showing various colors, so the user can pick a certain color.
834 // Can be used standalone, or as a popup.
835 //
836 // example:
837 // | <div dojoType="dijit.ColorPalette"></div>
838 //
839 // example:
840 // | var picker = new dijit.ColorPalette({ },srcNode);
841 // | picker.startup();
842
843
844 // palette: String
845 // Size of grid, either "7x10" or "3x4".
846 palette: "7x10",
847
848 // _palettes: [protected] Map
849 // This represents the value of the colors.
850 // The first level is a hashmap of the different palettes available.
851 // The next two dimensions represent the columns and rows of colors.
852 _palettes: {
853 "7x10": [["white", "seashell", "cornsilk", "lemonchiffon","lightyellow", "palegreen", "paleturquoise", "lightcyan", "lavender", "plum"],
854 ["lightgray", "pink", "bisque", "moccasin", "khaki", "lightgreen", "lightseagreen", "lightskyblue", "cornflowerblue", "violet"],
855 ["silver", "lightcoral", "sandybrown", "orange", "palegoldenrod", "chartreuse", "mediumturquoise", "skyblue", "mediumslateblue","orchid"],
856 ["gray", "red", "orangered", "darkorange", "yellow", "limegreen", "darkseagreen", "royalblue", "slateblue", "mediumorchid"],
857 ["dimgray", "crimson", "chocolate", "coral", "gold", "forestgreen", "seagreen", "blue", "blueviolet", "darkorchid"],
858 ["darkslategray","firebrick","saddlebrown", "sienna", "olive", "green", "darkcyan", "mediumblue","darkslateblue", "darkmagenta" ],
859 ["black", "darkred", "maroon", "brown", "darkolivegreen", "darkgreen", "midnightblue", "navy", "indigo", "purple"]],
860
861 "3x4": [["white", "lime", "green", "blue"],
862 ["silver", "yellow", "fuchsia", "navy"],
863 ["gray", "red", "purple", "black"]]
864 },
865
866 // _imagePaths: [protected] Map
867 // This is stores the path to the palette images
868 _imagePaths: {
869 "7x10": dojo.moduleUrl("dijit.themes", "a11y/colors7x10.png"),
870 "3x4": dojo.moduleUrl("dijit.themes", "a11y/colors3x4.png"),
871 "7x10-rtl": dojo.moduleUrl("dijit.themes", "a11y/colors7x10-rtl.png"),
872 "3x4-rtl": dojo.moduleUrl("dijit.themes", "a11y/colors3x4-rtl.png")
873 },
874
875 // templateString: String
876 // The template of this widget.
877 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"),
878
879 baseClass: "dijitColorPalette",
880
881 dyeClass: 'dijit._Color',
882
883 buildRendering: function(){
884 // Instantiate the template, which makes a skeleton into which we'll insert a bunch of
885 // <img> nodes
886
887 this.inherited(arguments);
888
889 this.imageNode.setAttribute("src", this._imagePaths[this.palette + (this.isLeftToRight() ? "" : "-rtl")].toString());
890
891 var i18nColorNames = dojo.i18n.getLocalization("dojo", "colors", this.lang);
892 this._preparePalette(
893 this._palettes[this.palette],
894 i18nColorNames
895 );
896 }
897});
898
899dojo.declare("dijit._Color", dojo.Color,
900 // summary:
901 // Object associated with each cell in a ColorPalette palette.
902 // Implements dijit.Dye.
903 {
904 constructor: function(/*String*/alias){
905 this._alias = alias;
906 this.setColor(dojo.Color.named[alias]);
907 },
908
909 getValue: function(){
910 // summary:
911 // Note that although dijit._Color is initialized with a value like "white" getValue() always
912 // returns a hex value
913 return this.toHex();
914 },
915
916 fillCell: function(/*DOMNode*/ cell, /*String*/ blankGif){
917 dojo.create("img", {
918 src: blankGif,
919 "class": "dijitPaletteImg",
920 alt: this._alias
921 }, cell);
922 }
923 }
924);
925
926}
927
928if(!dojo._hasResource["dijit.Declaration"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
929dojo._hasResource["dijit.Declaration"] = true;
930dojo.provide("dijit.Declaration");
931
932
933
934dojo.declare(
935 "dijit.Declaration",
936 dijit._Widget,
937 {
938 // summary:
939 // The Declaration widget allows a developer to declare new widget
940 // classes directly from a snippet of markup.
941
942 // _noScript: [private] Boolean
943 // Flag to parser to leave alone the script tags contained inside of me
944 _noScript: true,
945
946 // widgetClass: String
947 // Name of class being declared, ex: "acme.myWidget"
948 widgetClass: "",
949
950 // propList: Object
951 // Set of attributes for this widget along with default values, ex:
952 // {delay: 100, title: "hello world"}
953 defaults: null,
954
955 // mixins: String[]
956 // List containing the prototype for this widget, and also any mixins,
957 // ex: ["dijit._Widget", "dijit._Container"]
958 mixins: [],
959
960 buildRendering: function(){
961 var src = this.srcNodeRef.parentNode.removeChild(this.srcNodeRef),
962 methods = dojo.query("> script[type^='dojo/method'][event]", src).orphan(),
963 postscriptConnects = dojo.query("> script[type^='dojo/method']", src).orphan(),
964 regularConnects = dojo.query("> script[type^='dojo/connect']", src).orphan(),
965 srcType = src.nodeName;
966
967 var propList = this.defaults || {};
968
969 // For all methods defined like <script type="dojo/method" event="foo">,
970 // add that method to prototype
971 dojo.forEach(methods, function(s){
972 var evt = s.getAttribute("event"),
973 func = dojo.parser._functionFromScript(s);
974 propList[evt] = func;
975 });
976
977 // map array of strings like [ "dijit.form.Button" ] to array of mixin objects
978 // (note that dojo.map(this.mixins, dojo.getObject) doesn't work because it passes
979 // a bogus third argument to getObject(), confusing it)
980 this.mixins = this.mixins.length ?
981 dojo.map(this.mixins, function(name){ return dojo.getObject(name); } ) :
982 [ dijit._Widget, dijit._Templated ];
983
984 propList.widgetsInTemplate = true;
985 propList._skipNodeCache = true;
986 propList.templateString = "<"+srcType+" class='"+src.className+"' dojoAttachPoint='"+(src.getAttribute("dojoAttachPoint") || '')+"' dojoAttachEvent='"+(src.getAttribute("dojoAttachEvent") || '')+"' >"+src.innerHTML.replace(/\%7B/g,"{").replace(/\%7D/g,"}")+"</"+srcType+">";
987
988 // strip things so we don't create stuff under us in the initial setup phase
989 dojo.query("[dojoType]", src).forEach(function(node){
990 node.removeAttribute("dojoType");
991 });
992
993 // create the new widget class
994 var wc = dojo.declare(
995 this.widgetClass,
996 this.mixins,
997 propList
998 );
999
1000 // Handle <script> blocks of form:
1001 // <script type="dojo/connect" event="foo">
1002 // and
1003 // <script type="dojo/method">
1004 // (Note that the second one is just shorthand for a dojo/connect to postscript)
1005 // Since this is a connect in the declaration, we are actually connection to the method
1006 // in the _prototype_.
1007 var connects = regularConnects.concat(postscriptConnects);
1008 dojo.forEach(connects, function(s){
1009 var evt = s.getAttribute("event") || "postscript",
1010 func = dojo.parser._functionFromScript(s);
1011 dojo.connect(wc.prototype, evt, func);
1012 });
1013 }
1014 }
1015);
1016
1017}
1018
1019if(!dojo._hasResource["dojo.dnd.common"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1020dojo._hasResource["dojo.dnd.common"] = true;
1021dojo.provide("dojo.dnd.common");
1022
1023dojo.dnd.getCopyKeyState = dojo.isCopyKey;
1024
1025dojo.dnd._uniqueId = 0;
1026dojo.dnd.getUniqueId = function(){
1027 // summary:
1028 // returns a unique string for use with any DOM element
1029 var id;
1030 do{
1031 id = dojo._scopeName + "Unique" + (++dojo.dnd._uniqueId);
1032 }while(dojo.byId(id));
1033 return id;
1034};
1035
1036dojo.dnd._empty = {};
1037
1038dojo.dnd.isFormElement = function(/*Event*/ e){
1039 // summary:
1040 // returns true if user clicked on a form element
1041 var t = e.target;
1042 if(t.nodeType == 3 /*TEXT_NODE*/){
1043 t = t.parentNode;
1044 }
1045 return " button textarea input select option ".indexOf(" " + t.tagName.toLowerCase() + " ") >= 0; // Boolean
1046};
1047
1048}
1049
1050if(!dojo._hasResource["dojo.dnd.autoscroll"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1051dojo._hasResource["dojo.dnd.autoscroll"] = true;
1052dojo.provide("dojo.dnd.autoscroll");
1053
1054dojo.dnd.getViewport = function(){
1055 // summary:
1056 // Returns a viewport size (visible part of the window)
1057
1058 // TODO: remove this when getViewport() moved to dojo core, see #7028
1059
1060 // FIXME: need more docs!!
1061 var d = dojo.doc, dd = d.documentElement, w = window, b = dojo.body();
1062 if(dojo.isMozilla){
1063 return {w: dd.clientWidth, h: w.innerHeight}; // Object
1064 }else if(!dojo.isOpera && w.innerWidth){
1065 return {w: w.innerWidth, h: w.innerHeight}; // Object
1066 }else if (!dojo.isOpera && dd && dd.clientWidth){
1067 return {w: dd.clientWidth, h: dd.clientHeight}; // Object
1068 }else if (b.clientWidth){
1069 return {w: b.clientWidth, h: b.clientHeight}; // Object
1070 }
1071 return null; // Object
1072};
1073
1074dojo.dnd.V_TRIGGER_AUTOSCROLL = 32;
1075dojo.dnd.H_TRIGGER_AUTOSCROLL = 32;
1076
1077dojo.dnd.V_AUTOSCROLL_VALUE = 16;
1078dojo.dnd.H_AUTOSCROLL_VALUE = 16;
1079
1080dojo.dnd.autoScroll = function(e){
1081 // summary:
1082 // a handler for onmousemove event, which scrolls the window, if
1083 // necesary
1084 // e: Event
1085 // onmousemove event
1086
1087 // FIXME: needs more docs!
1088 var v = dojo.dnd.getViewport(), dx = 0, dy = 0;
1089 if(e.clientX < dojo.dnd.H_TRIGGER_AUTOSCROLL){
1090 dx = -dojo.dnd.H_AUTOSCROLL_VALUE;
1091 }else if(e.clientX > v.w - dojo.dnd.H_TRIGGER_AUTOSCROLL){
1092 dx = dojo.dnd.H_AUTOSCROLL_VALUE;
1093 }
1094 if(e.clientY < dojo.dnd.V_TRIGGER_AUTOSCROLL){
1095 dy = -dojo.dnd.V_AUTOSCROLL_VALUE;
1096 }else if(e.clientY > v.h - dojo.dnd.V_TRIGGER_AUTOSCROLL){
1097 dy = dojo.dnd.V_AUTOSCROLL_VALUE;
1098 }
1099 window.scrollBy(dx, dy);
1100};
1101
1102dojo.dnd._validNodes = {"div": 1, "p": 1, "td": 1};
1103dojo.dnd._validOverflow = {"auto": 1, "scroll": 1};
1104
1105dojo.dnd.autoScrollNodes = function(e){
1106 // summary:
1107 // a handler for onmousemove event, which scrolls the first avaialble
1108 // Dom element, it falls back to dojo.dnd.autoScroll()
1109 // e: Event
1110 // onmousemove event
1111
1112 // FIXME: needs more docs!
1113 for(var n = e.target; n;){
1114 if(n.nodeType == 1 && (n.tagName.toLowerCase() in dojo.dnd._validNodes)){
1115 var s = dojo.getComputedStyle(n);
1116 if(s.overflow.toLowerCase() in dojo.dnd._validOverflow){
1117 var b = dojo._getContentBox(n, s), t = dojo.position(n, true);
1118 //console.log(b.l, b.t, t.x, t.y, n.scrollLeft, n.scrollTop);
1119 var w = Math.min(dojo.dnd.H_TRIGGER_AUTOSCROLL, b.w / 2),
1120 h = Math.min(dojo.dnd.V_TRIGGER_AUTOSCROLL, b.h / 2),
1121 rx = e.pageX - t.x, ry = e.pageY - t.y, dx = 0, dy = 0;
1122 if(dojo.isWebKit || dojo.isOpera){
1123 // FIXME: this code should not be here, it should be taken into account
1124 // either by the event fixing code, or the dojo.position()
1125 // FIXME: this code doesn't work on Opera 9.5 Beta
1126 rx += dojo.body().scrollLeft, ry += dojo.body().scrollTop;
1127 }
1128 if(rx > 0 && rx < b.w){
1129 if(rx < w){
1130 dx = -w;
1131 }else if(rx > b.w - w){
1132 dx = w;
1133 }
1134 }
1135 //console.log("ry =", ry, "b.h =", b.h, "h =", h);
1136 if(ry > 0 && ry < b.h){
1137 if(ry < h){
1138 dy = -h;
1139 }else if(ry > b.h - h){
1140 dy = h;
1141 }
1142 }
1143 var oldLeft = n.scrollLeft, oldTop = n.scrollTop;
1144 n.scrollLeft = n.scrollLeft + dx;
1145 n.scrollTop = n.scrollTop + dy;
1146 if(oldLeft != n.scrollLeft || oldTop != n.scrollTop){ return; }
1147 }
1148 }
1149 try{
1150 n = n.parentNode;
1151 }catch(x){
1152 n = null;
1153 }
1154 }
1155 dojo.dnd.autoScroll(e);
1156};
1157
1158}
1159
1160if(!dojo._hasResource["dojo.dnd.Mover"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1161dojo._hasResource["dojo.dnd.Mover"] = true;
1162dojo.provide("dojo.dnd.Mover");
1163
1164
1165
1166
1167dojo.declare("dojo.dnd.Mover", null, {
1168 constructor: function(node, e, host){
1169 // summary:
1170 // an object, which makes a node follow the mouse.
1171 // Used as a default mover, and as a base class for custom movers.
1172 // node: Node
1173 // a node (or node's id) to be moved
1174 // e: Event
1175 // a mouse event, which started the move;
1176 // only pageX and pageY properties are used
1177 // host: Object?
1178 // object which implements the functionality of the move,
1179 // and defines proper events (onMoveStart and onMoveStop)
1180 this.node = dojo.byId(node);
1181 this.marginBox = {l: e.pageX, t: e.pageY};
1182 this.mouseButton = e.button;
1183 var h = this.host = host, d = node.ownerDocument,
1184 firstEvent = dojo.connect(d, "onmousemove", this, "onFirstMove");
1185 this.events = [
1186 dojo.connect(d, "onmousemove", this, "onMouseMove"),
1187 dojo.connect(d, "onmouseup", this, "onMouseUp"),
1188 // cancel text selection and text dragging
1189 dojo.connect(d, "ondragstart", dojo.stopEvent),
1190 dojo.connect(d.body, "onselectstart", dojo.stopEvent),
1191 firstEvent
1192 ];
1193 // notify that the move has started
1194 if(h && h.onMoveStart){
1195 h.onMoveStart(this);
1196 }
1197 },
1198 // mouse event processors
1199 onMouseMove: function(e){
1200 // summary:
1201 // event processor for onmousemove
1202 // e: Event
1203 // mouse event
1204 dojo.dnd.autoScroll(e);
1205 var m = this.marginBox;
1206 this.host.onMove(this, {l: m.l + e.pageX, t: m.t + e.pageY}, e);
1207 dojo.stopEvent(e);
1208 },
1209 onMouseUp: function(e){
1210 if(dojo.isWebKit && dojo.isMac && this.mouseButton == 2 ?
1211 e.button == 0 : this.mouseButton == e.button){
1212 this.destroy();
1213 }
1214 dojo.stopEvent(e);
1215 },
1216 // utilities
1217 onFirstMove: function(e){
1218 // summary:
1219 // makes the node absolute; it is meant to be called only once.
1220 // relative and absolutely positioned nodes are assumed to use pixel units
1221 var s = this.node.style, l, t, h = this.host;
1222 switch(s.position){
1223 case "relative":
1224 case "absolute":
1225 // assume that left and top values are in pixels already
1226 l = Math.round(parseFloat(s.left)) || 0;
1227 t = Math.round(parseFloat(s.top)) || 0;
1228 break;
1229 default:
1230 s.position = "absolute"; // enforcing the absolute mode
1231 var m = dojo.marginBox(this.node);
1232 // event.pageX/pageY (which we used to generate the initial
1233 // margin box) includes padding and margin set on the body.
1234 // However, setting the node's position to absolute and then
1235 // doing dojo.marginBox on it *doesn't* take that additional
1236 // space into account - so we need to subtract the combined
1237 // padding and margin. We use getComputedStyle and
1238 // _getMarginBox/_getContentBox to avoid the extra lookup of
1239 // the computed style.
1240 var b = dojo.doc.body;
1241 var bs = dojo.getComputedStyle(b);
1242 var bm = dojo._getMarginBox(b, bs);
1243 var bc = dojo._getContentBox(b, bs);
1244 l = m.l - (bc.l - bm.l);
1245 t = m.t - (bc.t - bm.t);
1246 break;
1247 }
1248 this.marginBox.l = l - this.marginBox.l;
1249 this.marginBox.t = t - this.marginBox.t;
1250 if(h && h.onFirstMove){
1251 h.onFirstMove(this, e);
1252 }
1253 dojo.disconnect(this.events.pop());
1254 },
1255 destroy: function(){
1256 // summary:
1257 // stops the move, deletes all references, so the object can be garbage-collected
1258 dojo.forEach(this.events, dojo.disconnect);
1259 // undo global settings
1260 var h = this.host;
1261 if(h && h.onMoveStop){
1262 h.onMoveStop(this);
1263 }
1264 // destroy objects
1265 this.events = this.node = this.host = null;
1266 }
1267});
1268
1269}
1270
1271if(!dojo._hasResource["dojo.dnd.Moveable"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1272dojo._hasResource["dojo.dnd.Moveable"] = true;
1273dojo.provide("dojo.dnd.Moveable");
1274
1275
1276
1277/*=====
1278dojo.declare("dojo.dnd.__MoveableArgs", [], {
1279 // handle: Node||String
1280 // A node (or node's id), which is used as a mouse handle.
1281 // If omitted, the node itself is used as a handle.
1282 handle: null,
1283
1284 // delay: Number
1285 // delay move by this number of pixels
1286 delay: 0,
1287
1288 // skip: Boolean
1289 // skip move of form elements
1290 skip: false,
1291
1292 // mover: Object
1293 // a constructor of custom Mover
1294 mover: dojo.dnd.Mover
1295});
1296=====*/
1297
1298dojo.declare("dojo.dnd.Moveable", null, {
1299 // object attributes (for markup)
1300 handle: "",
1301 delay: 0,
1302 skip: false,
1303
1304 constructor: function(node, params){
1305 // summary:
1306 // an object, which makes a node moveable
1307 // node: Node
1308 // a node (or node's id) to be moved
1309 // params: dojo.dnd.__MoveableArgs?
1310 // optional parameters
1311 this.node = dojo.byId(node);
1312 if(!params){ params = {}; }
1313 this.handle = params.handle ? dojo.byId(params.handle) : null;
1314 if(!this.handle){ this.handle = this.node; }
1315 this.delay = params.delay > 0 ? params.delay : 0;
1316 this.skip = params.skip;
1317 this.mover = params.mover ? params.mover : dojo.dnd.Mover;
1318 this.events = [
1319 dojo.connect(this.handle, "onmousedown", this, "onMouseDown"),
1320 // cancel text selection and text dragging
1321 dojo.connect(this.handle, "ondragstart", this, "onSelectStart"),
1322 dojo.connect(this.handle, "onselectstart", this, "onSelectStart")
1323 ];
1324 },
1325
1326 // markup methods
1327 markupFactory: function(params, node){
1328 return new dojo.dnd.Moveable(node, params);
1329 },
1330
1331 // methods
1332 destroy: function(){
1333 // summary:
1334 // stops watching for possible move, deletes all references, so the object can be garbage-collected
1335 dojo.forEach(this.events, dojo.disconnect);
1336 this.events = this.node = this.handle = null;
1337 },
1338
1339 // mouse event processors
1340 onMouseDown: function(e){
1341 // summary:
1342 // event processor for onmousedown, creates a Mover for the node
1343 // e: Event
1344 // mouse event
1345 if(this.skip && dojo.dnd.isFormElement(e)){ return; }
1346 if(this.delay){
1347 this.events.push(
1348 dojo.connect(this.handle, "onmousemove", this, "onMouseMove"),
1349 dojo.connect(this.handle, "onmouseup", this, "onMouseUp")
1350 );
1351 this._lastX = e.pageX;
1352 this._lastY = e.pageY;
1353 }else{
1354 this.onDragDetected(e);
1355 }
1356 dojo.stopEvent(e);
1357 },
1358 onMouseMove: function(e){
1359 // summary:
1360 // event processor for onmousemove, used only for delayed drags
1361 // e: Event
1362 // mouse event
1363 if(Math.abs(e.pageX - this._lastX) > this.delay || Math.abs(e.pageY - this._lastY) > this.delay){
1364 this.onMouseUp(e);
1365 this.onDragDetected(e);
1366 }
1367 dojo.stopEvent(e);
1368 },
1369 onMouseUp: function(e){
1370 // summary:
1371 // event processor for onmouseup, used only for delayed drags
1372 // e: Event
1373 // mouse event
1374 for(var i = 0; i < 2; ++i){
1375 dojo.disconnect(this.events.pop());
1376 }
1377 dojo.stopEvent(e);
1378 },
1379 onSelectStart: function(e){
1380 // summary:
1381 // event processor for onselectevent and ondragevent
1382 // e: Event
1383 // mouse event
1384 if(!this.skip || !dojo.dnd.isFormElement(e)){
1385 dojo.stopEvent(e);
1386 }
1387 },
1388
1389 // local events
1390 onDragDetected: function(/* Event */ e){
1391 // summary:
1392 // called when the drag is detected;
1393 // responsible for creation of the mover
1394 new this.mover(this.node, e, this);
1395 },
1396 onMoveStart: function(/* dojo.dnd.Mover */ mover){
1397 // summary:
1398 // called before every move operation
1399 dojo.publish("/dnd/move/start", [mover]);
1400 dojo.addClass(dojo.body(), "dojoMove");
1401 dojo.addClass(this.node, "dojoMoveItem");
1402 },
1403 onMoveStop: function(/* dojo.dnd.Mover */ mover){
1404 // summary:
1405 // called after every move operation
1406 dojo.publish("/dnd/move/stop", [mover]);
1407 dojo.removeClass(dojo.body(), "dojoMove");
1408 dojo.removeClass(this.node, "dojoMoveItem");
1409 },
1410 onFirstMove: function(/* dojo.dnd.Mover */ mover, /* Event */ e){
1411 // summary:
1412 // called during the very first move notification;
1413 // can be used to initialize coordinates, can be overwritten.
1414
1415 // default implementation does nothing
1416 },
1417 onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop, /* Event */ e){
1418 // summary:
1419 // called during every move notification;
1420 // should actually move the node; can be overwritten.
1421 this.onMoving(mover, leftTop);
1422 var s = mover.node.style;
1423 s.left = leftTop.l + "px";
1424 s.top = leftTop.t + "px";
1425 this.onMoved(mover, leftTop);
1426 },
1427 onMoving: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
1428 // summary:
1429 // called before every incremental move; can be overwritten.
1430
1431 // default implementation does nothing
1432 },
1433 onMoved: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
1434 // summary:
1435 // called after every incremental move; can be overwritten.
1436
1437 // default implementation does nothing
1438 }
1439});
1440
1441}
1442
1443if(!dojo._hasResource["dojo.dnd.move"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1444dojo._hasResource["dojo.dnd.move"] = true;
1445dojo.provide("dojo.dnd.move");
1446
1447
1448
1449
1450/*=====
1451dojo.declare("dojo.dnd.move.__constrainedMoveableArgs", [dojo.dnd.__MoveableArgs], {
1452 // constraints: Function
1453 // Calculates a constraint box.
1454 // It is called in a context of the moveable object.
1455 constraints: function(){},
1456
1457 // within: Boolean
1458 // restrict move within boundaries.
1459 within: false
1460});
1461=====*/
1462
1463dojo.declare("dojo.dnd.move.constrainedMoveable", dojo.dnd.Moveable, {
1464 // object attributes (for markup)
1465 constraints: function(){},
1466 within: false,
1467
1468 // markup methods
1469 markupFactory: function(params, node){
1470 return new dojo.dnd.move.constrainedMoveable(node, params);
1471 },
1472
1473 constructor: function(node, params){
1474 // summary:
1475 // an object that makes a node moveable
1476 // node: Node
1477 // a node (or node's id) to be moved
1478 // params: dojo.dnd.move.__constrainedMoveableArgs?
1479 // an optional object with additional parameters;
1480 // the rest is passed to the base class
1481 if(!params){ params = {}; }
1482 this.constraints = params.constraints;
1483 this.within = params.within;
1484 },
1485 onFirstMove: function(/* dojo.dnd.Mover */ mover){
1486 // summary:
1487 // called during the very first move notification;
1488 // can be used to initialize coordinates, can be overwritten.
1489 var c = this.constraintBox = this.constraints.call(this, mover);
1490 c.r = c.l + c.w;
1491 c.b = c.t + c.h;
1492 if(this.within){
1493 var mb = dojo.marginBox(mover.node);
1494 c.r -= mb.w;
1495 c.b -= mb.h;
1496 }
1497 },
1498 onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
1499 // summary:
1500 // called during every move notification;
1501 // should actually move the node; can be overwritten.
1502 var c = this.constraintBox, s = mover.node.style;
1503 s.left = (leftTop.l < c.l ? c.l : c.r < leftTop.l ? c.r : leftTop.l) + "px";
1504 s.top = (leftTop.t < c.t ? c.t : c.b < leftTop.t ? c.b : leftTop.t) + "px";
1505 }
1506});
1507
1508/*=====
1509dojo.declare("dojo.dnd.move.__boxConstrainedMoveableArgs", [dojo.dnd.move.__constrainedMoveableArgs], {
1510 // box: Object
1511 // a constraint box
1512 box: {}
1513});
1514=====*/
1515
1516dojo.declare("dojo.dnd.move.boxConstrainedMoveable", dojo.dnd.move.constrainedMoveable, {
1517 // box:
1518 // object attributes (for markup)
1519 box: {},
1520
1521 // markup methods
1522 markupFactory: function(params, node){
1523 return new dojo.dnd.move.boxConstrainedMoveable(node, params);
1524 },
1525
1526 constructor: function(node, params){
1527 // summary:
1528 // an object, which makes a node moveable
1529 // node: Node
1530 // a node (or node's id) to be moved
1531 // params: dojo.dnd.move.__boxConstrainedMoveableArgs?
1532 // an optional object with parameters
1533 var box = params && params.box;
1534 this.constraints = function(){ return box; };
1535 }
1536});
1537
1538/*=====
1539dojo.declare("dojo.dnd.move.__parentConstrainedMoveableArgs", [dojo.dnd.move.__constrainedMoveableArgs], {
1540 // area: String
1541 // A parent's area to restrict the move.
1542 // Can be "margin", "border", "padding", or "content".
1543 area: ""
1544});
1545=====*/
1546
1547dojo.declare("dojo.dnd.move.parentConstrainedMoveable", dojo.dnd.move.constrainedMoveable, {
1548 // area:
1549 // object attributes (for markup)
1550 area: "content",
1551
1552 // markup methods
1553 markupFactory: function(params, node){
1554 return new dojo.dnd.move.parentConstrainedMoveable(node, params);
1555 },
1556
1557 constructor: function(node, params){
1558 // summary:
1559 // an object, which makes a node moveable
1560 // node: Node
1561 // a node (or node's id) to be moved
1562 // params: dojo.dnd.move.__parentConstrainedMoveableArgs?
1563 // an optional object with parameters
1564 var area = params && params.area;
1565 this.constraints = function(){
1566 var n = this.node.parentNode,
1567 s = dojo.getComputedStyle(n),
1568 mb = dojo._getMarginBox(n, s);
1569 if(area == "margin"){
1570 return mb; // Object
1571 }
1572 var t = dojo._getMarginExtents(n, s);
1573 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
1574 if(area == "border"){
1575 return mb; // Object
1576 }
1577 t = dojo._getBorderExtents(n, s);
1578 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
1579 if(area == "padding"){
1580 return mb; // Object
1581 }
1582 t = dojo._getPadExtents(n, s);
1583 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
1584 return mb; // Object
1585 };
1586 }
1587});
1588
1589// WARNING: below are obsolete objects, instead of custom movers use custom moveables (above)
1590
1591dojo.dnd.move.constrainedMover = function(fun, within){
1592 // summary:
1593 // returns a constrained version of dojo.dnd.Mover
1594 // description:
1595 // this function produces n object, which will put a constraint on
1596 // the margin box of dragged object in absolute coordinates
1597 // fun: Function
1598 // called on drag, and returns a constraint box
1599 // within: Boolean
1600 // if true, constraints the whole dragged object withtin the rectangle,
1601 // otherwise the constraint is applied to the left-top corner
1602
1603 dojo.deprecated("dojo.dnd.move.constrainedMover, use dojo.dnd.move.constrainedMoveable instead");
1604 var mover = function(node, e, notifier){
1605 dojo.dnd.Mover.call(this, node, e, notifier);
1606 };
1607 dojo.extend(mover, dojo.dnd.Mover.prototype);
1608 dojo.extend(mover, {
1609 onMouseMove: function(e){
1610 // summary: event processor for onmousemove
1611 // e: Event: mouse event
1612 dojo.dnd.autoScroll(e);
1613 var m = this.marginBox, c = this.constraintBox,
1614 l = m.l + e.pageX, t = m.t + e.pageY;
1615 l = l < c.l ? c.l : c.r < l ? c.r : l;
1616 t = t < c.t ? c.t : c.b < t ? c.b : t;
1617 this.host.onMove(this, {l: l, t: t});
1618 },
1619 onFirstMove: function(){
1620 // summary: called once to initialize things; it is meant to be called only once
1621 dojo.dnd.Mover.prototype.onFirstMove.call(this);
1622 var c = this.constraintBox = fun.call(this);
1623 c.r = c.l + c.w;
1624 c.b = c.t + c.h;
1625 if(within){
1626 var mb = dojo.marginBox(this.node);
1627 c.r -= mb.w;
1628 c.b -= mb.h;
1629 }
1630 }
1631 });
1632 return mover; // Object
1633};
1634
1635dojo.dnd.move.boxConstrainedMover = function(box, within){
1636 // summary:
1637 // a specialization of dojo.dnd.constrainedMover, which constrains to the specified box
1638 // box: Object
1639 // a constraint box (l, t, w, h)
1640 // within: Boolean
1641 // if true, constraints the whole dragged object withtin the rectangle,
1642 // otherwise the constraint is applied to the left-top corner
1643
1644 dojo.deprecated("dojo.dnd.move.boxConstrainedMover, use dojo.dnd.move.boxConstrainedMoveable instead");
1645 return dojo.dnd.move.constrainedMover(function(){ return box; }, within); // Object
1646};
1647
1648dojo.dnd.move.parentConstrainedMover = function(area, within){
1649 // summary:
1650 // a specialization of dojo.dnd.constrainedMover, which constrains to the parent node
1651 // area: String
1652 // "margin" to constrain within the parent's margin box, "border" for the border box,
1653 // "padding" for the padding box, and "content" for the content box; "content" is the default value.
1654 // within: Boolean
1655 // if true, constraints the whole dragged object within the rectangle,
1656 // otherwise the constraint is applied to the left-top corner
1657
1658 dojo.deprecated("dojo.dnd.move.parentConstrainedMover, use dojo.dnd.move.parentConstrainedMoveable instead");
1659 var fun = function(){
1660 var n = this.node.parentNode,
1661 s = dojo.getComputedStyle(n),
1662 mb = dojo._getMarginBox(n, s);
1663 if(area == "margin"){
1664 return mb; // Object
1665 }
1666 var t = dojo._getMarginExtents(n, s);
1667 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
1668 if(area == "border"){
1669 return mb; // Object
1670 }
1671 t = dojo._getBorderExtents(n, s);
1672 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
1673 if(area == "padding"){
1674 return mb; // Object
1675 }
1676 t = dojo._getPadExtents(n, s);
1677 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
1678 return mb; // Object
1679 };
1680 return dojo.dnd.move.constrainedMover(fun, within); // Object
1681};
1682
1683// patching functions one level up for compatibility
1684
1685dojo.dnd.constrainedMover = dojo.dnd.move.constrainedMover;
1686dojo.dnd.boxConstrainedMover = dojo.dnd.move.boxConstrainedMover;
1687dojo.dnd.parentConstrainedMover = dojo.dnd.move.parentConstrainedMover;
1688
1689}
1690
1691if(!dojo._hasResource["dojo.dnd.TimedMoveable"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1692dojo._hasResource["dojo.dnd.TimedMoveable"] = true;
1693dojo.provide("dojo.dnd.TimedMoveable");
1694
1695
1696
1697/*=====
1698dojo.declare("dojo.dnd.__TimedMoveableArgs", [dojo.dnd.__MoveableArgs], {
1699 // timeout: Number
1700 // delay move by this number of ms,
1701 // accumulating position changes during the timeout
1702 timeout: 0
1703});
1704=====*/
1705
1706(function(){
1707 // precalculate long expressions
1708 var oldOnMove = dojo.dnd.Moveable.prototype.onMove;
1709
1710 dojo.declare("dojo.dnd.TimedMoveable", dojo.dnd.Moveable, {
1711 // summary:
1712 // A specialized version of Moveable to support an FPS throttling.
1713 // This class puts an upper restriction on FPS, which may reduce
1714 // the CPU load. The additional parameter "timeout" regulates
1715 // the delay before actually moving the moveable object.
1716
1717 // object attributes (for markup)
1718 timeout: 40, // in ms, 40ms corresponds to 25 fps
1719
1720 constructor: function(node, params){
1721 // summary:
1722 // an object that makes a node moveable with a timer
1723 // node: Node||String
1724 // a node (or node's id) to be moved
1725 // params: dojo.dnd.__TimedMoveableArgs
1726 // object with additional parameters.
1727
1728 // sanitize parameters
1729 if(!params){ params = {}; }
1730 if(params.timeout && typeof params.timeout == "number" && params.timeout >= 0){
1731 this.timeout = params.timeout;
1732 }
1733 },
1734
1735 // markup methods
1736 markupFactory: function(params, node){
1737 return new dojo.dnd.TimedMoveable(node, params);
1738 },
1739
1740 onMoveStop: function(/* dojo.dnd.Mover */ mover){
1741 if(mover._timer){
1742 // stop timer
1743 clearTimeout(mover._timer)
1744 // reflect the last received position
1745 oldOnMove.call(this, mover, mover._leftTop)
1746 }
1747 dojo.dnd.Moveable.prototype.onMoveStop.apply(this, arguments);
1748 },
1749 onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
1750 mover._leftTop = leftTop;
1751 if(!mover._timer){
1752 var _t = this; // to avoid using dojo.hitch()
1753 mover._timer = setTimeout(function(){
1754 // we don't have any pending requests
1755 mover._timer = null;
1756 // reflect the last received position
1757 oldOnMove.call(_t, mover, mover._leftTop);
1758 }, this.timeout);
1759 }
1760 }
1761 });
1762})();
1763
1764}
1765
1766if(!dojo._hasResource["dojo.fx.Toggler"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1767dojo._hasResource["dojo.fx.Toggler"] = true;
1768dojo.provide("dojo.fx.Toggler");
1769
1770dojo.declare("dojo.fx.Toggler", null, {
1771 // summary:
1772 // A simple `dojo.Animation` toggler API.
1773 //
1774 // description:
1775 // class constructor for an animation toggler. It accepts a packed
1776 // set of arguments about what type of animation to use in each
1777 // direction, duration, etc. All available members are mixed into
1778 // these animations from the constructor (for example, `node`,
1779 // `showDuration`, `hideDuration`).
1780 //
1781 // example:
1782 // | var t = new dojo.fx.Toggler({
1783 // | node: "nodeId",
1784 // | showDuration: 500,
1785 // | // hideDuration will default to "200"
1786 // | showFunc: dojo.fx.wipeIn,
1787 // | // hideFunc will default to "fadeOut"
1788 // | });
1789 // | t.show(100); // delay showing for 100ms
1790 // | // ...time passes...
1791 // | t.hide();
1792
1793 // node: DomNode
1794 // the node to target for the showing and hiding animations
1795 node: null,
1796
1797 // showFunc: Function
1798 // The function that returns the `dojo.Animation` to show the node
1799 showFunc: dojo.fadeIn,
1800
1801 // hideFunc: Function
1802 // The function that returns the `dojo.Animation` to hide the node
1803 hideFunc: dojo.fadeOut,
1804
1805 // showDuration:
1806 // Time in milliseconds to run the show Animation
1807 showDuration: 200,
1808
1809 // hideDuration:
1810 // Time in milliseconds to run the hide Animation
1811 hideDuration: 200,
1812
1813 // FIXME: need a policy for where the toggler should "be" the next
1814 // time show/hide are called if we're stopped somewhere in the
1815 // middle.
1816 // FIXME: also would be nice to specify individual showArgs/hideArgs mixed into
1817 // each animation individually.
1818 // FIXME: also would be nice to have events from the animations exposed/bridged
1819
1820 /*=====
1821 _showArgs: null,
1822 _showAnim: null,
1823
1824 _hideArgs: null,
1825 _hideAnim: null,
1826
1827 _isShowing: false,
1828 _isHiding: false,
1829 =====*/
1830
1831 constructor: function(args){
1832 var _t = this;
1833
1834 dojo.mixin(_t, args);
1835 _t.node = args.node;
1836 _t._showArgs = dojo.mixin({}, args);
1837 _t._showArgs.node = _t.node;
1838 _t._showArgs.duration = _t.showDuration;
1839 _t.showAnim = _t.showFunc(_t._showArgs);
1840
1841 _t._hideArgs = dojo.mixin({}, args);
1842 _t._hideArgs.node = _t.node;
1843 _t._hideArgs.duration = _t.hideDuration;
1844 _t.hideAnim = _t.hideFunc(_t._hideArgs);
1845
1846 dojo.connect(_t.showAnim, "beforeBegin", dojo.hitch(_t.hideAnim, "stop", true));
1847 dojo.connect(_t.hideAnim, "beforeBegin", dojo.hitch(_t.showAnim, "stop", true));
1848 },
1849
1850 show: function(delay){
1851 // summary: Toggle the node to showing
1852 // delay: Integer?
1853 // Ammount of time to stall playing the show animation
1854 return this.showAnim.play(delay || 0);
1855 },
1856
1857 hide: function(delay){
1858 // summary: Toggle the node to hidden
1859 // delay: Integer?
1860 // Ammount of time to stall playing the hide animation
1861 return this.hideAnim.play(delay || 0);
1862 }
1863});
1864
1865}
1866
1867if(!dojo._hasResource["dojo.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1868dojo._hasResource["dojo.fx"] = true;
1869dojo.provide("dojo.fx");
1870 // FIXME: remove this back-compat require in 2.0
1871/*=====
1872dojo.fx = {
1873 // summary: Effects library on top of Base animations
1874};
1875=====*/
1876(function(){
1877
1878 var d = dojo,
1879 _baseObj = {
1880 _fire: function(evt, args){
1881 if(this[evt]){
1882 this[evt].apply(this, args||[]);
1883 }
1884 return this;
1885 }
1886 };
1887
1888 var _chain = function(animations){
1889 this._index = -1;
1890 this._animations = animations||[];
1891 this._current = this._onAnimateCtx = this._onEndCtx = null;
1892
1893 this.duration = 0;
1894 d.forEach(this._animations, function(a){
1895 this.duration += a.duration;
1896 if(a.delay){ this.duration += a.delay; }
1897 }, this);
1898 };
1899 d.extend(_chain, {
1900 _onAnimate: function(){
1901 this._fire("onAnimate", arguments);
1902 },
1903 _onEnd: function(){
1904 d.disconnect(this._onAnimateCtx);
1905 d.disconnect(this._onEndCtx);
1906 this._onAnimateCtx = this._onEndCtx = null;
1907 if(this._index + 1 == this._animations.length){
1908 this._fire("onEnd");
1909 }else{
1910 // switch animations
1911 this._current = this._animations[++this._index];
1912 this._onAnimateCtx = d.connect(this._current, "onAnimate", this, "_onAnimate");
1913 this._onEndCtx = d.connect(this._current, "onEnd", this, "_onEnd");
1914 this._current.play(0, true);
1915 }
1916 },
1917 play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
1918 if(!this._current){ this._current = this._animations[this._index = 0]; }
1919 if(!gotoStart && this._current.status() == "playing"){ return this; }
1920 var beforeBegin = d.connect(this._current, "beforeBegin", this, function(){
1921 this._fire("beforeBegin");
1922 }),
1923 onBegin = d.connect(this._current, "onBegin", this, function(arg){
1924 this._fire("onBegin", arguments);
1925 }),
1926 onPlay = d.connect(this._current, "onPlay", this, function(arg){
1927 this._fire("onPlay", arguments);
1928 d.disconnect(beforeBegin);
1929 d.disconnect(onBegin);
1930 d.disconnect(onPlay);
1931 });
1932 if(this._onAnimateCtx){
1933 d.disconnect(this._onAnimateCtx);
1934 }
1935 this._onAnimateCtx = d.connect(this._current, "onAnimate", this, "_onAnimate");
1936 if(this._onEndCtx){
1937 d.disconnect(this._onEndCtx);
1938 }
1939 this._onEndCtx = d.connect(this._current, "onEnd", this, "_onEnd");
1940 this._current.play.apply(this._current, arguments);
1941 return this;
1942 },
1943 pause: function(){
1944 if(this._current){
1945 var e = d.connect(this._current, "onPause", this, function(arg){
1946 this._fire("onPause", arguments);
1947 d.disconnect(e);
1948 });
1949 this._current.pause();
1950 }
1951 return this;
1952 },
1953 gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
1954 this.pause();
1955 var offset = this.duration * percent;
1956 this._current = null;
1957 d.some(this._animations, function(a){
1958 if(a.duration <= offset){
1959 this._current = a;
1960 return true;
1961 }
1962 offset -= a.duration;
1963 return false;
1964 });
1965 if(this._current){
1966 this._current.gotoPercent(offset / this._current.duration, andPlay);
1967 }
1968 return this;
1969 },
1970 stop: function(/*boolean?*/ gotoEnd){
1971 if(this._current){
1972 if(gotoEnd){
1973 for(; this._index + 1 < this._animations.length; ++this._index){
1974 this._animations[this._index].stop(true);
1975 }
1976 this._current = this._animations[this._index];
1977 }
1978 var e = d.connect(this._current, "onStop", this, function(arg){
1979 this._fire("onStop", arguments);
1980 d.disconnect(e);
1981 });
1982 this._current.stop();
1983 }
1984 return this;
1985 },
1986 status: function(){
1987 return this._current ? this._current.status() : "stopped";
1988 },
1989 destroy: function(){
1990 if(this._onAnimateCtx){ d.disconnect(this._onAnimateCtx); }
1991 if(this._onEndCtx){ d.disconnect(this._onEndCtx); }
1992 }
1993 });
1994 d.extend(_chain, _baseObj);
1995
1996 dojo.fx.chain = function(/*dojo.Animation[]*/ animations){
1997 // summary:
1998 // Chain a list of `dojo.Animation`s to run in sequence
1999 //
2000 // description:
2001 // Return a `dojo.Animation` which will play all passed
2002 // `dojo.Animation` instances in sequence, firing its own
2003 // synthesized events simulating a single animation. (eg:
2004 // onEnd of this animation means the end of the chain,
2005 // not the individual animations within)
2006 //
2007 // example:
2008 // Once `node` is faded out, fade in `otherNode`
2009 // | dojo.fx.chain([
2010 // | dojo.fadeIn({ node:node }),
2011 // | dojo.fadeOut({ node:otherNode })
2012 // | ]).play();
2013 //
2014 return new _chain(animations) // dojo.Animation
2015 };
2016
2017 var _combine = function(animations){
2018 this._animations = animations||[];
2019 this._connects = [];
2020 this._finished = 0;
2021
2022 this.duration = 0;
2023 d.forEach(animations, function(a){
2024 var duration = a.duration;
2025 if(a.delay){ duration += a.delay; }
2026 if(this.duration < duration){ this.duration = duration; }
2027 this._connects.push(d.connect(a, "onEnd", this, "_onEnd"));
2028 }, this);
2029
2030 this._pseudoAnimation = new d.Animation({curve: [0, 1], duration: this.duration});
2031 var self = this;
2032 d.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop", "onEnd"],
2033 function(evt){
2034 self._connects.push(d.connect(self._pseudoAnimation, evt,
2035 function(){ self._fire(evt, arguments); }
2036 ));
2037 }
2038 );
2039 };
2040 d.extend(_combine, {
2041 _doAction: function(action, args){
2042 d.forEach(this._animations, function(a){
2043 a[action].apply(a, args);
2044 });
2045 return this;
2046 },
2047 _onEnd: function(){
2048 if(++this._finished > this._animations.length){
2049 this._fire("onEnd");
2050 }
2051 },
2052 _call: function(action, args){
2053 var t = this._pseudoAnimation;
2054 t[action].apply(t, args);
2055 },
2056 play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
2057 this._finished = 0;
2058 this._doAction("play", arguments);
2059 this._call("play", arguments);
2060 return this;
2061 },
2062 pause: function(){
2063 this._doAction("pause", arguments);
2064 this._call("pause", arguments);
2065 return this;
2066 },
2067 gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
2068 var ms = this.duration * percent;
2069 d.forEach(this._animations, function(a){
2070 a.gotoPercent(a.duration < ms ? 1 : (ms / a.duration), andPlay);
2071 });
2072 this._call("gotoPercent", arguments);
2073 return this;
2074 },
2075 stop: function(/*boolean?*/ gotoEnd){
2076 this._doAction("stop", arguments);
2077 this._call("stop", arguments);
2078 return this;
2079 },
2080 status: function(){
2081 return this._pseudoAnimation.status();
2082 },
2083 destroy: function(){
2084 d.forEach(this._connects, dojo.disconnect);
2085 }
2086 });
2087 d.extend(_combine, _baseObj);
2088
2089 dojo.fx.combine = function(/*dojo.Animation[]*/ animations){
2090 // summary:
2091 // Combine a list of `dojo.Animation`s to run in parallel
2092 //
2093 // description:
2094 // Combine an array of `dojo.Animation`s to run in parallel,
2095 // providing a new `dojo.Animation` instance encompasing each
2096 // animation, firing standard animation events.
2097 //
2098 // example:
2099 // Fade out `node` while fading in `otherNode` simultaneously
2100 // | dojo.fx.combine([
2101 // | dojo.fadeIn({ node:node }),
2102 // | dojo.fadeOut({ node:otherNode })
2103 // | ]).play();
2104 //
2105 // example:
2106 // When the longest animation ends, execute a function:
2107 // | var anim = dojo.fx.combine([
2108 // | dojo.fadeIn({ node: n, duration:700 }),
2109 // | dojo.fadeOut({ node: otherNode, duration: 300 })
2110 // | ]);
2111 // | dojo.connect(anim, "onEnd", function(){
2112 // | // overall animation is done.
2113 // | });
2114 // | anim.play(); // play the animation
2115 //
2116 return new _combine(animations); // dojo.Animation
2117 };
2118
2119 dojo.fx.wipeIn = function(/*Object*/ args){
2120 // summary:
2121 // Expand a node to it's natural height.
2122 //
2123 // description:
2124 // Returns an animation that will expand the
2125 // node defined in 'args' object from it's current height to
2126 // it's natural height (with no scrollbar).
2127 // Node must have no margin/border/padding.
2128 //
2129 // args: Object
2130 // A hash-map of standard `dojo.Animation` constructor properties
2131 // (such as easing: node: duration: and so on)
2132 //
2133 // example:
2134 // | dojo.fx.wipeIn({
2135 // | node:"someId"
2136 // | }).play()
2137 var node = args.node = d.byId(args.node), s = node.style, o;
2138
2139 var anim = d.animateProperty(d.mixin({
2140 properties: {
2141 height: {
2142 // wrapped in functions so we wait till the last second to query (in case value has changed)
2143 start: function(){
2144 // start at current [computed] height, but use 1px rather than 0
2145 // because 0 causes IE to display the whole panel
2146 o = s.overflow;
2147 s.overflow = "hidden";
2148 if(s.visibility == "hidden" || s.display == "none"){
2149 s.height = "1px";
2150 s.display = "";
2151 s.visibility = "";
2152 return 1;
2153 }else{
2154 var height = d.style(node, "height");
2155 return Math.max(height, 1);
2156 }
2157 },
2158 end: function(){
2159 return node.scrollHeight;
2160 }
2161 }
2162 }
2163 }, args));
2164
2165 d.connect(anim, "onEnd", function(){
2166 s.height = "auto";
2167 s.overflow = o;
2168 });
2169
2170 return anim; // dojo.Animation
2171 }
2172
2173 dojo.fx.wipeOut = function(/*Object*/ args){
2174 // summary:
2175 // Shrink a node to nothing and hide it.
2176 //
2177 // description:
2178 // Returns an animation that will shrink node defined in "args"
2179 // from it's current height to 1px, and then hide it.
2180 //
2181 // args: Object
2182 // A hash-map of standard `dojo.Animation` constructor properties
2183 // (such as easing: node: duration: and so on)
2184 //
2185 // example:
2186 // | dojo.fx.wipeOut({ node:"someId" }).play()
2187
2188 var node = args.node = d.byId(args.node), s = node.style, o;
2189
2190 var anim = d.animateProperty(d.mixin({
2191 properties: {
2192 height: {
2193 end: 1 // 0 causes IE to display the whole panel
2194 }
2195 }
2196 }, args));
2197
2198 d.connect(anim, "beforeBegin", function(){
2199 o = s.overflow;
2200 s.overflow = "hidden";
2201 s.display = "";
2202 });
2203 d.connect(anim, "onEnd", function(){
2204 s.overflow = o;
2205 s.height = "auto";
2206 s.display = "none";
2207 });
2208
2209 return anim; // dojo.Animation
2210 }
2211
2212 dojo.fx.slideTo = function(/*Object*/ args){
2213 // summary:
2214 // Slide a node to a new top/left position
2215 //
2216 // description:
2217 // Returns an animation that will slide "node"
2218 // defined in args Object from its current position to
2219 // the position defined by (args.left, args.top).
2220 //
2221 // args: Object
2222 // A hash-map of standard `dojo.Animation` constructor properties
2223 // (such as easing: node: duration: and so on). Special args members
2224 // are `top` and `left`, which indicate the new position to slide to.
2225 //
2226 // example:
2227 // | dojo.fx.slideTo({ node: node, left:"40", top:"50", units:"px" }).play()
2228
2229 var node = args.node = d.byId(args.node),
2230 top = null, left = null;
2231
2232 var init = (function(n){
2233 return function(){
2234 var cs = d.getComputedStyle(n);
2235 var pos = cs.position;
2236 top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0);
2237 left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0);
2238 if(pos != 'absolute' && pos != 'relative'){
2239 var ret = d.position(n, true);
2240 top = ret.y;
2241 left = ret.x;
2242 n.style.position="absolute";
2243 n.style.top=top+"px";
2244 n.style.left=left+"px";
2245 }
2246 };
2247 })(node);
2248 init();
2249
2250 var anim = d.animateProperty(d.mixin({
2251 properties: {
2252 top: args.top || 0,
2253 left: args.left || 0
2254 }
2255 }, args));
2256 d.connect(anim, "beforeBegin", anim, init);
2257
2258 return anim; // dojo.Animation
2259 }
2260
2261})();
2262
2263}
2264
2265if(!dojo._hasResource["dijit.form._FormMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2266dojo._hasResource["dijit.form._FormMixin"] = true;
2267dojo.provide("dijit.form._FormMixin");
2268
2269
2270
2271dojo.declare("dijit.form._FormMixin", null,
2272 {
2273 // summary:
2274 // Mixin for containers of form widgets (i.e. widgets that represent a single value
2275 // and can be children of a <form> node or dijit.form.Form widget)
2276 // description:
2277 // Can extract all the form widgets
2278 // values and combine them into a single javascript object, or alternately
2279 // take such an object and set the values for all the contained
2280 // form widgets
2281
2282/*=====
2283 // value: Object
2284 // Name/value hash for each child widget with a name and value.
2285 // Child widgets without names are not part of the hash.
2286 //
2287 // If there are multiple child widgets w/the same name, value is an array,
2288 // unless they are radio buttons in which case value is a scalar (since only
2289 // one radio button can be checked at a time).
2290 //
2291 // If a child widget's name is a dot separated list (like a.b.c.d), it's a nested structure.
2292 //
2293 // Example:
2294 // | { name: "John Smith", interests: ["sports", "movies"] }
2295=====*/
2296
2297 // TODO:
2298 // * Repeater
2299 // * better handling for arrays. Often form elements have names with [] like
2300 // * people[3].sex (for a list of people [{name: Bill, sex: M}, ...])
2301 //
2302 //
2303
2304 reset: function(){
2305 dojo.forEach(this.getDescendants(), function(widget){
2306 if(widget.reset){
2307 widget.reset();
2308 }
2309 });
2310 },
2311
2312 validate: function(){
2313 // summary:
2314 // returns if the form is valid - same as isValid - but
2315 // provides a few additional (ui-specific) features.
2316 // 1 - it will highlight any sub-widgets that are not
2317 // valid
2318 // 2 - it will call focus() on the first invalid
2319 // sub-widget
2320 var didFocus = false;
2321 return dojo.every(dojo.map(this.getDescendants(), function(widget){
2322 // Need to set this so that "required" widgets get their
2323 // state set.
2324 widget._hasBeenBlurred = true;
2325 var valid = widget.disabled || !widget.validate || widget.validate();
2326 if(!valid && !didFocus){
2327 // Set focus of the first non-valid widget
2328 dojo.window.scrollIntoView(widget.containerNode || widget.domNode);
2329 widget.focus();
2330 didFocus = true;
2331 }
2332 return valid;
2333 }), function(item){ return item; });
2334 },
2335
2336 setValues: function(val){
2337 dojo.deprecated(this.declaredClass+"::setValues() is deprecated. Use set('value', val) instead.", "", "2.0");
2338 return this.set('value', val);
2339 },
2340 _setValueAttr: function(/*object*/obj){
2341 // summary:
2342 // Fill in form values from according to an Object (in the format returned by attr('value'))
2343
2344 // generate map from name --> [list of widgets with that name]
2345 var map = { };
2346 dojo.forEach(this.getDescendants(), function(widget){
2347 if(!widget.name){ return; }
2348 var entry = map[widget.name] || (map[widget.name] = [] );
2349 entry.push(widget);
2350 });
2351
2352 for(var name in map){
2353 if(!map.hasOwnProperty(name)){
2354 continue;
2355 }
2356 var widgets = map[name], // array of widgets w/this name
2357 values = dojo.getObject(name, false, obj); // list of values for those widgets
2358
2359 if(values === undefined){
2360 continue;
2361 }
2362 if(!dojo.isArray(values)){
2363 values = [ values ];
2364 }
2365 if(typeof widgets[0].checked == 'boolean'){
2366 // for checkbox/radio, values is a list of which widgets should be checked
2367 dojo.forEach(widgets, function(w, i){
2368 w.set('value', dojo.indexOf(values, w.value) != -1);
2369 });
2370 }else if(widgets[0].multiple){
2371 // it takes an array (e.g. multi-select)
2372 widgets[0].set('value', values);
2373 }else{
2374 // otherwise, values is a list of values to be assigned sequentially to each widget
2375 dojo.forEach(widgets, function(w, i){
2376 w.set('value', values[i]);
2377 });
2378 }
2379 }
2380
2381 /***
2382 * TODO: code for plain input boxes (this shouldn't run for inputs that are part of widgets)
2383
2384 dojo.forEach(this.containerNode.elements, function(element){
2385 if(element.name == ''){return}; // like "continue"
2386 var namePath = element.name.split(".");
2387 var myObj=obj;
2388 var name=namePath[namePath.length-1];
2389 for(var j=1,len2=namePath.length;j<len2;++j){
2390 var p=namePath[j - 1];
2391 // repeater support block
2392 var nameA=p.split("[");
2393 if(nameA.length > 1){
2394 if(typeof(myObj[nameA[0]]) == "undefined"){
2395 myObj[nameA[0]]=[ ];
2396 } // if
2397
2398 nameIndex=parseInt(nameA[1]);
2399 if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
2400 myObj[nameA[0]][nameIndex] = { };
2401 }
2402 myObj=myObj[nameA[0]][nameIndex];
2403 continue;
2404 } // repeater support ends
2405
2406 if(typeof(myObj[p]) == "undefined"){
2407 myObj=undefined;
2408 break;
2409 };
2410 myObj=myObj[p];
2411 }
2412
2413 if(typeof(myObj) == "undefined"){
2414 return; // like "continue"
2415 }
2416 if(typeof(myObj[name]) == "undefined" && this.ignoreNullValues){
2417 return; // like "continue"
2418 }
2419
2420 // TODO: widget values (just call attr('value', ...) on the widget)
2421
2422 // TODO: maybe should call dojo.getNodeProp() instead
2423 switch(element.type){
2424 case "checkbox":
2425 element.checked = (name in myObj) &&
2426 dojo.some(myObj[name], function(val){ return val == element.value; });
2427 break;
2428 case "radio":
2429 element.checked = (name in myObj) && myObj[name] == element.value;
2430 break;
2431 case "select-multiple":
2432 element.selectedIndex=-1;
2433 dojo.forEach(element.options, function(option){
2434 option.selected = dojo.some(myObj[name], function(val){ return option.value == val; });
2435 });
2436 break;
2437 case "select-one":
2438 element.selectedIndex="0";
2439 dojo.forEach(element.options, function(option){
2440 option.selected = option.value == myObj[name];
2441 });
2442 break;
2443 case "hidden":
2444 case "text":
2445 case "textarea":
2446 case "password":
2447 element.value = myObj[name] || "";
2448 break;
2449 }
2450 });
2451 */
2452 },
2453
2454 getValues: function(){
2455 dojo.deprecated(this.declaredClass+"::getValues() is deprecated. Use get('value') instead.", "", "2.0");
2456 return this.get('value');
2457 },
2458 _getValueAttr: function(){
2459 // summary:
2460 // Returns Object representing form values.
2461 // description:
2462 // Returns name/value hash for each form element.
2463 // If there are multiple elements w/the same name, value is an array,
2464 // unless they are radio buttons in which case value is a scalar since only
2465 // one can be checked at a time.
2466 //
2467 // If the name is a dot separated list (like a.b.c.d), creates a nested structure.
2468 // Only works on widget form elements.
2469 // example:
2470 // | { name: "John Smith", interests: ["sports", "movies"] }
2471
2472 // get widget values
2473 var obj = { };
2474 dojo.forEach(this.getDescendants(), function(widget){
2475 var name = widget.name;
2476 if(!name || widget.disabled){ return; }
2477
2478 // Single value widget (checkbox, radio, or plain <input> type widget
2479 var value = widget.get('value');
2480
2481 // Store widget's value(s) as a scalar, except for checkboxes which are automatically arrays
2482 if(typeof widget.checked == 'boolean'){
2483 if(/Radio/.test(widget.declaredClass)){
2484 // radio button
2485 if(value !== false){
2486 dojo.setObject(name, value, obj);
2487 }else{
2488 // give radio widgets a default of null
2489 value = dojo.getObject(name, false, obj);
2490 if(value === undefined){
2491 dojo.setObject(name, null, obj);
2492 }
2493 }
2494 }else{
2495 // checkbox/toggle button
2496 var ary=dojo.getObject(name, false, obj);
2497 if(!ary){
2498 ary=[];
2499 dojo.setObject(name, ary, obj);
2500 }
2501 if(value !== false){
2502 ary.push(value);
2503 }
2504 }
2505 }else{
2506 var prev=dojo.getObject(name, false, obj);
2507 if(typeof prev != "undefined"){
2508 if(dojo.isArray(prev)){
2509 prev.push(value);
2510 }else{
2511 dojo.setObject(name, [prev, value], obj);
2512 }
2513 }else{
2514 // unique name
2515 dojo.setObject(name, value, obj);
2516 }
2517 }
2518 });
2519
2520 /***
2521 * code for plain input boxes (see also dojo.formToObject, can we use that instead of this code?
2522 * but it doesn't understand [] notation, presumably)
2523 var obj = { };
2524 dojo.forEach(this.containerNode.elements, function(elm){
2525 if(!elm.name) {
2526 return; // like "continue"
2527 }
2528 var namePath = elm.name.split(".");
2529 var myObj=obj;
2530 var name=namePath[namePath.length-1];
2531 for(var j=1,len2=namePath.length;j<len2;++j){
2532 var nameIndex = null;
2533 var p=namePath[j - 1];
2534 var nameA=p.split("[");
2535 if(nameA.length > 1){
2536 if(typeof(myObj[nameA[0]]) == "undefined"){
2537 myObj[nameA[0]]=[ ];
2538 } // if
2539 nameIndex=parseInt(nameA[1]);
2540 if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
2541 myObj[nameA[0]][nameIndex] = { };
2542 }
2543 } else if(typeof(myObj[nameA[0]]) == "undefined"){
2544 myObj[nameA[0]] = { }
2545 } // if
2546
2547 if(nameA.length == 1){
2548 myObj=myObj[nameA[0]];
2549 } else{
2550 myObj=myObj[nameA[0]][nameIndex];
2551 } // if
2552 } // for
2553
2554 if((elm.type != "select-multiple" && elm.type != "checkbox" && elm.type != "radio") || (elm.type == "radio" && elm.checked)){
2555 if(name == name.split("[")[0]){
2556 myObj[name]=elm.value;
2557 } else{
2558 // can not set value when there is no name
2559 }
2560 } else if(elm.type == "checkbox" && elm.checked){
2561 if(typeof(myObj[name]) == 'undefined'){
2562 myObj[name]=[ ];
2563 }
2564 myObj[name].push(elm.value);
2565 } else if(elm.type == "select-multiple"){
2566 if(typeof(myObj[name]) == 'undefined'){
2567 myObj[name]=[ ];
2568 }
2569 for(var jdx=0,len3=elm.options.length; jdx<len3; ++jdx){
2570 if(elm.options[jdx].selected){
2571 myObj[name].push(elm.options[jdx].value);
2572 }
2573 }
2574 } // if
2575 name=undefined;
2576 }); // forEach
2577 ***/
2578 return obj;
2579 },
2580
2581 // TODO: ComboBox might need time to process a recently input value. This should be async?
2582 isValid: function(){
2583 // summary:
2584 // Returns true if all of the widgets are valid
2585
2586 // This also populate this._invalidWidgets[] array with list of invalid widgets...
2587 // TODO: put that into separate function? It's confusing to have that as a side effect
2588 // of a method named isValid().
2589
2590 this._invalidWidgets = dojo.filter(this.getDescendants(), function(widget){
2591 return !widget.disabled && widget.isValid && !widget.isValid();
2592 });
2593 return !this._invalidWidgets.length;
2594 },
2595
2596
2597 onValidStateChange: function(isValid){
2598 // summary:
2599 // Stub function to connect to if you want to do something
2600 // (like disable/enable a submit button) when the valid
2601 // state changes on the form as a whole.
2602 },
2603
2604 _widgetChange: function(widget){
2605 // summary:
2606 // Connected to a widget's onChange function - update our
2607 // valid state, if needed.
2608 var isValid = this._lastValidState;
2609 if(!widget || this._lastValidState === undefined){
2610 // We have passed a null widget, or we haven't been validated
2611 // yet - let's re-check all our children
2612 // This happens when we connect (or reconnect) our children
2613 isValid = this.isValid();
2614 if(this._lastValidState === undefined){
2615 // Set this so that we don't fire an onValidStateChange
2616 // the first time
2617 this._lastValidState = isValid;
2618 }
2619 }else if(widget.isValid){
2620 this._invalidWidgets = dojo.filter(this._invalidWidgets || [], function(w){
2621 return (w != widget);
2622 }, this);
2623 if(!widget.isValid() && !widget.get("disabled")){
2624 this._invalidWidgets.push(widget);
2625 }
2626 isValid = (this._invalidWidgets.length === 0);
2627 }
2628 if(isValid !== this._lastValidState){
2629 this._lastValidState = isValid;
2630 this.onValidStateChange(isValid);
2631 }
2632 },
2633
2634 connectChildren: function(){
2635 // summary:
2636 // Connects to the onChange function of all children to
2637 // track valid state changes. You can call this function
2638 // directly, ex. in the event that you programmatically
2639 // add a widget to the form *after* the form has been
2640 // initialized.
2641 dojo.forEach(this._changeConnections, dojo.hitch(this, "disconnect"));
2642 var _this = this;
2643
2644 // we connect to validate - so that it better reflects the states
2645 // of the widgets - also, we only connect if it has a validate
2646 // function (to avoid too many unneeded connections)
2647 var conns = (this._changeConnections = []);
2648 dojo.forEach(dojo.filter(this.getDescendants(),
2649 function(item){ return item.validate; }
2650 ),
2651 function(widget){
2652 // We are interested in whenever the widget is validated - or
2653 // whenever the disabled attribute on that widget is changed
2654 conns.push(_this.connect(widget, "validate",
2655 dojo.hitch(_this, "_widgetChange", widget)));
2656 conns.push(_this.connect(widget, "_setDisabledAttr",
2657 dojo.hitch(_this, "_widgetChange", widget)));
2658 });
2659
2660 // Call the widget change function to update the valid state, in
2661 // case something is different now.
2662 this._widgetChange(null);
2663 },
2664
2665 startup: function(){
2666 this.inherited(arguments);
2667 // Initialize our valid state tracking. Needs to be done in startup
2668 // because it's not guaranteed that our children are initialized
2669 // yet.
2670 this._changeConnections = [];
2671 this.connectChildren();
2672 }
2673 });
2674
2675}
2676
2677if(!dojo._hasResource["dijit._DialogMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2678dojo._hasResource["dijit._DialogMixin"] = true;
2679dojo.provide("dijit._DialogMixin");
2680
2681
2682
2683dojo.declare("dijit._DialogMixin", null,
2684 {
2685 // summary:
2686 // This provides functions useful to Dialog and TooltipDialog
2687
2688 attributeMap: dijit._Widget.prototype.attributeMap,
2689
2690 execute: function(/*Object*/ formContents){
2691 // summary:
2692 // Callback when the user hits the submit button.
2693 // Override this method to handle Dialog execution.
2694 // description:
2695 // After the user has pressed the submit button, the Dialog
2696 // first calls onExecute() to notify the container to hide the
2697 // dialog and restore focus to wherever it used to be.
2698 //
2699 // *Then* this method is called.
2700 // type:
2701 // callback
2702 },
2703
2704 onCancel: function(){
2705 // summary:
2706 // Called when user has pressed the Dialog's cancel button, to notify container.
2707 // description:
2708 // Developer shouldn't override or connect to this method;
2709 // it's a private communication device between the TooltipDialog
2710 // and the thing that opened it (ex: `dijit.form.DropDownButton`)
2711 // type:
2712 // protected
2713 },
2714
2715 onExecute: function(){
2716 // summary:
2717 // Called when user has pressed the dialog's OK button, to notify container.
2718 // description:
2719 // Developer shouldn't override or connect to this method;
2720 // it's a private communication device between the TooltipDialog
2721 // and the thing that opened it (ex: `dijit.form.DropDownButton`)
2722 // type:
2723 // protected
2724 },
2725
2726 _onSubmit: function(){
2727 // summary:
2728 // Callback when user hits submit button
2729 // type:
2730 // protected
2731 this.onExecute(); // notify container that we are about to execute
2732 this.execute(this.get('value'));
2733 },
2734
2735 _getFocusItems: function(/*Node*/ dialogNode){
2736 // summary:
2737 // Find focusable Items each time a dialog is opened,
2738 // setting _firstFocusItem and _lastFocusItem
2739 // tags:
2740 // protected
2741
2742 var elems = dijit._getTabNavigable(dojo.byId(dialogNode));
2743 this._firstFocusItem = elems.lowest || elems.first || dialogNode;
2744 this._lastFocusItem = elems.last || elems.highest || this._firstFocusItem;
2745 if(dojo.isMoz && this._firstFocusItem.tagName.toLowerCase() == "input" &&
2746 dojo.getNodeProp(this._firstFocusItem, "type").toLowerCase() == "file"){
2747 // FF doesn't behave well when first element is input type=file, set first focusable to dialog container
2748 dojo.attr(dialogNode, "tabIndex", "0");
2749 this._firstFocusItem = dialogNode;
2750 }
2751 }
2752 }
2753);
2754
2755}
2756
2757if(!dojo._hasResource["dijit.DialogUnderlay"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2758dojo._hasResource["dijit.DialogUnderlay"] = true;
2759dojo.provide("dijit.DialogUnderlay");
2760
2761
2762
2763
2764
2765
2766dojo.declare(
2767 "dijit.DialogUnderlay",
2768 [dijit._Widget, dijit._Templated],
2769 {
2770 // summary:
2771 // The component that blocks the screen behind a `dijit.Dialog`
2772 //
2773 // description:
2774 // A component used to block input behind a `dijit.Dialog`. Only a single
2775 // instance of this widget is created by `dijit.Dialog`, and saved as
2776 // a reference to be shared between all Dialogs as `dijit._underlay`
2777 //
2778 // The underlay itself can be styled based on and id:
2779 // | #myDialog_underlay { background-color:red; }
2780 //
2781 // In the case of `dijit.Dialog`, this id is based on the id of the Dialog,
2782 // suffixed with _underlay.
2783
2784 // Template has two divs; outer div is used for fade-in/fade-out, and also to hold background iframe.
2785 // Inner div has opacity specified in CSS file.
2786 templateString: "<div class='dijitDialogUnderlayWrapper'><div class='dijitDialogUnderlay' dojoAttachPoint='node'></div></div>",
2787
2788 // Parameters on creation or updatable later
2789
2790 // dialogId: String
2791 // Id of the dialog.... DialogUnderlay's id is based on this id
2792 dialogId: "",
2793
2794 // class: String
2795 // This class name is used on the DialogUnderlay node, in addition to dijitDialogUnderlay
2796 "class": "",
2797
2798 attributeMap: { id: "domNode" },
2799
2800 _setDialogIdAttr: function(id){
2801 dojo.attr(this.node, "id", id + "_underlay");
2802 },
2803
2804 _setClassAttr: function(clazz){
2805 this.node.className = "dijitDialogUnderlay " + clazz;
2806 },
2807
2808 postCreate: function(){
2809 // summary:
2810 // Append the underlay to the body
2811 dojo.body().appendChild(this.domNode);
2812 },
2813
2814 layout: function(){
2815 // summary:
2816 // Sets the background to the size of the viewport
2817 //
2818 // description:
2819 // Sets the background to the size of the viewport (rather than the size
2820 // of the document) since we need to cover the whole browser window, even
2821 // if the document is only a few lines long.
2822 // tags:
2823 // private
2824
2825 var is = this.node.style,
2826 os = this.domNode.style;
2827
2828 // hide the background temporarily, so that the background itself isn't
2829 // causing scrollbars to appear (might happen when user shrinks browser
2830 // window and then we are called to resize)
2831 os.display = "none";
2832
2833 // then resize and show
2834 var viewport = dojo.window.getBox();
2835 os.top = viewport.t + "px";
2836 os.left = viewport.l + "px";
2837 is.width = viewport.w + "px";
2838 is.height = viewport.h + "px";
2839 os.display = "block";
2840 },
2841
2842 show: function(){
2843 // summary:
2844 // Show the dialog underlay
2845 this.domNode.style.display = "block";
2846 this.layout();
2847 this.bgIframe = new dijit.BackgroundIframe(this.domNode);
2848 },
2849
2850 hide: function(){
2851 // summary:
2852 // Hides the dialog underlay
2853 this.bgIframe.destroy();
2854 this.domNode.style.display = "none";
2855 },
2856
2857 uninitialize: function(){
2858 if(this.bgIframe){
2859 this.bgIframe.destroy();
2860 }
2861 this.inherited(arguments);
2862 }
2863 }
2864);
2865
2866}
2867
2868if(!dojo._hasResource["dojo.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2869dojo._hasResource["dojo.html"] = true;
2870dojo.provide("dojo.html");
2871
2872// the parser might be needed..
2873
2874
2875(function(){ // private scope, sort of a namespace
2876
2877 // idCounter is incremented with each instantiation to allow asignment of a unique id for tracking, logging purposes
2878 var idCounter = 0,
2879 d = dojo;
2880
2881 dojo.html._secureForInnerHtml = function(/*String*/ cont){
2882 // summary:
2883 // removes !DOCTYPE and title elements from the html string.
2884 //
2885 // khtml is picky about dom faults, you can't attach a style or <title> node as child of body
2886 // must go into head, so we need to cut out those tags
2887 // cont:
2888 // An html string for insertion into the dom
2889 //
2890 return cont.replace(/(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig, ""); // String
2891 };
2892
2893/*====
2894 dojo.html._emptyNode = function(node){
2895 // summary:
2896 // removes all child nodes from the given node
2897 // node: DOMNode
2898 // the parent element
2899 };
2900=====*/
2901 dojo.html._emptyNode = dojo.empty;
2902
2903 dojo.html._setNodeContent = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont){
2904 // summary:
2905 // inserts the given content into the given node
2906 // node:
2907 // the parent element
2908 // content:
2909 // the content to be set on the parent element.
2910 // This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
2911
2912 // always empty
2913 d.empty(node);
2914
2915 if(cont) {
2916 if(typeof cont == "string") {
2917 cont = d._toDom(cont, node.ownerDocument);
2918 }
2919 if(!cont.nodeType && d.isArrayLike(cont)) {
2920 // handle as enumerable, but it may shrink as we enumerate it
2921 for(var startlen=cont.length, i=0; i<cont.length; i=startlen==cont.length ? i+1 : 0) {
2922 d.place( cont[i], node, "last");
2923 }
2924 } else {
2925 // pass nodes, documentFragments and unknowns through to dojo.place
2926 d.place(cont, node, "last");
2927 }
2928 }
2929
2930 // return DomNode
2931 return node;
2932 };
2933
2934 // we wrap up the content-setting operation in a object
2935 dojo.declare("dojo.html._ContentSetter", null,
2936 {
2937 // node: DomNode|String
2938 // An node which will be the parent element that we set content into
2939 node: "",
2940
2941 // content: String|DomNode|DomNode[]
2942 // The content to be placed in the node. Can be an HTML string, a node reference, or a enumerable list of nodes
2943 content: "",
2944
2945 // id: String?
2946 // Usually only used internally, and auto-generated with each instance
2947 id: "",
2948
2949 // cleanContent: Boolean
2950 // Should the content be treated as a full html document,
2951 // and the real content stripped of <html>, <body> wrapper before injection
2952 cleanContent: false,
2953
2954 // extractContent: Boolean
2955 // Should the content be treated as a full html document, and the real content stripped of <html>, <body> wrapper before injection
2956 extractContent: false,
2957
2958 // parseContent: Boolean
2959 // Should the node by passed to the parser after the new content is set
2960 parseContent: false,
2961
2962 // lifecyle methods
2963 constructor: function(/* Object */params, /* String|DomNode */node){
2964 // summary:
2965 // Provides a configurable, extensible object to wrap the setting on content on a node
2966 // call the set() method to actually set the content..
2967
2968 // the original params are mixed directly into the instance "this"
2969 dojo.mixin(this, params || {});
2970
2971 // give precedence to params.node vs. the node argument
2972 // and ensure its a node, not an id string
2973 node = this.node = dojo.byId( this.node || node );
2974
2975 if(!this.id){
2976 this.id = [
2977 "Setter",
2978 (node) ? node.id || node.tagName : "",
2979 idCounter++
2980 ].join("_");
2981 }
2982 },
2983 set: function(/* String|DomNode|NodeList? */ cont, /* Object? */ params){
2984 // summary:
2985 // front-end to the set-content sequence
2986 // cont:
2987 // An html string, node or enumerable list of nodes for insertion into the dom
2988 // If not provided, the object's content property will be used
2989 if(undefined !== cont){
2990 this.content = cont;
2991 }
2992 // in the re-use scenario, set needs to be able to mixin new configuration
2993 if(params){
2994 this._mixin(params);
2995 }
2996
2997 this.onBegin();
2998 this.setContent();
2999 this.onEnd();
3000
3001 return this.node;
3002 },
3003 setContent: function(){
3004 // summary:
3005 // sets the content on the node
3006
3007 var node = this.node;
3008 if(!node) {
3009 // can't proceed
3010 throw new Error(this.declaredClass + ": setContent given no node");
3011 }
3012 try{
3013 node = dojo.html._setNodeContent(node, this.content);
3014 }catch(e){
3015 // check if a domfault occurs when we are appending this.errorMessage
3016 // like for instance if domNode is a UL and we try append a DIV
3017
3018 // FIXME: need to allow the user to provide a content error message string
3019 var errMess = this.onContentError(e);
3020 try{
3021 node.innerHTML = errMess;
3022 }catch(e){
3023 console.error('Fatal ' + this.declaredClass + '.setContent could not change content due to '+e.message, e);
3024 }
3025 }
3026 // always put back the node for the next method
3027 this.node = node; // DomNode
3028 },
3029
3030 empty: function() {
3031 // summary
3032 // cleanly empty out existing content
3033
3034 // destroy any widgets from a previous run
3035 // NOTE: if you dont want this you'll need to empty
3036 // the parseResults array property yourself to avoid bad things happenning
3037 if(this.parseResults && this.parseResults.length) {
3038 dojo.forEach(this.parseResults, function(w) {
3039 if(w.destroy){
3040 w.destroy();
3041 }
3042 });
3043 delete this.parseResults;
3044 }
3045 // this is fast, but if you know its already empty or safe, you could
3046 // override empty to skip this step
3047 dojo.html._emptyNode(this.node);
3048 },
3049
3050 onBegin: function(){
3051 // summary
3052 // Called after instantiation, but before set();
3053 // It allows modification of any of the object properties
3054 // - including the node and content provided - before the set operation actually takes place
3055 // This default implementation checks for cleanContent and extractContent flags to
3056 // optionally pre-process html string content
3057 var cont = this.content;
3058
3059 if(dojo.isString(cont)){
3060 if(this.cleanContent){
3061 cont = dojo.html._secureForInnerHtml(cont);
3062 }
3063
3064 if(this.extractContent){
3065 var match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
3066 if(match){ cont = match[1]; }
3067 }
3068 }
3069
3070 // clean out the node and any cruft associated with it - like widgets
3071 this.empty();
3072
3073 this.content = cont;
3074 return this.node; /* DomNode */
3075 },
3076
3077 onEnd: function(){
3078 // summary
3079 // Called after set(), when the new content has been pushed into the node
3080 // It provides an opportunity for post-processing before handing back the node to the caller
3081 // This default implementation checks a parseContent flag to optionally run the dojo parser over the new content
3082 if(this.parseContent){
3083 // populates this.parseResults if you need those..
3084 this._parse();
3085 }
3086 return this.node; /* DomNode */
3087 },
3088
3089 tearDown: function(){
3090 // summary
3091 // manually reset the Setter instance if its being re-used for example for another set()
3092 // description
3093 // tearDown() is not called automatically.
3094 // In normal use, the Setter instance properties are simply allowed to fall out of scope
3095 // but the tearDown method can be called to explicitly reset this instance.
3096 delete this.parseResults;
3097 delete this.node;
3098 delete this.content;
3099 },
3100
3101 onContentError: function(err){
3102 return "Error occured setting content: " + err;
3103 },
3104
3105 _mixin: function(params){
3106 // mix properties/methods into the instance
3107 // TODO: the intention with tearDown is to put the Setter's state
3108 // back to that of the original constructor (vs. deleting/resetting everything regardless of ctor params)
3109 // so we could do something here to move the original properties aside for later restoration
3110 var empty = {}, key;
3111 for(key in params){
3112 if(key in empty){ continue; }
3113 // TODO: here's our opportunity to mask the properties we dont consider configurable/overridable
3114 // .. but history shows we'll almost always guess wrong
3115 this[key] = params[key];
3116 }
3117 },
3118 _parse: function(){
3119 // summary:
3120 // runs the dojo parser over the node contents, storing any results in this.parseResults
3121 // Any errors resulting from parsing are passed to _onError for handling
3122
3123 var rootNode = this.node;
3124 try{
3125 // store the results (widgets, whatever) for potential retrieval
3126 this.parseResults = dojo.parser.parse({
3127 rootNode: rootNode,
3128 dir: this.dir,
3129 lang: this.lang
3130 });
3131 }catch(e){
3132 this._onError('Content', e, "Error parsing in _ContentSetter#"+this.id);
3133 }
3134 },
3135
3136 _onError: function(type, err, consoleText){
3137 // summary:
3138 // shows user the string that is returned by on[type]Error
3139 // overide/implement on[type]Error and return your own string to customize
3140 var errText = this['on' + type + 'Error'].call(this, err);
3141 if(consoleText){
3142 console.error(consoleText, err);
3143 }else if(errText){ // a empty string won't change current content
3144 dojo.html._setNodeContent(this.node, errText, true);
3145 }
3146 }
3147 }); // end dojo.declare()
3148
3149 dojo.html.set = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont, /* Object? */ params){
3150 // summary:
3151 // inserts (replaces) the given content into the given node. dojo.place(cont, node, "only")
3152 // may be a better choice for simple HTML insertion.
3153 // description:
3154 // Unless you need to use the params capabilities of this method, you should use
3155 // dojo.place(cont, node, "only"). dojo.place() has more robust support for injecting
3156 // an HTML string into the DOM, but it only handles inserting an HTML string as DOM
3157 // elements, or inserting a DOM node. dojo.place does not handle NodeList insertions
3158 // or the other capabilities as defined by the params object for this method.
3159 // node:
3160 // the parent element that will receive the content
3161 // cont:
3162 // the content to be set on the parent element.
3163 // This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
3164 // params:
3165 // Optional flags/properties to configure the content-setting. See dojo.html._ContentSetter
3166 // example:
3167 // A safe string/node/nodelist content replacement/injection with hooks for extension
3168 // Example Usage:
3169 // dojo.html.set(node, "some string");
3170 // dojo.html.set(node, contentNode, {options});
3171 // dojo.html.set(node, myNode.childNodes, {options});
3172 if(undefined == cont){
3173 console.warn("dojo.html.set: no cont argument provided, using empty string");
3174 cont = "";
3175 }
3176 if(!params){
3177 // simple and fast
3178 return dojo.html._setNodeContent(node, cont, true);
3179 }else{
3180 // more options but slower
3181 // note the arguments are reversed in order, to match the convention for instantiation via the parser
3182 var op = new dojo.html._ContentSetter(dojo.mixin(
3183 params,
3184 { content: cont, node: node }
3185 ));
3186 return op.set();
3187 }
3188 };
3189})();
3190
3191}
3192
3193if(!dojo._hasResource["dijit.layout.ContentPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
3194dojo._hasResource["dijit.layout.ContentPane"] = true;
3195dojo.provide("dijit.layout.ContentPane");
3196
3197
3198
3199 // for dijit.layout.marginBox2contentBox()
3200
3201
3202
3203
3204
3205
3206dojo.declare(
3207 "dijit.layout.ContentPane", dijit._Widget,
3208{
3209 // summary:
3210 // A widget that acts as a container for mixed HTML and widgets, and includes an Ajax interface
3211 // description:
3212 // A widget that can be used as a stand alone widget
3213 // or as a base class for other widgets.
3214 //
3215 // Handles replacement of document fragment using either external uri or javascript
3216 // generated markup or DOM content, instantiating widgets within that content.
3217 // Don't confuse it with an iframe, it only needs/wants document fragments.
3218 // It's useful as a child of LayoutContainer, SplitContainer, or TabContainer.
3219 // But note that those classes can contain any widget as a child.
3220 // example:
3221 // Some quick samples:
3222 // To change the innerHTML use .set('content', '<b>new content</b>')
3223 //
3224 // Or you can send it a NodeList, .set('content', dojo.query('div [class=selected]', userSelection))
3225 // please note that the nodes in NodeList will copied, not moved
3226 //
3227 // To do a ajax update use .set('href', url)
3228
3229 // href: String
3230 // The href of the content that displays now.
3231 // Set this at construction if you want to load data externally when the
3232 // pane is shown. (Set preload=true to load it immediately.)
3233 // Changing href after creation doesn't have any effect; Use set('href', ...);
3234 href: "",
3235
3236/*=====
3237 // content: String || DomNode || NodeList || dijit._Widget
3238 // The innerHTML of the ContentPane.
3239 // Note that the initialization parameter / argument to attr("content", ...)
3240 // can be a String, DomNode, Nodelist, or _Widget.
3241 content: "",
3242=====*/
3243
3244 // extractContent: Boolean
3245 // Extract visible content from inside of <body> .... </body>.
3246 // I.e., strip <html> and <head> (and it's contents) from the href
3247 extractContent: false,
3248
3249 // parseOnLoad: Boolean
3250 // Parse content and create the widgets, if any.
3251 parseOnLoad: true,
3252
3253 // preventCache: Boolean
3254 // Prevent caching of data from href's by appending a timestamp to the href.
3255 preventCache: false,
3256
3257 // preload: Boolean
3258 // Force load of data on initialization even if pane is hidden.
3259 preload: false,
3260
3261 // refreshOnShow: Boolean
3262 // Refresh (re-download) content when pane goes from hidden to shown
3263 refreshOnShow: false,
3264
3265 // loadingMessage: String
3266 // Message that shows while downloading
3267 loadingMessage: "<span class='dijitContentPaneLoading'>${loadingState}</span>",
3268
3269 // errorMessage: String
3270 // Message that shows if an error occurs
3271 errorMessage: "<span class='dijitContentPaneError'>${errorState}</span>",
3272
3273 // isLoaded: [readonly] Boolean
3274 // True if the ContentPane has data in it, either specified
3275 // during initialization (via href or inline content), or set
3276 // via attr('content', ...) / attr('href', ...)
3277 //
3278 // False if it doesn't have any content, or if ContentPane is
3279 // still in the process of downloading href.
3280 isLoaded: false,
3281
3282 baseClass: "dijitContentPane",
3283
3284 // doLayout: Boolean
3285 // - false - don't adjust size of children
3286 // - true - if there is a single visible child widget, set it's size to
3287 // however big the ContentPane is
3288 doLayout: true,
3289
3290 // ioArgs: Object
3291 // Parameters to pass to xhrGet() request, for example:
3292 // | <div dojoType="dijit.layout.ContentPane" href="./bar" ioArgs="{timeout: 500}">
3293 ioArgs: {},
3294
3295 // isContainer: [protected] Boolean
3296 // Indicates that this widget acts as a "parent" to the descendant widgets.
3297 // When the parent is started it will call startup() on the child widgets.
3298 // See also `isLayoutContainer`.
3299 isContainer: true,
3300
3301 // isLayoutContainer: [protected] Boolean
3302 // Indicates that this widget will call resize() on it's child widgets
3303 // when they become visible.
3304 isLayoutContainer: true,
3305
3306 // onLoadDeferred: [readonly] dojo.Deferred
3307 // This is the `dojo.Deferred` returned by attr('href', ...) and refresh().
3308 // Calling onLoadDeferred.addCallback() or addErrback() registers your
3309 // callback to be called only once, when the prior attr('href', ...) call or
3310 // the initial href parameter to the constructor finishes loading.
3311 //
3312 // This is different than an onLoad() handler which gets called any time any href is loaded.
3313 onLoadDeferred: null,
3314
3315 // Override _Widget's attributeMap because we don't want the title attribute (used to specify
3316 // tab labels) to be copied to ContentPane.domNode... otherwise a tooltip shows up over the
3317 // entire pane.
3318 attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
3319 title: []
3320 }),
3321
3322 postMixInProperties: function(){
3323 this.inherited(arguments);
3324 var messages = dojo.i18n.getLocalization("dijit", "loading", this.lang);
3325 this.loadingMessage = dojo.string.substitute(this.loadingMessage, messages);
3326 this.errorMessage = dojo.string.substitute(this.errorMessage, messages);
3327
3328 // Detect if we were initialized with data
3329 if(!this.href && this.srcNodeRef && this.srcNodeRef.innerHTML){
3330 this.isLoaded = true;
3331 }
3332 },
3333
3334 buildRendering: function(){
3335 // Overrides Widget.buildRendering().
3336 // Since we have no template we need to set this.containerNode ourselves.
3337 // For subclasses of ContentPane do have a template, does nothing.
3338 this.inherited(arguments);
3339 if(!this.containerNode){
3340 // make getDescendants() work
3341 this.containerNode = this.domNode;
3342 }
3343 },
3344
3345 postCreate: function(){
3346 // remove the title attribute so it doesn't show up when hovering
3347 // over a node
3348 this.domNode.title = "";
3349
3350 if(!dojo.attr(this.domNode,"role")){
3351 dijit.setWaiRole(this.domNode, "group");
3352 }
3353
3354 dojo.addClass(this.domNode, this.baseClass);
3355 },
3356
3357 startup: function(){
3358 // summary:
3359 // See `dijit.layout._LayoutWidget.startup` for description.
3360 // Although ContentPane doesn't extend _LayoutWidget, it does implement
3361 // the same API.
3362 if(this._started){ return; }
3363
3364 var parent = dijit._Contained.prototype.getParent.call(this);
3365 this._childOfLayoutWidget = parent && parent.isLayoutContainer;
3366
3367 // I need to call resize() on my child/children (when I become visible), unless
3368 // I'm the child of a layout widget in which case my parent will call resize() on me and I'll do it then.
3369 this._needLayout = !this._childOfLayoutWidget;
3370
3371 if(this.isLoaded){
3372 dojo.forEach(this.getChildren(), function(child){
3373 child.startup();
3374 });
3375 }
3376
3377 if(this._isShown() || this.preload){
3378 this._onShow();
3379 }
3380
3381 this.inherited(arguments);
3382 },
3383
3384 _checkIfSingleChild: function(){
3385 // summary:
3386 // Test if we have exactly one visible widget as a child,
3387 // and if so assume that we are a container for that widget,
3388 // and should propogate startup() and resize() calls to it.
3389 // Skips over things like data stores since they aren't visible.
3390
3391 var childNodes = dojo.query("> *", this.containerNode).filter(function(node){
3392 return node.tagName !== "SCRIPT"; // or a regexp for hidden elements like script|area|map|etc..
3393 }),
3394 childWidgetNodes = childNodes.filter(function(node){
3395 return dojo.hasAttr(node, "dojoType") || dojo.hasAttr(node, "widgetId");
3396 }),
3397 candidateWidgets = dojo.filter(childWidgetNodes.map(dijit.byNode), function(widget){
3398 return widget && widget.domNode && widget.resize;
3399 });
3400
3401 if(
3402 // all child nodes are widgets
3403 childNodes.length == childWidgetNodes.length &&
3404
3405 // all but one are invisible (like dojo.data)
3406 candidateWidgets.length == 1
3407 ){
3408 this._singleChild = candidateWidgets[0];
3409 }else{
3410 delete this._singleChild;
3411 }
3412
3413 // So we can set overflow: hidden to avoid a safari bug w/scrollbars showing up (#9449)
3414 dojo.toggleClass(this.containerNode, this.baseClass + "SingleChild", !!this._singleChild);
3415 },
3416
3417 setHref: function(/*String|Uri*/ href){
3418 // summary:
3419 // Deprecated. Use set('href', ...) instead.
3420 dojo.deprecated("dijit.layout.ContentPane.setHref() is deprecated. Use set('href', ...) instead.", "", "2.0");
3421 return this.set("href", href);
3422 },
3423 _setHrefAttr: function(/*String|Uri*/ href){
3424 // summary:
3425 // Hook so attr("href", ...) works.
3426 // description:
3427 // Reset the (external defined) content of this pane and replace with new url
3428 // Note: It delays the download until widget is shown if preload is false.
3429 // href:
3430 // url to the page you want to get, must be within the same domain as your mainpage
3431
3432 // Cancel any in-flight requests (an attr('href') will cancel any in-flight attr('href', ...))
3433 this.cancel();
3434
3435 this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel"));
3436
3437 this.href = href;
3438
3439 // _setHrefAttr() is called during creation and by the user, after creation.
3440 // only in the second case do we actually load the URL; otherwise it's done in startup()
3441 if(this._created && (this.preload || this._isShown())){
3442 this._load();
3443 }else{
3444 // Set flag to indicate that href needs to be loaded the next time the
3445 // ContentPane is made visible
3446 this._hrefChanged = true;
3447 }
3448
3449 return this.onLoadDeferred; // dojo.Deferred
3450 },
3451
3452 setContent: function(/*String|DomNode|Nodelist*/data){
3453 // summary:
3454 // Deprecated. Use set('content', ...) instead.
3455 dojo.deprecated("dijit.layout.ContentPane.setContent() is deprecated. Use set('content', ...) instead.", "", "2.0");
3456 this.set("content", data);
3457 },
3458 _setContentAttr: function(/*String|DomNode|Nodelist*/data){
3459 // summary:
3460 // Hook to make attr("content", ...) work.
3461 // Replaces old content with data content, include style classes from old content
3462 // data:
3463 // the new Content may be String, DomNode or NodeList
3464 //
3465 // if data is a NodeList (or an array of nodes) nodes are copied
3466 // so you can import nodes from another document implicitly
3467
3468 // clear href so we can't run refresh and clear content
3469 // refresh should only work if we downloaded the content
3470 this.href = "";
3471
3472 // Cancel any in-flight requests (an attr('content') will cancel any in-flight attr('href', ...))
3473 this.cancel();
3474
3475 // Even though user is just setting content directly, still need to define an onLoadDeferred
3476 // because the _onLoadHandler() handler is still getting called from setContent()
3477 this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel"));
3478
3479 this._setContent(data || "");
3480
3481 this._isDownloaded = false; // mark that content is from a attr('content') not an attr('href')
3482
3483 return this.onLoadDeferred; // dojo.Deferred
3484 },
3485 _getContentAttr: function(){
3486 // summary:
3487 // Hook to make attr("content") work
3488 return this.containerNode.innerHTML;
3489 },
3490
3491 cancel: function(){
3492 // summary:
3493 // Cancels an in-flight download of content
3494 if(this._xhrDfd && (this._xhrDfd.fired == -1)){
3495 this._xhrDfd.cancel();
3496 }
3497 delete this._xhrDfd; // garbage collect
3498
3499 this.onLoadDeferred = null;
3500 },
3501
3502 uninitialize: function(){
3503 if(this._beingDestroyed){
3504 this.cancel();
3505 }
3506 this.inherited(arguments);
3507 },
3508
3509 destroyRecursive: function(/*Boolean*/ preserveDom){
3510 // summary:
3511 // Destroy the ContentPane and its contents
3512
3513 // if we have multiple controllers destroying us, bail after the first
3514 if(this._beingDestroyed){
3515 return;
3516 }
3517 this.inherited(arguments);
3518 },
3519
3520 resize: function(changeSize, resultSize){
3521 // summary:
3522 // See `dijit.layout._LayoutWidget.resize` for description.
3523 // Although ContentPane doesn't extend _LayoutWidget, it does implement
3524 // the same API.
3525
3526 // For the TabContainer --> BorderContainer --> ContentPane case, _onShow() is
3527 // never called, so resize() is our trigger to do the initial href download.
3528 if(!this._wasShown){
3529 this._onShow();
3530 }
3531
3532 this._resizeCalled = true;
3533
3534 // Set margin box size, unless it wasn't specified, in which case use current size.
3535 if(changeSize){
3536 dojo.marginBox(this.domNode, changeSize);
3537 }
3538
3539 // Compute content box size of containerNode in case we [later] need to size our single child.
3540 var cn = this.containerNode;
3541 if(cn === this.domNode){
3542 // If changeSize or resultSize was passed to this method and this.containerNode ==
3543 // this.domNode then we can compute the content-box size without querying the node,
3544 // which is more reliable (similar to LayoutWidget.resize) (see for example #9449).
3545 var mb = resultSize || {};
3546 dojo.mixin(mb, changeSize || {}); // changeSize overrides resultSize
3547 if(!("h" in mb) || !("w" in mb)){
3548 mb = dojo.mixin(dojo.marginBox(cn), mb); // just use dojo.marginBox() to fill in missing values
3549 }
3550 this._contentBox = dijit.layout.marginBox2contentBox(cn, mb);
3551 }else{
3552 this._contentBox = dojo.contentBox(cn);
3553 }
3554
3555 // Make my children layout, or size my single child widget
3556 this._layoutChildren();
3557 },
3558
3559 _isShown: function(){
3560 // summary:
3561 // Returns true if the content is currently shown.
3562 // description:
3563 // If I am a child of a layout widget then it actually returns true if I've ever been visible,
3564 // not whether I'm currently visible, since that's much faster than tracing up the DOM/widget
3565 // tree every call, and at least solves the performance problem on page load by deferring loading
3566 // hidden ContentPanes until they are first shown
3567
3568 if(this._childOfLayoutWidget){
3569 // If we are TitlePane, etc - we return that only *IF* we've been resized
3570 if(this._resizeCalled && "open" in this){
3571 return this.open;
3572 }
3573 return this._resizeCalled;
3574 }else if("open" in this){
3575 return this.open; // for TitlePane, etc.
3576 }else{
3577 // TODO: with _childOfLayoutWidget check maybe this branch no longer necessary?
3578 var node = this.domNode;
3579 return (node.style.display != 'none') && (node.style.visibility != 'hidden') && !dojo.hasClass(node, "dijitHidden");
3580 }
3581 },
3582
3583 _onShow: function(){
3584 // summary:
3585 // Called when the ContentPane is made visible
3586 // description:
3587 // For a plain ContentPane, this is called on initialization, from startup().
3588 // If the ContentPane is a hidden pane of a TabContainer etc., then it's
3589 // called whenever the pane is made visible.
3590 //
3591 // Does necessary processing, including href download and layout/resize of
3592 // child widget(s)
3593
3594 if(this.href){
3595 if(!this._xhrDfd && // if there's an href that isn't already being loaded
3596 (!this.isLoaded || this._hrefChanged || this.refreshOnShow)
3597 ){
3598 this.refresh();
3599 }
3600 }else{
3601 // If we are the child of a layout widget then the layout widget will call resize() on
3602 // us, and then we will size our child/children. Otherwise, we need to do it now.
3603 if(!this._childOfLayoutWidget && this._needLayout){
3604 // If a layout has been scheduled for when we become visible, do it now
3605 this._layoutChildren();
3606 }
3607 }
3608
3609 this.inherited(arguments);
3610
3611 // Need to keep track of whether ContentPane has been shown (which is different than
3612 // whether or not it's currently visible).
3613 this._wasShown = true;
3614 },
3615
3616 refresh: function(){
3617 // summary:
3618 // [Re]download contents of href and display
3619 // description:
3620 // 1. cancels any currently in-flight requests
3621 // 2. posts "loading..." message
3622 // 3. sends XHR to download new data
3623
3624 // Cancel possible prior in-flight request
3625 this.cancel();
3626
3627 this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel"));
3628 this._load();
3629 return this.onLoadDeferred;
3630 },
3631
3632 _load: function(){
3633 // summary:
3634 // Load/reload the href specified in this.href
3635
3636 // display loading message
3637 this._setContent(this.onDownloadStart(), true);
3638
3639 var self = this;
3640 var getArgs = {
3641 preventCache: (this.preventCache || this.refreshOnShow),
3642 url: this.href,
3643 handleAs: "text"
3644 };
3645 if(dojo.isObject(this.ioArgs)){
3646 dojo.mixin(getArgs, this.ioArgs);
3647 }
3648
3649 var hand = (this._xhrDfd = (this.ioMethod || dojo.xhrGet)(getArgs));
3650
3651 hand.addCallback(function(html){
3652 try{
3653 self._isDownloaded = true;
3654 self._setContent(html, false);
3655 self.onDownloadEnd();
3656 }catch(err){
3657 self._onError('Content', err); // onContentError
3658 }
3659 delete self._xhrDfd;
3660 return html;
3661 });
3662
3663 hand.addErrback(function(err){
3664 if(!hand.canceled){
3665 // show error message in the pane
3666 self._onError('Download', err); // onDownloadError
3667 }
3668 delete self._xhrDfd;
3669 return err;
3670 });
3671
3672 // Remove flag saying that a load is needed
3673 delete this._hrefChanged;
3674 },
3675
3676 _onLoadHandler: function(data){
3677 // summary:
3678 // This is called whenever new content is being loaded
3679 this.isLoaded = true;
3680 try{
3681 this.onLoadDeferred.callback(data);
3682 this.onLoad(data);
3683 }catch(e){
3684 console.error('Error '+this.widgetId+' running custom onLoad code: ' + e.message);
3685 }
3686 },
3687
3688 _onUnloadHandler: function(){
3689 // summary:
3690 // This is called whenever the content is being unloaded
3691 this.isLoaded = false;
3692 try{
3693 this.onUnload();
3694 }catch(e){
3695 console.error('Error '+this.widgetId+' running custom onUnload code: ' + e.message);
3696 }
3697 },
3698
3699 destroyDescendants: function(){
3700 // summary:
3701 // Destroy all the widgets inside the ContentPane and empty containerNode
3702
3703 // Make sure we call onUnload (but only when the ContentPane has real content)
3704 if(this.isLoaded){
3705 this._onUnloadHandler();
3706 }
3707
3708 // Even if this.isLoaded == false there might still be a "Loading..." message
3709 // to erase, so continue...
3710
3711 // For historical reasons we need to delete all widgets under this.containerNode,
3712 // even ones that the user has created manually.
3713 var setter = this._contentSetter;
3714 dojo.forEach(this.getChildren(), function(widget){
3715 if(widget.destroyRecursive){
3716 widget.destroyRecursive();
3717 }
3718 });
3719 if(setter){
3720 // Most of the widgets in setter.parseResults have already been destroyed, but
3721 // things like Menu that have been moved to <body> haven't yet
3722 dojo.forEach(setter.parseResults, function(widget){
3723 if(widget.destroyRecursive && widget.domNode && widget.domNode.parentNode == dojo.body()){
3724 widget.destroyRecursive();
3725 }
3726 });
3727 delete setter.parseResults;
3728 }
3729
3730 // And then clear away all the DOM nodes
3731 dojo.html._emptyNode(this.containerNode);
3732
3733 // Delete any state information we have about current contents
3734 delete this._singleChild;
3735 },
3736
3737 _setContent: function(cont, isFakeContent){
3738 // summary:
3739 // Insert the content into the container node
3740
3741 // first get rid of child widgets
3742 this.destroyDescendants();
3743
3744 // dojo.html.set will take care of the rest of the details
3745 // we provide an override for the error handling to ensure the widget gets the errors
3746 // configure the setter instance with only the relevant widget instance properties
3747 // NOTE: unless we hook into attr, or provide property setters for each property,
3748 // we need to re-configure the ContentSetter with each use
3749 var setter = this._contentSetter;
3750 if(! (setter && setter instanceof dojo.html._ContentSetter)){
3751 setter = this._contentSetter = new dojo.html._ContentSetter({
3752 node: this.containerNode,
3753 _onError: dojo.hitch(this, this._onError),
3754 onContentError: dojo.hitch(this, function(e){
3755 // fires if a domfault occurs when we are appending this.errorMessage
3756 // like for instance if domNode is a UL and we try append a DIV
3757 var errMess = this.onContentError(e);
3758 try{
3759 this.containerNode.innerHTML = errMess;
3760 }catch(e){
3761 console.error('Fatal '+this.id+' could not change content due to '+e.message, e);
3762 }
3763 })/*,
3764 _onError */
3765 });
3766 };
3767
3768 var setterParams = dojo.mixin({
3769 cleanContent: this.cleanContent,
3770 extractContent: this.extractContent,
3771 parseContent: this.parseOnLoad,
3772 dir: this.dir,
3773 lang: this.lang
3774 }, this._contentSetterParams || {});
3775
3776 dojo.mixin(setter, setterParams);
3777
3778 setter.set( (dojo.isObject(cont) && cont.domNode) ? cont.domNode : cont );
3779
3780 // setter params must be pulled afresh from the ContentPane each time
3781 delete this._contentSetterParams;
3782
3783 if(!isFakeContent){
3784 // Startup each top level child widget (and they will start their children, recursively)
3785 dojo.forEach(this.getChildren(), function(child){
3786 // The parser has already called startup on all widgets *without* a getParent() method
3787 if(!this.parseOnLoad || child.getParent){
3788 child.startup();
3789 }
3790 }, this);
3791
3792 // Call resize() on each of my child layout widgets,
3793 // or resize() on my single child layout widget...
3794 // either now (if I'm currently visible)
3795 // or when I become visible
3796 this._scheduleLayout();
3797
3798 this._onLoadHandler(cont);
3799 }
3800 },
3801
3802 _onError: function(type, err, consoleText){
3803 this.onLoadDeferred.errback(err);
3804
3805 // shows user the string that is returned by on[type]Error
3806 // overide on[type]Error and return your own string to customize
3807 var errText = this['on' + type + 'Error'].call(this, err);
3808 if(consoleText){
3809 console.error(consoleText, err);
3810 }else if(errText){// a empty string won't change current content
3811 this._setContent(errText, true);
3812 }
3813 },
3814
3815 _scheduleLayout: function(){
3816 // summary:
3817 // Call resize() on each of my child layout widgets, either now
3818 // (if I'm currently visible) or when I become visible
3819 if(this._isShown()){
3820 this._layoutChildren();
3821 }else{
3822 this._needLayout = true;
3823 }
3824 },
3825
3826 _layoutChildren: function(){
3827 // summary:
3828 // Since I am a Container widget, each of my children expects me to
3829 // call resize() or layout() on them.
3830 // description:
3831 // Should be called on initialization and also whenever we get new content
3832 // (from an href, or from attr('content', ...))... but deferred until
3833 // the ContentPane is visible
3834
3835 if(this.doLayout){
3836 this._checkIfSingleChild();
3837 }
3838
3839 if(this._singleChild && this._singleChild.resize){
3840 var cb = this._contentBox || dojo.contentBox(this.containerNode);
3841
3842 // note: if widget has padding this._contentBox will have l and t set,
3843 // but don't pass them to resize() or it will doubly-offset the child
3844 this._singleChild.resize({w: cb.w, h: cb.h});
3845 }else{
3846 // All my child widgets are independently sized (rather than matching my size),
3847 // but I still need to call resize() on each child to make it layout.
3848 dojo.forEach(this.getChildren(), function(widget){
3849 if(widget.resize){
3850 widget.resize();
3851 }
3852 });
3853 }
3854 delete this._needLayout;
3855 },
3856
3857 // EVENT's, should be overide-able
3858 onLoad: function(data){
3859 // summary:
3860 // Event hook, is called after everything is loaded and widgetified
3861 // tags:
3862 // callback
3863 },
3864
3865 onUnload: function(){
3866 // summary:
3867 // Event hook, is called before old content is cleared
3868 // tags:
3869 // callback
3870 },
3871
3872 onDownloadStart: function(){
3873 // summary:
3874 // Called before download starts.
3875 // description:
3876 // The string returned by this function will be the html
3877 // that tells the user we are loading something.
3878 // Override with your own function if you want to change text.
3879 // tags:
3880 // extension
3881 return this.loadingMessage;
3882 },
3883
3884 onContentError: function(/*Error*/ error){
3885 // summary:
3886 // Called on DOM faults, require faults etc. in content.
3887 //
3888 // In order to display an error message in the pane, return
3889 // the error message from this method, as an HTML string.
3890 //
3891 // By default (if this method is not overriden), it returns
3892 // nothing, so the error message is just printed to the console.
3893 // tags:
3894 // extension
3895 },
3896
3897 onDownloadError: function(/*Error*/ error){
3898 // summary:
3899 // Called when download error occurs.
3900 //
3901 // In order to display an error message in the pane, return
3902 // the error message from this method, as an HTML string.
3903 //
3904 // Default behavior (if this method is not overriden) is to display
3905 // the error message inside the pane.
3906 // tags:
3907 // extension
3908 return this.errorMessage;
3909 },
3910
3911 onDownloadEnd: function(){
3912 // summary:
3913 // Called when download is finished.
3914 // tags:
3915 // callback
3916 }
3917});
3918
3919}
3920
3921if(!dojo._hasResource["dijit.TooltipDialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
3922dojo._hasResource["dijit.TooltipDialog"] = true;
3923dojo.provide("dijit.TooltipDialog");
3924
3925
3926
3927
3928
3929
3930dojo.declare(
3931 "dijit.TooltipDialog",
3932 [dijit.layout.ContentPane, dijit._Templated, dijit.form._FormMixin, dijit._DialogMixin],
3933 {
3934 // summary:
3935 // Pops up a dialog that appears like a Tooltip
3936
3937 // title: String
3938 // Description of tooltip dialog (required for a11y)
3939 title: "",
3940
3941 // doLayout: [protected] Boolean
3942 // Don't change this parameter from the default value.
3943 // This ContentPane parameter doesn't make sense for TooltipDialog, since TooltipDialog
3944 // is never a child of a layout container, nor can you specify the size of
3945 // TooltipDialog in order to control the size of an inner widget.
3946 doLayout: false,
3947
3948 // autofocus: Boolean
3949 // A Toggle to modify the default focus behavior of a Dialog, which
3950 // is to focus on the first dialog element after opening the dialog.
3951 // False will disable autofocusing. Default: true
3952 autofocus: true,
3953
3954 // baseClass: [protected] String
3955 // The root className to use for the various states of this widget
3956 baseClass: "dijitTooltipDialog",
3957
3958 // _firstFocusItem: [private] [readonly] DomNode
3959 // The pointer to the first focusable node in the dialog.
3960 // Set by `dijit._DialogMixin._getFocusItems`.
3961 _firstFocusItem: null,
3962
3963 // _lastFocusItem: [private] [readonly] DomNode
3964 // The pointer to which node has focus prior to our dialog.
3965 // Set by `dijit._DialogMixin._getFocusItems`.
3966 _lastFocusItem: null,
3967
3968 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"),
3969
3970 postCreate: function(){
3971 this.inherited(arguments);
3972 this.connect(this.containerNode, "onkeypress", "_onKey");
3973 this.containerNode.title = this.title;
3974 },
3975
3976 orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ corner){
3977 // summary:
3978 // Configure widget to be displayed in given position relative to the button.
3979 // This is called from the dijit.popup code, and should not be called
3980 // directly.
3981 // tags:
3982 // protected
3983 var c = this._currentOrientClass;
3984 if(c){
3985 dojo.removeClass(this.domNode, c);
3986 }
3987 c = "dijitTooltipAB"+(corner.charAt(1) == 'L'?"Left":"Right")+" dijitTooltip"+(corner.charAt(0) == 'T' ? "Below" : "Above");
3988 dojo.addClass(this.domNode, c);
3989 this._currentOrientClass = c;
3990 },
3991
3992 onOpen: function(/*Object*/ pos){
3993 // summary:
3994 // Called when dialog is displayed.
3995 // This is called from the dijit.popup code, and should not be called directly.
3996 // tags:
3997 // protected
3998
3999 this.orient(this.domNode,pos.aroundCorner, pos.corner);
4000 this._onShow(); // lazy load trigger
4001
4002 if(this.autofocus){
4003 this._getFocusItems(this.containerNode);
4004 dijit.focus(this._firstFocusItem);
4005 }
4006 },
4007
4008 onClose: function(){
4009 // summary:
4010 // Called when dialog is hidden.
4011 // This is called from the dijit.popup code, and should not be called directly.
4012 // tags:
4013 // protected
4014 this.onHide();
4015 },
4016
4017 _onKey: function(/*Event*/ evt){
4018 // summary:
4019 // Handler for keyboard events
4020 // description:
4021 // Keep keyboard focus in dialog; close dialog on escape key
4022 // tags:
4023 // private
4024
4025 var node = evt.target;
4026 var dk = dojo.keys;
4027 if(evt.charOrCode === dk.TAB){
4028 this._getFocusItems(this.containerNode);
4029 }
4030 var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
4031 if(evt.charOrCode == dk.ESCAPE){
4032 // Use setTimeout to avoid crash on IE, see #10396.
4033 setTimeout(dojo.hitch(this, "onCancel"), 0);
4034 dojo.stopEvent(evt);
4035 }else if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === dk.TAB){
4036 if(!singleFocusItem){
4037 dijit.focus(this._lastFocusItem); // send focus to last item in dialog
4038 }
4039 dojo.stopEvent(evt);
4040 }else if(node == this._lastFocusItem && evt.charOrCode === dk.TAB && !evt.shiftKey){
4041 if(!singleFocusItem){
4042 dijit.focus(this._firstFocusItem); // send focus to first item in dialog
4043 }
4044 dojo.stopEvent(evt);
4045 }else if(evt.charOrCode === dk.TAB){
4046 // we want the browser's default tab handling to move focus
4047 // but we don't want the tab to propagate upwards
4048 evt.stopPropagation();
4049 }
4050 }
4051 }
4052 );
4053
4054}
4055
4056if(!dojo._hasResource["dijit.Dialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
4057dojo._hasResource["dijit.Dialog"] = true;
4058dojo.provide("dijit.Dialog");
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074/*=====
4075dijit._underlay = function(kwArgs){
4076 // summary:
4077 // A shared instance of a `dijit.DialogUnderlay`
4078 //
4079 // description:
4080 // A shared instance of a `dijit.DialogUnderlay` created and
4081 // used by `dijit.Dialog`, though never created until some Dialog
4082 // or subclass thereof is shown.
4083};
4084=====*/
4085
4086dojo.declare(
4087 "dijit._DialogBase",
4088 [dijit._Templated, dijit.form._FormMixin, dijit._DialogMixin, dijit._CssStateMixin],
4089 {
4090 // summary:
4091 // A modal dialog Widget
4092 //
4093 // description:
4094 // Pops up a modal dialog window, blocking access to the screen
4095 // and also graying out the screen Dialog is extended from
4096 // ContentPane so it supports all the same parameters (href, etc.)
4097 //
4098 // example:
4099 // | <div dojoType="dijit.Dialog" href="test.html"></div>
4100 //
4101 // example:
4102 // | var foo = new dijit.Dialog({ title: "test dialog", content: "test content" };
4103 // | dojo.body().appendChild(foo.domNode);
4104 // | foo.startup();
4105
4106 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"),
4107
4108 baseClass: "dijitDialog",
4109
4110 cssStateNodes: {
4111 closeButtonNode: "dijitDialogCloseIcon"
4112 },
4113
4114 attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
4115 title: [
4116 { node: "titleNode", type: "innerHTML" },
4117 { node: "titleBar", type: "attribute" }
4118 ],
4119 "aria-describedby":""
4120 }),
4121
4122 // open: Boolean
4123 // True if Dialog is currently displayed on screen.
4124 open: false,
4125
4126 // duration: Integer
4127 // The time in milliseconds it takes the dialog to fade in and out
4128 duration: dijit.defaultDuration,
4129
4130 // refocus: Boolean
4131 // A Toggle to modify the default focus behavior of a Dialog, which
4132 // is to re-focus the element which had focus before being opened.
4133 // False will disable refocusing. Default: true
4134 refocus: true,
4135
4136 // autofocus: Boolean
4137 // A Toggle to modify the default focus behavior of a Dialog, which
4138 // is to focus on the first dialog element after opening the dialog.
4139 // False will disable autofocusing. Default: true
4140 autofocus: true,
4141
4142 // _firstFocusItem: [private] [readonly] DomNode
4143 // The pointer to the first focusable node in the dialog.
4144 // Set by `dijit._DialogMixin._getFocusItems`.
4145 _firstFocusItem: null,
4146
4147 // _lastFocusItem: [private] [readonly] DomNode
4148 // The pointer to which node has focus prior to our dialog.
4149 // Set by `dijit._DialogMixin._getFocusItems`.
4150 _lastFocusItem: null,
4151
4152 // doLayout: [protected] Boolean
4153 // Don't change this parameter from the default value.
4154 // This ContentPane parameter doesn't make sense for Dialog, since Dialog
4155 // is never a child of a layout container, nor can you specify the size of
4156 // Dialog in order to control the size of an inner widget.
4157 doLayout: false,
4158
4159 // draggable: Boolean
4160 // Toggles the moveable aspect of the Dialog. If true, Dialog
4161 // can be dragged by it's title. If false it will remain centered
4162 // in the viewport.
4163 draggable: true,
4164
4165 //aria-describedby: String
4166 // Allows the user to add an aria-describedby attribute onto the dialog. The value should
4167 // be the id of the container element of text that describes the dialog purpose (usually
4168 // the first text in the dialog).
4169 // <div dojoType="dijit.Dialog" aria-describedby="intro" .....>
4170 // <div id="intro">Introductory text</div>
4171 // <div>rest of dialog contents</div>
4172 // </div>
4173 "aria-describedby":"",
4174
4175 postMixInProperties: function(){
4176 var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
4177 dojo.mixin(this, _nlsResources);
4178 this.inherited(arguments);
4179 },
4180
4181 postCreate: function(){
4182 dojo.style(this.domNode, {
4183 display: "none",
4184 position:"absolute"
4185 });
4186 dojo.body().appendChild(this.domNode);
4187
4188 this.inherited(arguments);
4189
4190 this.connect(this, "onExecute", "hide");
4191 this.connect(this, "onCancel", "hide");
4192 this._modalconnects = [];
4193 },
4194
4195 onLoad: function(){
4196 // summary:
4197 // Called when data has been loaded from an href.
4198 // Unlike most other callbacks, this function can be connected to (via `dojo.connect`)
4199 // but should *not* be overriden.
4200 // tags:
4201 // callback
4202
4203 // when href is specified we need to reposition the dialog after the data is loaded
4204 // and find the focusable elements
4205 this._position();
4206 if(this.autofocus){
4207 this._getFocusItems(this.domNode);
4208 dijit.focus(this._firstFocusItem);
4209 }
4210 this.inherited(arguments);
4211 },
4212
4213 _endDrag: function(e){
4214 // summary:
4215 // Called after dragging the Dialog. Saves the position of the dialog in the viewport.
4216 // tags:
4217 // private
4218 if(e && e.node && e.node === this.domNode){
4219 this._relativePosition = dojo.position(e.node);
4220 }
4221 },
4222
4223 _setup: function(){
4224 // summary:
4225 // Stuff we need to do before showing the Dialog for the first
4226 // time (but we defer it until right beforehand, for
4227 // performance reasons).
4228 // tags:
4229 // private
4230
4231 var node = this.domNode;
4232
4233 if(this.titleBar && this.draggable){
4234 this._moveable = (dojo.isIE == 6) ?
4235 new dojo.dnd.TimedMoveable(node, { handle: this.titleBar }) : // prevent overload, see #5285
4236 new dojo.dnd.Moveable(node, { handle: this.titleBar, timeout: 0 });
4237 dojo.subscribe("/dnd/move/stop",this,"_endDrag");
4238 }else{
4239 dojo.addClass(node,"dijitDialogFixed");
4240 }
4241
4242 this.underlayAttrs = {
4243 dialogId: this.id,
4244 "class": dojo.map(this["class"].split(/\s/), function(s){ return s+"_underlay"; }).join(" ")
4245 };
4246
4247 this._fadeIn = dojo.fadeIn({
4248 node: node,
4249 duration: this.duration,
4250 beforeBegin: dojo.hitch(this, function(){
4251 var underlay = dijit._underlay;
4252 if(!underlay){
4253 underlay = dijit._underlay = new dijit.DialogUnderlay(this.underlayAttrs);
4254 }else{
4255 underlay.set(this.underlayAttrs);
4256 }
4257
4258 var ds = dijit._dialogStack,
4259 zIndex = 948 + ds.length*2;
4260 if(ds.length == 1){ // first dialog
4261 underlay.show();
4262 }
4263 dojo.style(dijit._underlay.domNode, 'zIndex', zIndex);
4264 dojo.style(this.domNode, 'zIndex', zIndex + 1);
4265 }),
4266 onEnd: dojo.hitch(this, function(){
4267 if(this.autofocus){
4268 // find focusable Items each time dialog is shown since if dialog contains a widget the
4269 // first focusable items can change
4270 this._getFocusItems(this.domNode);
4271 dijit.focus(this._firstFocusItem);
4272 }
4273 })
4274 });
4275
4276 this._fadeOut = dojo.fadeOut({
4277 node: node,
4278 duration: this.duration,
4279 onEnd: dojo.hitch(this, function(){
4280 node.style.display = "none";
4281
4282 // Restore the previous dialog in the stack, or if this is the only dialog
4283 // then restore to original page
4284 var ds = dijit._dialogStack;
4285 if(ds.length == 0){
4286 dijit._underlay.hide();
4287 }else{
4288 dojo.style(dijit._underlay.domNode, 'zIndex', 948 + ds.length*2);
4289 dijit._underlay.set(ds[ds.length-1].underlayAttrs);
4290 }
4291
4292 // Restore focus to wherever it was before this dialog was displayed
4293 if(this.refocus){
4294 var focus = this._savedFocus;
4295
4296 // If we are returning control to a previous dialog but for some reason
4297 // that dialog didn't have a focused field, set focus to first focusable item.
4298 // This situation could happen if two dialogs appeared at nearly the same time,
4299 // since a dialog doesn't set it's focus until the fade-in is finished.
4300 if(ds.length > 0){
4301 var pd = ds[ds.length-1];
4302 if(!dojo.isDescendant(focus.node, pd.domNode)){
4303 pd._getFocusItems(pd.domNode);
4304 focus = pd._firstFocusItem;
4305 }
4306 }
4307
4308 dijit.focus(focus);
4309 }
4310 })
4311 });
4312 },
4313
4314 uninitialize: function(){
4315 var wasPlaying = false;
4316 if(this._fadeIn && this._fadeIn.status() == "playing"){
4317 wasPlaying = true;
4318 this._fadeIn.stop();
4319 }
4320 if(this._fadeOut && this._fadeOut.status() == "playing"){
4321 wasPlaying = true;
4322 this._fadeOut.stop();
4323 }
4324
4325 // Hide the underlay, unless the underlay widget has already been destroyed
4326 // because we are being called during page unload (when all widgets are destroyed)
4327 if((this.open || wasPlaying) && !dijit._underlay._destroyed){
4328 dijit._underlay.hide();
4329 }
4330
4331 if(this._moveable){
4332 this._moveable.destroy();
4333 }
4334 this.inherited(arguments);
4335 },
4336
4337 _size: function(){
4338 // summary:
4339 // If necessary, shrink dialog contents so dialog fits in viewport
4340 // tags:
4341 // private
4342
4343 this._checkIfSingleChild();
4344
4345 // If we resized the dialog contents earlier, reset them back to original size, so
4346 // that if the user later increases the viewport size, the dialog can display w/out a scrollbar.
4347 // Need to do this before the dojo.marginBox(this.domNode) call below.
4348 if(this._singleChild){
4349 if(this._singleChildOriginalStyle){
4350 this._singleChild.domNode.style.cssText = this._singleChildOriginalStyle;
4351 }
4352 delete this._singleChildOriginalStyle;
4353 }else{
4354 dojo.style(this.containerNode, {
4355 width:"auto",
4356 height:"auto"
4357 });
4358 }
4359
4360 var mb = dojo.marginBox(this.domNode);
4361 var viewport = dojo.window.getBox();
4362 if(mb.w >= viewport.w || mb.h >= viewport.h){
4363 // Reduce size of dialog contents so that dialog fits in viewport
4364
4365 var w = Math.min(mb.w, Math.floor(viewport.w * 0.75)),
4366 h = Math.min(mb.h, Math.floor(viewport.h * 0.75));
4367
4368 if(this._singleChild && this._singleChild.resize){
4369 this._singleChildOriginalStyle = this._singleChild.domNode.style.cssText;
4370 this._singleChild.resize({w: w, h: h});
4371 }else{
4372 dojo.style(this.containerNode, {
4373 width: w + "px",
4374 height: h + "px",
4375 overflow: "auto",
4376 position: "relative" // workaround IE bug moving scrollbar or dragging dialog
4377 });
4378 }
4379 }else{
4380 if(this._singleChild && this._singleChild.resize){
4381 this._singleChild.resize();
4382 }
4383 }
4384 },
4385
4386 _position: function(){
4387 // summary:
4388 // Position modal dialog in the viewport. If no relative offset
4389 // in the viewport has been determined (by dragging, for instance),
4390 // center the node. Otherwise, use the Dialog's stored relative offset,
4391 // and position the node to top: left: values based on the viewport.
4392 // tags:
4393 // private
4394 if(!dojo.hasClass(dojo.body(),"dojoMove")){
4395 var node = this.domNode,
4396 viewport = dojo.window.getBox(),
4397 p = this._relativePosition,
4398 bb = p ? null : dojo._getBorderBox(node),
4399 l = Math.floor(viewport.l + (p ? p.x : (viewport.w - bb.w) / 2)),
4400 t = Math.floor(viewport.t + (p ? p.y : (viewport.h - bb.h) / 2))
4401 ;
4402 dojo.style(node,{
4403 left: l + "px",
4404 top: t + "px"
4405 });
4406 }
4407 },
4408
4409 _onKey: function(/*Event*/ evt){
4410 // summary:
4411 // Handles the keyboard events for accessibility reasons
4412 // tags:
4413 // private
4414
4415 var ds = dijit._dialogStack;
4416 if(ds[ds.length-1] != this){
4417 // console.debug(this.id + ': skipping because', this, 'is not the active dialog');
4418 return;
4419 }
4420
4421 if(evt.charOrCode){
4422 var dk = dojo.keys;
4423 var node = evt.target;
4424 if(evt.charOrCode === dk.TAB){
4425 this._getFocusItems(this.domNode);
4426 }
4427 var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
4428 // see if we are shift-tabbing from first focusable item on dialog
4429 if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === dk.TAB){
4430 if(!singleFocusItem){
4431 dijit.focus(this._lastFocusItem); // send focus to last item in dialog
4432 }
4433 dojo.stopEvent(evt);
4434 }else if(node == this._lastFocusItem && evt.charOrCode === dk.TAB && !evt.shiftKey){
4435 if(!singleFocusItem){
4436 dijit.focus(this._firstFocusItem); // send focus to first item in dialog
4437 }
4438 dojo.stopEvent(evt);
4439 }else{
4440 // see if the key is for the dialog
4441 while(node){
4442 if(node == this.domNode || dojo.hasClass(node, "dijitPopup")){
4443 if(evt.charOrCode == dk.ESCAPE){
4444 this.onCancel();
4445 }else{
4446 return; // just let it go
4447 }
4448 }
4449 node = node.parentNode;
4450 }
4451 // this key is for the disabled document window
4452 if(evt.charOrCode !== dk.TAB){ // allow tabbing into the dialog for a11y
4453 dojo.stopEvent(evt);
4454 // opera won't tab to a div
4455 }else if(!dojo.isOpera){
4456 try{
4457 this._firstFocusItem.focus();
4458 }catch(e){ /*squelch*/ }
4459 }
4460 }
4461 }
4462 },
4463
4464 show: function(){
4465 // summary:
4466 // Display the dialog
4467 if(this.open){ return; }
4468
4469 // first time we show the dialog, there's some initialization stuff to do
4470 if(!this._alreadyInitialized){
4471 this._setup();
4472 this._alreadyInitialized=true;
4473 }
4474
4475 if(this._fadeOut.status() == "playing"){
4476 this._fadeOut.stop();
4477 }
4478
4479 this._modalconnects.push(dojo.connect(window, "onscroll", this, "layout"));
4480 this._modalconnects.push(dojo.connect(window, "onresize", this, function(){
4481 // IE gives spurious resize events and can actually get stuck
4482 // in an infinite loop if we don't ignore them
4483 var viewport = dojo.window.getBox();
4484 if(!this._oldViewport ||
4485 viewport.h != this._oldViewport.h ||
4486 viewport.w != this._oldViewport.w){
4487 this.layout();
4488 this._oldViewport = viewport;
4489 }
4490 }));
4491 this._modalconnects.push(dojo.connect(dojo.doc.documentElement, "onkeypress", this, "_onKey"));
4492
4493 dojo.style(this.domNode, {
4494 opacity:0,
4495 display:""
4496 });
4497
4498 this.open = true;
4499 this._onShow(); // lazy load trigger
4500
4501 this._size();
4502 this._position();
4503 dijit._dialogStack.push(this);
4504 this._fadeIn.play();
4505
4506 this._savedFocus = dijit.getFocus(this);
4507 },
4508
4509 hide: function(){
4510 // summary:
4511 // Hide the dialog
4512
4513 // if we haven't been initialized yet then we aren't showing and we can just return
4514 // or if we aren't the active dialog, don't allow us to close yet
4515 var ds = dijit._dialogStack;
4516 if(!this._alreadyInitialized || this != ds[ds.length-1]){
4517 return;
4518 }
4519
4520 if(this._fadeIn.status() == "playing"){
4521 this._fadeIn.stop();
4522 }
4523
4524 // throw away current active dialog from stack -- making the previous dialog or the node on the original page active
4525 ds.pop();
4526
4527 this._fadeOut.play();
4528
4529 if(this._scrollConnected){
4530 this._scrollConnected = false;
4531 }
4532 dojo.forEach(this._modalconnects, dojo.disconnect);
4533 this._modalconnects = [];
4534
4535 if(this._relativePosition){
4536 delete this._relativePosition;
4537 }
4538 this.open = false;
4539
4540 this.onHide();
4541 },
4542
4543 layout: function(){
4544 // summary:
4545 // Position the Dialog and the underlay
4546 // tags:
4547 // private
4548 if(this.domNode.style.display != "none"){
4549 if(dijit._underlay){ // avoid race condition during show()
4550 dijit._underlay.layout();
4551 }
4552 this._position();
4553 }
4554 },
4555
4556 destroy: function(){
4557 dojo.forEach(this._modalconnects, dojo.disconnect);
4558 if(this.refocus && this.open){
4559 setTimeout(dojo.hitch(dijit,"focus",this._savedFocus), 25);
4560 }
4561 this.inherited(arguments);
4562 }
4563 }
4564);
4565
4566dojo.declare(
4567 "dijit.Dialog",
4568 [dijit.layout.ContentPane, dijit._DialogBase],
4569 {}
4570);
4571
4572// Stack of currenctly displayed dialogs, layered on top of each other
4573dijit._dialogStack = [];
4574
4575// For back-compat. TODO: remove in 2.0
4576
4577
4578}
4579
4580if(!dojo._hasResource["dijit._editor.selection"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
4581dojo._hasResource["dijit._editor.selection"] = true;
4582dojo.provide("dijit._editor.selection");
4583
4584// FIXME:
4585// all of these methods branch internally for IE. This is probably
4586// sub-optimal in terms of runtime performance. We should investigate the
4587// size difference for differentiating at definition time.
4588
4589dojo.mixin(dijit._editor.selection, {
4590 getType: function(){
4591 // summary:
4592 // Get the selection type (like dojo.doc.select.type in IE).
4593 if(dojo.isIE){
4594 return dojo.doc.selection.type.toLowerCase();
4595 }else{
4596 var stype = "text";
4597
4598 // Check if the actual selection is a CONTROL (IMG, TABLE, HR, etc...).
4599 var oSel;
4600 try{
4601 oSel = dojo.global.getSelection();
4602 }catch(e){ /*squelch*/ }
4603
4604 if(oSel && oSel.rangeCount == 1){
4605 var oRange = oSel.getRangeAt(0);
4606 if( (oRange.startContainer == oRange.endContainer) &&
4607 ((oRange.endOffset - oRange.startOffset) == 1) &&
4608 (oRange.startContainer.nodeType != 3 /* text node*/)
4609 ){
4610 stype = "control";
4611 }
4612 }
4613 return stype; //String
4614 }
4615 },
4616
4617 getSelectedText: function(){
4618 // summary:
4619 // Return the text (no html tags) included in the current selection or null if no text is selected
4620 if(dojo.isIE){
4621 if(dijit._editor.selection.getType() == 'control'){
4622 return null;
4623 }
4624 return dojo.doc.selection.createRange().text;
4625 }else{
4626 var selection = dojo.global.getSelection();
4627 if(selection){
4628 return selection.toString(); //String
4629 }
4630 }
4631 return '';
4632 },
4633
4634 getSelectedHtml: function(){
4635 // summary:
4636 // Return the html text of the current selection or null if unavailable
4637 if(dojo.isIE){
4638 if(dijit._editor.selection.getType() == 'control'){
4639 return null;
4640 }
4641 return dojo.doc.selection.createRange().htmlText;
4642 }else{
4643 var selection = dojo.global.getSelection();
4644 if(selection && selection.rangeCount){
4645 var i;
4646 var html = "";
4647 for(i = 0; i < selection.rangeCount; i++){
4648 //Handle selections spanning ranges, such as Opera
4649 var frag = selection.getRangeAt(i).cloneContents();
4650 var div = dojo.doc.createElement("div");
4651 div.appendChild(frag);
4652 html += div.innerHTML;
4653 }
4654 return html; //String
4655 }
4656 return null;
4657 }
4658 },
4659
4660 getSelectedElement: function(){
4661 // summary:
4662 // Retrieves the selected element (if any), just in the case that
4663 // a single element (object like and image or a table) is
4664 // selected.
4665 if(dijit._editor.selection.getType() == "control"){
4666 if(dojo.isIE){
4667 var range = dojo.doc.selection.createRange();
4668 if(range && range.item){
4669 return dojo.doc.selection.createRange().item(0);
4670 }
4671 }else{
4672 var selection = dojo.global.getSelection();
4673 return selection.anchorNode.childNodes[ selection.anchorOffset ];
4674 }
4675 }
4676 return null;
4677 },
4678
4679 getParentElement: function(){
4680 // summary:
4681 // Get the parent element of the current selection
4682 if(dijit._editor.selection.getType() == "control"){
4683 var p = this.getSelectedElement();
4684 if(p){ return p.parentNode; }
4685 }else{
4686 if(dojo.isIE){
4687 var r = dojo.doc.selection.createRange();
4688 r.collapse(true);
4689 return r.parentElement();
4690 }else{
4691 var selection = dojo.global.getSelection();
4692 if(selection){
4693 var node = selection.anchorNode;
4694 while(node && (node.nodeType != 1)){ // not an element
4695 node = node.parentNode;
4696 }
4697 return node;
4698 }
4699 }
4700 }
4701 return null;
4702 },
4703
4704 hasAncestorElement: function(/*String*/tagName /* ... */){
4705 // summary:
4706 // Check whether current selection has a parent element which is
4707 // of type tagName (or one of the other specified tagName)
4708 // tagName: String
4709 // The tag name to determine if it has an ancestor of.
4710 return this.getAncestorElement.apply(this, arguments) != null; //Boolean
4711 },
4712
4713 getAncestorElement: function(/*String*/tagName /* ... */){
4714 // summary:
4715 // Return the parent element of the current selection which is of
4716 // type tagName (or one of the other specified tagName)
4717 // tagName: String
4718 // The tag name to determine if it has an ancestor of.
4719 var node = this.getSelectedElement() || this.getParentElement();
4720 return this.getParentOfType(node, arguments); //DOMNode
4721 },
4722
4723 isTag: function(/*DomNode*/ node, /*String[]*/ tags){
4724 // summary:
4725 // Function to determine if a node is one of an array of tags.
4726 // node:
4727 // The node to inspect.
4728 // tags:
4729 // An array of tag name strings to check to see if the node matches.
4730 if(node && node.tagName){
4731 var _nlc = node.tagName.toLowerCase();
4732 for(var i=0; i<tags.length; i++){
4733 var _tlc = String(tags[i]).toLowerCase();
4734 if(_nlc == _tlc){
4735 return _tlc; // String
4736 }
4737 }
4738 }
4739 return "";
4740 },
4741
4742 getParentOfType: function(/*DomNode*/ node, /*String[]*/ tags){
4743 // summary:
4744 // Function to locate a parent node that matches one of a set of tags
4745 // node:
4746 // The node to inspect.
4747 // tags:
4748 // An array of tag name strings to check to see if the node matches.
4749 while(node){
4750 if(this.isTag(node, tags).length){
4751 return node; // DOMNode
4752 }
4753 node = node.parentNode;
4754 }
4755 return null;
4756 },
4757
4758 collapse: function(/*Boolean*/beginning){
4759 // summary:
4760 // Function to collapse (clear), the current selection
4761 // beginning: Boolean
4762 // Boolean to indicate whether to collapse the cursor to the beginning of the selection or end.
4763 if(window.getSelection){
4764 var selection = dojo.global.getSelection();
4765 if(selection.removeAllRanges){ // Mozilla
4766 if(beginning){
4767 selection.collapseToStart();
4768 }else{
4769 selection.collapseToEnd();
4770 }
4771 }else{ // Safari
4772 // pulled from WebCore/ecma/kjs_window.cpp, line 2536
4773 selection.collapse(beginning);
4774 }
4775 }else if(dojo.isIE){ // IE
4776 var range = dojo.doc.selection.createRange();
4777 range.collapse(beginning);
4778 range.select();
4779 }
4780 },
4781
4782 remove: function(){
4783 // summary:
4784 // Function to delete the currently selected content from the document.
4785 var sel = dojo.doc.selection;
4786 if(dojo.isIE){
4787 if(sel.type.toLowerCase() != "none"){
4788 sel.clear();
4789 }
4790 return sel; //Selection
4791 }else{
4792 sel = dojo.global.getSelection();
4793 sel.deleteFromDocument();
4794 return sel; //Selection
4795 }
4796 },
4797
4798 selectElementChildren: function(/*DomNode*/element,/*Boolean?*/nochangefocus){
4799 // summary:
4800 // clear previous selection and select the content of the node
4801 // (excluding the node itself)
4802 // element: DOMNode
4803 // The element you wish to select the children content of.
4804 // nochangefocus: Boolean
4805 // Boolean to indicate if the foxus should change or not.
4806 var win = dojo.global;
4807 var doc = dojo.doc;
4808 var range;
4809 element = dojo.byId(element);
4810 if(doc.selection && dojo.isIE && dojo.body().createTextRange){ // IE
4811 range = element.ownerDocument.body.createTextRange();
4812 range.moveToElementText(element);
4813 if(!nochangefocus){
4814 try{
4815 range.select(); // IE throws an exception here if the widget is hidden. See #5439
4816 }catch(e){ /* squelch */}
4817 }
4818 }else if(win.getSelection){
4819 var selection = dojo.global.getSelection();
4820 if(dojo.isOpera){
4821 //Opera's selectAllChildren doesn't seem to work right
4822 //against <body> nodes and possibly others ... so
4823 //we use the W3C range API
4824 if(selection.rangeCount){
4825 range = selection.getRangeAt(0);
4826 }else{
4827 range = doc.createRange();
4828 }
4829 range.setStart(element, 0);
4830 range.setEnd(element,(element.nodeType == 3)?element.length:element.childNodes.length);
4831 selection.addRange(range);
4832 }else{
4833 selection.selectAllChildren(element);
4834 }
4835 }
4836 },
4837
4838 selectElement: function(/*DomNode*/element,/*Boolean?*/nochangefocus){
4839 // summary:
4840 // clear previous selection and select element (including all its children)
4841 // element: DOMNode
4842 // The element to select.
4843 // nochangefocus: Boolean
4844 // Boolean indicating if the focus should be changed. IE only.
4845 var range;
4846 var doc = dojo.doc;
4847 var win = dojo.global;
4848 element = dojo.byId(element);
4849 if(dojo.isIE && dojo.body().createTextRange){
4850 try{
4851 range = dojo.body().createControlRange();
4852 range.addElement(element);
4853 if(!nochangefocus){
4854 range.select();
4855 }
4856 }catch(e){
4857 this.selectElementChildren(element,nochangefocus);
4858 }
4859 }else if(dojo.global.getSelection){
4860 var selection = win.getSelection();
4861 range = doc.createRange();
4862 if(selection.removeAllRanges){ // Mozilla
4863 // FIXME: does this work on Safari?
4864 if(dojo.isOpera){
4865 //Opera works if you use the current range on
4866 //the selection if present.
4867 if(selection.getRangeAt(0)){
4868 range = selection.getRangeAt(0);
4869 }
4870 }
4871 range.selectNode(element);
4872 selection.removeAllRanges();
4873 selection.addRange(range);
4874 }
4875 }
4876 },
4877
4878 inSelection: function(node){
4879 // summary:
4880 // This function determines if 'node' is
4881 // in the current selection.
4882 // tags:
4883 // public
4884 if(node){
4885 var newRange;
4886 var doc = dojo.doc;
4887 var range;
4888
4889 if(dojo.global.getSelection){
4890 //WC3
4891 var sel = dojo.global.getSelection();
4892 if(sel && sel.rangeCount > 0){
4893 range = sel.getRangeAt(0);
4894 }
4895 if(range && range.compareBoundaryPoints && doc.createRange){
4896 try{
4897 newRange = doc.createRange();
4898 newRange.setStart(node, 0);
4899 if(range.compareBoundaryPoints(range.START_TO_END, newRange) === 1){
4900 return true;
4901 }
4902 }catch(e){ /* squelch */}
4903 }
4904 }else if(doc.selection){
4905 // Probably IE, so we can't use the range object as the pseudo
4906 // range doesn't implement the boundry checking, we have to
4907 // use IE specific crud.
4908 range = doc.selection.createRange();
4909 try{
4910 newRange = node.ownerDocument.body.createControlRange();
4911 if(newRange){
4912 newRange.addElement(node);
4913 }
4914 }catch(e1){
4915 try{
4916 newRange = node.ownerDocument.body.createTextRange();
4917 newRange.moveToElementText(node);
4918 }catch(e2){/* squelch */}
4919 }
4920 if(range && newRange){
4921 // We can finally compare similar to W3C
4922 if(range.compareEndPoints("EndToStart", newRange) === 1){
4923 return true;
4924 }
4925 }
4926 }
4927 }
4928 return false; // boolean
4929 }
4930
4931});
4932
4933}
4934
4935if(!dojo._hasResource["dijit._editor.range"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
4936dojo._hasResource["dijit._editor.range"] = true;
4937dojo.provide("dijit._editor.range");
4938
4939dijit.range={};
4940
4941dijit.range.getIndex=function(/*DomNode*/node, /*DomNode*/parent){
4942// dojo.profile.start("dijit.range.getIndex");
4943 var ret=[], retR=[];
4944 var stop = parent;
4945 var onode = node;
4946
4947 var pnode, n;
4948 while(node != stop){
4949 var i = 0;
4950 pnode = node.parentNode;
4951 while((n=pnode.childNodes[i++])){
4952 if(n === node){
4953 --i;
4954 break;
4955 }
4956 }
4957 //if(i>=pnode.childNodes.length){
4958 //dojo.debug("Error finding index of a node in dijit.range.getIndex");
4959 //}
4960 ret.unshift(i);
4961 retR.unshift(i-pnode.childNodes.length);
4962 node = pnode;
4963 }
4964
4965 //normalized() can not be called so often to prevent
4966 //invalidating selection/range, so we have to detect
4967 //here that any text nodes in a row
4968 if(ret.length > 0 && onode.nodeType == 3){
4969 n = onode.previousSibling;
4970 while(n && n.nodeType == 3){
4971 ret[ret.length-1]--;
4972 n = n.previousSibling;
4973 }
4974 n = onode.nextSibling;
4975 while(n && n.nodeType == 3){
4976 retR[retR.length-1]++;
4977 n = n.nextSibling;
4978 }
4979 }
4980// dojo.profile.end("dijit.range.getIndex");
4981 return {o: ret, r:retR};
4982}
4983
4984dijit.range.getNode = function(/*Array*/index, /*DomNode*/parent){
4985 if(!dojo.isArray(index) || index.length == 0){
4986 return parent;
4987 }
4988 var node = parent;
4989// if(!node)debugger
4990 dojo.every(index, function(i){
4991 if(i >= 0 && i < node.childNodes.length){
4992 node = node.childNodes[i];
4993 }else{
4994 node = null;
4995 //console.debug('Error: can not find node with index',index,'under parent node',parent );
4996 return false; //terminate dojo.every
4997 }
4998 return true; //carry on the every loop
4999 });
5000
5001 return node;
5002}
5003
5004dijit.range.getCommonAncestor = function(n1,n2,root){
5005 root = root||n1.ownerDocument.body;
5006 var getAncestors = function(n){
5007 var as=[];
5008 while(n){
5009 as.unshift(n);
5010 if(n !== root){
5011 n = n.parentNode;
5012 }else{
5013 break;
5014 }
5015 }
5016 return as;
5017 };
5018 var n1as = getAncestors(n1);
5019 var n2as = getAncestors(n2);
5020
5021 var m = Math.min(n1as.length,n2as.length);
5022 var com = n1as[0]; //at least, one element should be in the array: the root (BODY by default)
5023 for(var i=1;i<m;i++){
5024 if(n1as[i] === n2as[i]){
5025 com = n1as[i]
5026 }else{
5027 break;
5028 }
5029 }
5030 return com;
5031}
5032
5033dijit.range.getAncestor = function(/*DomNode*/node, /*RegEx?*/regex, /*DomNode?*/root){
5034 root = root || node.ownerDocument.body;
5035 while(node && node !== root){
5036 var name = node.nodeName.toUpperCase() ;
5037 if(regex.test(name)){
5038 return node;
5039 }
5040
5041 node = node.parentNode;
5042 }
5043 return null;
5044}
5045
5046dijit.range.BlockTagNames = /^(?:P|DIV|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI|DT|DE)$/;
5047dijit.range.getBlockAncestor = function(/*DomNode*/node, /*RegEx?*/regex, /*DomNode?*/root){
5048 root = root || node.ownerDocument.body;
5049 regex = regex || dijit.range.BlockTagNames;
5050 var block=null, blockContainer;
5051 while(node && node !== root){
5052 var name = node.nodeName.toUpperCase() ;
5053 if(!block && regex.test(name)){
5054 block = node;
5055 }
5056 if(!blockContainer && (/^(?:BODY|TD|TH|CAPTION)$/).test(name)){
5057 blockContainer = node;
5058 }
5059
5060 node = node.parentNode;
5061 }
5062 return {blockNode:block, blockContainer:blockContainer || node.ownerDocument.body};
5063}
5064
5065dijit.range.atBeginningOfContainer = function(/*DomNode*/container, /*DomNode*/node, /*Int*/offset){
5066 var atBeginning = false;
5067 var offsetAtBeginning = (offset == 0);
5068 if(!offsetAtBeginning && node.nodeType == 3){ //if this is a text node, check whether the left part is all space
5069 if(/^[\s\xA0]+$/.test(node.nodeValue.substr(0,offset))){
5070 offsetAtBeginning = true;
5071 }
5072 }
5073 if(offsetAtBeginning){
5074 var cnode = node;
5075 atBeginning = true;
5076 while(cnode && cnode !== container){
5077 if(cnode.previousSibling){
5078 atBeginning = false;
5079 break;
5080 }
5081 cnode = cnode.parentNode;
5082 }
5083 }
5084 return atBeginning;
5085}
5086
5087dijit.range.atEndOfContainer = function(/*DomNode*/container, /*DomNode*/node, /*Int*/offset){
5088 var atEnd = false;
5089 var offsetAtEnd = (offset == (node.length || node.childNodes.length));
5090 if(!offsetAtEnd && node.nodeType == 3){ //if this is a text node, check whether the right part is all space
5091 if(/^[\s\xA0]+$/.test(node.nodeValue.substr(offset))){
5092 offsetAtEnd = true;
5093 }
5094 }
5095 if(offsetAtEnd){
5096 var cnode = node;
5097 atEnd = true;
5098 while(cnode && cnode !== container){
5099 if(cnode.nextSibling){
5100 atEnd = false;
5101 break;
5102 }
5103 cnode = cnode.parentNode;
5104 }
5105 }
5106 return atEnd;
5107}
5108
5109dijit.range.adjacentNoneTextNode=function(startnode, next){
5110 var node = startnode;
5111 var len = (0-startnode.length) || 0;
5112 var prop = next?'nextSibling':'previousSibling';
5113 while(node){
5114 if(node.nodeType!=3){
5115 break;
5116 }
5117 len += node.length
5118 node = node[prop];
5119 }
5120 return [node,len];
5121}
5122
5123dijit.range._w3c = Boolean(window['getSelection']);
5124dijit.range.create = function(/*Window?*/win){
5125 if(dijit.range._w3c){
5126 return (win || dojo.global).document.createRange();
5127 }else{//IE
5128 return new dijit.range.W3CRange;
5129 }
5130}
5131
5132dijit.range.getSelection = function(/*Window*/win, /*Boolean?*/ignoreUpdate){
5133 if(dijit.range._w3c){
5134 return win.getSelection();
5135 }else{//IE
5136 var s = new dijit.range.ie.selection(win);
5137 if(!ignoreUpdate){
5138 s._getCurrentSelection();
5139 }
5140 return s;
5141 }
5142}
5143
5144if(!dijit.range._w3c){
5145 dijit.range.ie={
5146 cachedSelection: {},
5147 selection: function(win){
5148 this._ranges = [];
5149 this.addRange = function(r, /*boolean*/internal){
5150 this._ranges.push(r);
5151 if(!internal){
5152 r._select();
5153 }
5154 this.rangeCount = this._ranges.length;
5155 };
5156 this.removeAllRanges = function(){
5157 //don't detach, the range may be used later
5158// for(var i=0;i<this._ranges.length;i++){
5159// this._ranges[i].detach();
5160// }
5161 this._ranges = [];
5162 this.rangeCount = 0;
5163 };
5164 var _initCurrentRange = function(){
5165 var r = win.document.selection.createRange();
5166 var type=win.document.selection.type.toUpperCase();
5167 if(type == "CONTROL"){
5168 //TODO: multiple range selection(?)
5169 return new dijit.range.W3CRange(dijit.range.ie.decomposeControlRange(r));
5170 }else{
5171 return new dijit.range.W3CRange(dijit.range.ie.decomposeTextRange(r));
5172 }
5173 };
5174 this.getRangeAt = function(i){
5175 return this._ranges[i];
5176 };
5177 this._getCurrentSelection = function(){
5178 this.removeAllRanges();
5179 var r=_initCurrentRange();
5180 if(r){
5181 this.addRange(r, true);
5182 }
5183 };
5184 },
5185 decomposeControlRange: function(range){
5186 var firstnode = range.item(0), lastnode = range.item(range.length-1);
5187 var startContainer = firstnode.parentNode, endContainer = lastnode.parentNode;
5188 var startOffset = dijit.range.getIndex(firstnode, startContainer).o;
5189 var endOffset = dijit.range.getIndex(lastnode, endContainer).o+1;
5190 return [startContainer, startOffset,endContainer, endOffset];
5191 },
5192 getEndPoint: function(range, end){
5193 var atmrange = range.duplicate();
5194 atmrange.collapse(!end);
5195 var cmpstr = 'EndTo' + (end?'End':'Start');
5196 var parentNode = atmrange.parentElement();
5197
5198 var startnode, startOffset, lastNode;
5199 if(parentNode.childNodes.length>0){
5200 dojo.every(parentNode.childNodes, function(node,i){
5201 var calOffset;
5202 if(node.nodeType != 3){
5203 atmrange.moveToElementText(node);
5204
5205 if(atmrange.compareEndPoints(cmpstr,range) > 0){
5206 //startnode = node.previousSibling;
5207 if(lastNode && lastNode.nodeType == 3){
5208 //where shall we put the start? in the text node or after?
5209 startnode = lastNode;
5210 calOffset = true;
5211 }else{
5212 startnode = parentNode;
5213 startOffset = i;
5214 return false;
5215 }
5216 }else{
5217 if(i == parentNode.childNodes.length-1){
5218 startnode = parentNode;
5219 startOffset = parentNode.childNodes.length;
5220 return false;
5221 }
5222 }
5223 }else{
5224 if(i == parentNode.childNodes.length-1){//at the end of this node
5225 startnode = node;
5226 calOffset = true;
5227 }
5228 }
5229 // try{
5230 if(calOffset && startnode){
5231 var prevnode = dijit.range.adjacentNoneTextNode(startnode)[0];
5232 if(prevnode){
5233 startnode = prevnode.nextSibling;
5234 }else{
5235 startnode = parentNode.firstChild; //firstChild must be a text node
5236 }
5237 var prevnodeobj = dijit.range.adjacentNoneTextNode(startnode);
5238 prevnode = prevnodeobj[0];
5239 var lenoffset = prevnodeobj[1];
5240 if(prevnode){
5241 atmrange.moveToElementText(prevnode);
5242 atmrange.collapse(false);
5243 }else{
5244 atmrange.moveToElementText(parentNode);
5245 }
5246 atmrange.setEndPoint(cmpstr, range);
5247 startOffset = atmrange.text.length-lenoffset;
5248
5249 return false;
5250 }
5251 // }catch(e){ debugger }
5252 lastNode = node;
5253 return true;
5254 });
5255 }else{
5256 startnode = parentNode;
5257 startOffset = 0;
5258 }
5259
5260 //if at the end of startnode and we are dealing with start container, then
5261 //move the startnode to nextSibling if it is a text node
5262 //TODO: do this for end container?
5263 if(!end && startnode.nodeType == 1 && startOffset == startnode.childNodes.length){
5264 var nextnode=startnode.nextSibling;
5265 if(nextnode && nextnode.nodeType == 3){
5266 startnode = nextnode;
5267 startOffset = 0;
5268 }
5269 }
5270 return [startnode, startOffset];
5271 },
5272 setEndPoint: function(range, container, offset){
5273 //text node
5274 var atmrange = range.duplicate(), node, len;
5275 if(container.nodeType!=3){ //normal node
5276 if(offset > 0){
5277 node = container.childNodes[offset-1];
5278 if(node){
5279 if(node.nodeType == 3){
5280 container = node;
5281 offset = node.length;
5282 //pass through
5283 }else{
5284 if(node.nextSibling && node.nextSibling.nodeType == 3){
5285 container=node.nextSibling;
5286 offset=0;
5287 //pass through
5288 }else{
5289 atmrange.moveToElementText(node.nextSibling?node:container);
5290 var parent = node.parentNode;
5291 var tempNode = parent.insertBefore(node.ownerDocument.createTextNode(' '), node.nextSibling);
5292 atmrange.collapse(false);
5293 parent.removeChild(tempNode);
5294 }
5295 }
5296 }
5297 }else{
5298 atmrange.moveToElementText(container);
5299 atmrange.collapse(true);
5300 }
5301 }
5302 if(container.nodeType == 3){
5303 var prevnodeobj = dijit.range.adjacentNoneTextNode(container);
5304 var prevnode = prevnodeobj[0];
5305 len = prevnodeobj[1];
5306 if(prevnode){
5307 atmrange.moveToElementText(prevnode);
5308 atmrange.collapse(false);
5309 //if contentEditable is not inherit, the above collapse won't make the end point
5310 //in the correctly position: it always has a -1 offset, so compensate it
5311 if(prevnode.contentEditable!='inherit'){
5312 len++;
5313 }
5314 }else{
5315 atmrange.moveToElementText(container.parentNode);
5316 atmrange.collapse(true);
5317 }
5318
5319 offset += len;
5320 if(offset>0){
5321 if(atmrange.move('character',offset) != offset){
5322 console.error('Error when moving!');
5323 }
5324 }
5325 }
5326
5327 return atmrange;
5328 },
5329 decomposeTextRange: function(range){
5330 var tmpary = dijit.range.ie.getEndPoint(range);
5331 var startContainer = tmpary[0], startOffset = tmpary[1];
5332 var endContainer = tmpary[0], endOffset = tmpary[1];
5333
5334 if(range.htmlText.length){
5335 if(range.htmlText == range.text){ //in the same text node
5336 endOffset = startOffset+range.text.length;
5337 }else{
5338 tmpary = dijit.range.ie.getEndPoint(range,true);
5339 endContainer = tmpary[0], endOffset = tmpary[1];
5340// if(startContainer.tagName == "BODY"){
5341// startContainer = startContainer.firstChild;
5342// }
5343 }
5344 }
5345 return [startContainer, startOffset, endContainer, endOffset];
5346 },
5347 setRange: function(range, startContainer,
5348 startOffset, endContainer, endOffset, collapsed){
5349 var start=dijit.range.ie.setEndPoint(range, startContainer, startOffset);
5350
5351 range.setEndPoint('StartToStart',start);
5352 if(!collapsed){
5353 var end=dijit.range.ie.setEndPoint(range, endContainer, endOffset);
5354 }
5355 range.setEndPoint('EndToEnd',end || start);
5356
5357 return range;
5358 }
5359 }
5360
5361dojo.declare("dijit.range.W3CRange",null, {
5362 constructor: function(){
5363 if(arguments.length>0){
5364 this.setStart(arguments[0][0],arguments[0][1]);
5365 this.setEnd(arguments[0][2],arguments[0][3]);
5366 }else{
5367 this.commonAncestorContainer = null;
5368 this.startContainer = null;
5369 this.startOffset = 0;
5370 this.endContainer = null;
5371 this.endOffset = 0;
5372 this.collapsed = true;
5373 }
5374 },
5375 _updateInternal: function(){
5376 if(this.startContainer !== this.endContainer){
5377 this.commonAncestorContainer = dijit.range.getCommonAncestor(this.startContainer, this.endContainer);
5378 }else{
5379 this.commonAncestorContainer = this.startContainer;
5380 }
5381 this.collapsed = (this.startContainer === this.endContainer) && (this.startOffset == this.endOffset);
5382 },
5383 setStart: function(node, offset){
5384 offset=parseInt(offset);
5385 if(this.startContainer === node && this.startOffset == offset){
5386 return;
5387 }
5388 delete this._cachedBookmark;
5389
5390 this.startContainer = node;
5391 this.startOffset = offset;
5392 if(!this.endContainer){
5393 this.setEnd(node, offset);
5394 }else{
5395 this._updateInternal();
5396 }
5397 },
5398 setEnd: function(node, offset){
5399 offset=parseInt(offset);
5400 if(this.endContainer === node && this.endOffset == offset){
5401 return;
5402 }
5403 delete this._cachedBookmark;
5404
5405 this.endContainer = node;
5406 this.endOffset = offset;
5407 if(!this.startContainer){
5408 this.setStart(node, offset);
5409 }else{
5410 this._updateInternal();
5411 }
5412 },
5413 setStartAfter: function(node, offset){
5414 this._setPoint('setStart', node, offset, 1);
5415 },
5416 setStartBefore: function(node, offset){
5417 this._setPoint('setStart', node, offset, 0);
5418 },
5419 setEndAfter: function(node, offset){
5420 this._setPoint('setEnd', node, offset, 1);
5421 },
5422 setEndBefore: function(node, offset){
5423 this._setPoint('setEnd', node, offset, 0);
5424 },
5425 _setPoint: function(what, node, offset, ext){
5426 var index = dijit.range.getIndex(node, node.parentNode).o;
5427 this[what](node.parentNode, index.pop()+ext);
5428 },
5429 _getIERange: function(){
5430 var r = (this._body || this.endContainer.ownerDocument.body).createTextRange();
5431 dijit.range.ie.setRange(r, this.startContainer, this.startOffset, this.endContainer, this.endOffset, this.collapsed);
5432 return r;
5433 },
5434 getBookmark: function(body){
5435 this._getIERange();
5436 return this._cachedBookmark;
5437 },
5438 _select: function(){
5439 var r = this._getIERange();
5440 r.select();
5441 },
5442 deleteContents: function(){
5443 var r = this._getIERange();
5444 r.pasteHTML('');
5445 this.endContainer = this.startContainer;
5446 this.endOffset = this.startOffset;
5447 this.collapsed = true;
5448 },
5449 cloneRange: function(){
5450 var r = new dijit.range.W3CRange([this.startContainer,this.startOffset,
5451 this.endContainer,this.endOffset]);
5452 r._body = this._body;
5453 return r;
5454 },
5455 detach: function(){
5456 this._body = null;
5457 this.commonAncestorContainer = null;
5458 this.startContainer = null;
5459 this.startOffset = 0;
5460 this.endContainer = null;
5461 this.endOffset = 0;
5462 this.collapsed = true;
5463}
5464});
5465} //if(!dijit.range._w3c)
5466
5467}
5468
5469if(!dojo._hasResource["dijit._editor.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
5470dojo._hasResource["dijit._editor.html"] = true;
5471dojo.provide("dijit._editor.html");
5472
5473dijit._editor.escapeXml=function(/*String*/str, /*Boolean?*/noSingleQuotes){
5474 // summary:
5475 // Adds escape sequences for special characters in XML: &<>"'
5476 // Optionally skips escapes for single quotes
5477 str = str.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
5478 if(!noSingleQuotes){
5479 str = str.replace(/'/gm, "&#39;");
5480 }
5481 return str; // string
5482};
5483
5484dijit._editor.getNodeHtml=function(/* DomNode */node){
5485 var output;
5486 switch(node.nodeType){
5487 case 1: //element node
5488 var lName = node.nodeName.toLowerCase();
5489 if(!lName || lName.charAt(0) == "/"){
5490 // IE does some strange things with malformed HTML input, like
5491 // treating a close tag </span> without an open tag <span>, as
5492 // a new tag with tagName of /span. Corrupts output HTML, remove
5493 // them. Other browsers don't prefix tags that way, so will
5494 // never show up.
5495 return "";
5496 }
5497 output = '<' + lName;
5498
5499 //store the list of attributes and sort it to have the
5500 //attributes appear in the dictionary order
5501 var attrarray = [];
5502 var attr;
5503 if(dojo.isIE && node.outerHTML){
5504 var s = node.outerHTML;
5505 s = s.substr(0, s.indexOf('>'))
5506 .replace(/(['"])[^"']*\1/g, ''); //to make the following regexp safe
5507 var reg = /(\b\w+)\s?=/g;
5508 var m, key;
5509 while((m = reg.exec(s))){
5510 key = m[1];
5511 if(key.substr(0,3) != '_dj'){
5512 if(key == 'src' || key == 'href'){
5513 if(node.getAttribute('_djrealurl')){
5514 attrarray.push([key,node.getAttribute('_djrealurl')]);
5515 continue;
5516 }
5517 }
5518 var val, match;
5519 switch(key){
5520 case 'style':
5521 val = node.style.cssText.toLowerCase();
5522 break;
5523 case 'class':
5524 val = node.className;
5525 break;
5526 case 'width':
5527 if(lName === "img"){
5528 // This somehow gets lost on IE for IMG tags and the like
5529 // and we have to find it in outerHTML, known IE oddity.
5530 match=/width=(\S+)/i.exec(s);
5531 if(match){
5532 val = match[1];
5533 }
5534 break;
5535 }
5536 case 'height':
5537 if(lName === "img"){
5538 // This somehow gets lost on IE for IMG tags and the like
5539 // and we have to find it in outerHTML, known IE oddity.
5540 match=/height=(\S+)/i.exec(s);
5541 if(match){
5542 val = match[1];
5543 }
5544 break;
5545 }
5546 default:
5547 val = node.getAttribute(key);
5548 }
5549 if(val != null){
5550 attrarray.push([key, val.toString()]);
5551 }
5552 }
5553 }
5554 }else{
5555 var i = 0;
5556 while((attr = node.attributes[i++])){
5557 //ignore all attributes starting with _dj which are
5558 //internal temporary attributes used by the editor
5559 var n = attr.name;
5560 if(n.substr(0,3) != '_dj' /*&&
5561 (attr.specified == undefined || attr.specified)*/){
5562 var v = attr.value;
5563 if(n == 'src' || n == 'href'){
5564 if(node.getAttribute('_djrealurl')){
5565 v = node.getAttribute('_djrealurl');
5566 }
5567 }
5568 attrarray.push([n,v]);
5569 }
5570 }
5571 }
5572 attrarray.sort(function(a,b){
5573 return a[0] < b[0] ? -1 : (a[0] == b[0] ? 0 : 1);
5574 });
5575 var j = 0;
5576 while((attr = attrarray[j++])){
5577 output += ' ' + attr[0] + '="' +
5578 (dojo.isString(attr[1]) ? dijit._editor.escapeXml(attr[1], true) : attr[1]) + '"';
5579 }
5580 if(lName === "script"){
5581 // Browsers handle script tags differently in how you get content,
5582 // but innerHTML always seems to work, so insert its content that way
5583 // Yes, it's bad to allow script tags in the editor code, but some people
5584 // seem to want to do it, so we need to at least return them right.
5585 // other plugins/filters can strip them.
5586 output += '>' + node.innerHTML +'</' + lName + '>';
5587 }else{
5588 if(node.childNodes.length){
5589 output += '>' + dijit._editor.getChildrenHtml(node)+'</' + lName +'>';
5590 }else{
5591 switch(lName){
5592 case 'br':
5593 case 'hr':
5594 case 'img':
5595 case 'input':
5596 case 'base':
5597 case 'meta':
5598 case 'area':
5599 case 'basefont':
5600 // These should all be singly closed
5601 output += ' />';
5602 break;
5603 default:
5604 // Assume XML style separate closure for everything else.
5605 output += '></' + lName + '>';
5606 }
5607 }
5608 }
5609 break;
5610 case 4: // cdata
5611 case 3: // text
5612 // FIXME:
5613 output = dijit._editor.escapeXml(node.nodeValue, true);
5614 break;
5615 case 8: //comment
5616 // FIXME:
5617 output = '<!--' + dijit._editor.escapeXml(node.nodeValue, true) + '-->';
5618 break;
5619 default:
5620 output = "<!-- Element not recognized - Type: " + node.nodeType + " Name: " + node.nodeName + "-->";
5621 }
5622 return output;
5623};
5624
5625dijit._editor.getChildrenHtml = function(/* DomNode */dom){
5626 // summary:
5627 // Returns the html content of a DomNode and children
5628 var out = "";
5629 if(!dom){ return out; }
5630 var nodes = dom["childNodes"] || dom;
5631
5632 //IE issue.
5633 //If we have an actual node we can check parent relationships on for IE,
5634 //We should check, as IE sometimes builds invalid DOMS. If no parent, we can't check
5635 //And should just process it and hope for the best.
5636 var checkParent = !dojo.isIE || nodes !== dom;
5637
5638 var node, i = 0;
5639 while((node = nodes[i++])){
5640 //IE is broken. DOMs are supposed to be a tree. But in the case of malformed HTML, IE generates a graph
5641 //meaning one node ends up with multiple references (multiple parents). This is totally wrong and invalid, but
5642 //such is what it is. We have to keep track and check for this because otherise the source output HTML will have dups.
5643 //No other browser generates a graph. Leave it to IE to break a fundamental DOM rule. So, we check the parent if we can
5644 //If we can't, nothing more we can do other than walk it.
5645 if(!checkParent || node.parentNode == dom){
5646 out += dijit._editor.getNodeHtml(node);
5647 }
5648 }
5649 return out; // String
5650};
5651
5652}
5653
5654if(!dojo._hasResource["dijit._editor.RichText"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
5655dojo._hasResource["dijit._editor.RichText"] = true;
5656dojo.provide("dijit._editor.RichText");
5657
5658
5659
5660
5661
5662
5663
5664// used to restore content when user leaves this page then comes back
5665// but do not try doing dojo.doc.write if we are using xd loading.
5666// dojo.doc.write will only work if RichText.js is included in the dojo.js
5667// file. If it is included in dojo.js and you want to allow rich text saving
5668// for back/forward actions, then set dojo.config.allowXdRichTextSave = true.
5669if(!dojo.config["useXDomain"] || dojo.config["allowXdRichTextSave"]){
5670 if(dojo._postLoad){
5671 (function(){
5672 var savetextarea = dojo.doc.createElement('textarea');
5673 savetextarea.id = dijit._scopeName + "._editor.RichText.savedContent";
5674 dojo.style(savetextarea, {
5675 display:'none',
5676 position:'absolute',
5677 top:"-100px",
5678 height:"3px",
5679 width:"3px"
5680 });
5681 dojo.body().appendChild(savetextarea);
5682 })();
5683 }else{
5684 //dojo.body() is not available before onLoad is fired
5685 try{
5686 dojo.doc.write('<textarea id="' + dijit._scopeName + '._editor.RichText.savedContent" ' +
5687 'style="display:none;position:absolute;top:-100px;left:-100px;height:3px;width:3px;overflow:hidden;"></textarea>');
5688 }catch(e){ }
5689 }
5690}
5691
5692dojo.declare("dijit._editor.RichText", [dijit._Widget, dijit._CssStateMixin], {
5693 constructor: function(params){
5694 // summary:
5695 // dijit._editor.RichText is the core of dijit.Editor, which provides basic
5696 // WYSIWYG editing features.
5697 //
5698 // description:
5699 // dijit._editor.RichText is the core of dijit.Editor, which provides basic
5700 // WYSIWYG editing features. It also encapsulates the differences
5701 // of different js engines for various browsers. Do not use this widget
5702 // with an HTML &lt;TEXTAREA&gt; tag, since the browser unescapes XML escape characters,
5703 // like &lt;. This can have unexpected behavior and lead to security issues
5704 // such as scripting attacks.
5705 //
5706 // tags:
5707 // private
5708
5709 // contentPreFilters: Function(String)[]
5710 // Pre content filter function register array.
5711 // these filters will be executed before the actual
5712 // editing area gets the html content.
5713 this.contentPreFilters = [];
5714
5715 // contentPostFilters: Function(String)[]
5716 // post content filter function register array.
5717 // These will be used on the resulting html
5718 // from contentDomPostFilters. The resulting
5719 // content is the final html (returned by getValue()).
5720 this.contentPostFilters = [];
5721
5722 // contentDomPreFilters: Function(DomNode)[]
5723 // Pre content dom filter function register array.
5724 // These filters are applied after the result from
5725 // contentPreFilters are set to the editing area.
5726 this.contentDomPreFilters = [];
5727
5728 // contentDomPostFilters: Function(DomNode)[]
5729 // Post content dom filter function register array.
5730 // These filters are executed on the editing area dom.
5731 // The result from these will be passed to contentPostFilters.
5732 this.contentDomPostFilters = [];
5733
5734 // editingAreaStyleSheets: dojo._URL[]
5735 // array to store all the stylesheets applied to the editing area
5736 this.editingAreaStyleSheets = [];
5737
5738 // Make a copy of this.events before we start writing into it, otherwise we
5739 // will modify the prototype which leads to bad things on pages w/multiple editors
5740 this.events = [].concat(this.events);
5741
5742 this._keyHandlers = {};
5743 this.contentPreFilters.push(dojo.hitch(this, "_preFixUrlAttributes"));
5744 if(dojo.isMoz){
5745 this.contentPreFilters.push(this._normalizeFontStyle);
5746 this.contentPostFilters.push(this._removeMozBogus);
5747 }
5748 if(dojo.isWebKit){
5749 // Try to clean up WebKit bogus artifacts. The inserted classes
5750 // made by WebKit sometimes messes things up.
5751 this.contentPreFilters.push(this._removeWebkitBogus);
5752 this.contentPostFilters.push(this._removeWebkitBogus);
5753 }
5754 if(dojo.isIE){
5755 // IE generates <strong> and <em> but we want to normalize to <b> and <i>
5756 this.contentPostFilters.push(this._normalizeFontStyle);
5757 }
5758 //this.contentDomPostFilters.push(this._postDomFixUrlAttributes);
5759
5760 if(params && dojo.isString(params.value)){
5761 this.value = params.value;
5762 }
5763
5764 this.onLoadDeferred = new dojo.Deferred();
5765 },
5766
5767 baseClass: "dijitEditor",
5768
5769 // inheritWidth: Boolean
5770 // whether to inherit the parent's width or simply use 100%
5771 inheritWidth: false,
5772
5773 // focusOnLoad: [deprecated] Boolean
5774 // Focus into this widget when the page is loaded
5775 focusOnLoad: false,
5776
5777 // name: String?
5778 // Specifies the name of a (hidden) <textarea> node on the page that's used to save
5779 // the editor content on page leave. Used to restore editor contents after navigating
5780 // to a new page and then hitting the back button.
5781 name: "",
5782
5783 // styleSheets: [const] String
5784 // semicolon (";") separated list of css files for the editing area
5785 styleSheets: "",
5786
5787 // _content: [private] String
5788 // temporary content storage
5789 _content: "",
5790
5791 // height: String
5792 // Set height to fix the editor at a specific height, with scrolling.
5793 // By default, this is 300px. If you want to have the editor always
5794 // resizes to accommodate the content, use AlwaysShowToolbar plugin
5795 // and set height="". If this editor is used within a layout widget,
5796 // set height="100%".
5797 height: "300px",
5798
5799 // minHeight: String
5800 // The minimum height that the editor should have.
5801 minHeight: "1em",
5802
5803 // isClosed: [private] Boolean
5804 isClosed: true,
5805
5806 // isLoaded: [private] Boolean
5807 isLoaded: false,
5808
5809 // _SEPARATOR: [private] String
5810 // Used to concat contents from multiple editors into a single string,
5811 // so they can be saved into a single <textarea> node. See "name" attribute.
5812 _SEPARATOR: "@@**%%__RICHTEXTBOUNDRY__%%**@@",
5813
5814 // onLoadDeferred: [protected] dojo.Deferred
5815 // Deferred which is fired when the editor finishes loading
5816 onLoadDeferred: null,
5817
5818 // isTabIndent: Boolean
5819 // Make tab key and shift-tab indent and outdent rather than navigating.
5820 // Caution: sing this makes web pages inaccessible to users unable to use a mouse.
5821 isTabIndent: false,
5822
5823 // disableSpellCheck: [const] Boolean
5824 // When true, disables the browser's native spell checking, if supported.
5825 // Works only in Firefox.
5826 disableSpellCheck: false,
5827
5828 postCreate: function(){
5829 if("textarea" == this.domNode.tagName.toLowerCase()){
5830 console.warn("RichText should not be used with the TEXTAREA tag. See dijit._editor.RichText docs.");
5831 }
5832
5833 this.inherited(arguments);
5834
5835 dojo.publish(dijit._scopeName + "._editor.RichText::init", [this]);
5836 this.open();
5837 this.setupDefaultShortcuts();
5838 },
5839
5840 setupDefaultShortcuts: function(){
5841 // summary:
5842 // Add some default key handlers
5843 // description:
5844 // Overwrite this to setup your own handlers. The default
5845 // implementation does not use Editor commands, but directly
5846 // executes the builtin commands within the underlying browser
5847 // support.
5848 // tags:
5849 // protected
5850 var exec = dojo.hitch(this, function(cmd, arg){
5851 return function(){
5852 return !this.execCommand(cmd,arg);
5853 };
5854 });
5855
5856 var ctrlKeyHandlers = {
5857 b: exec("bold"),
5858 i: exec("italic"),
5859 u: exec("underline"),
5860 a: exec("selectall"),
5861 s: function(){ this.save(true); },
5862 m: function(){ this.isTabIndent = !this.isTabIndent; },
5863
5864 "1": exec("formatblock", "h1"),
5865 "2": exec("formatblock", "h2"),
5866 "3": exec("formatblock", "h3"),
5867 "4": exec("formatblock", "h4"),
5868
5869 "\\": exec("insertunorderedlist")
5870 };
5871
5872 if(!dojo.isIE){
5873 ctrlKeyHandlers.Z = exec("redo"); //FIXME: undo?
5874 }
5875
5876 for(var key in ctrlKeyHandlers){
5877 this.addKeyHandler(key, true, false, ctrlKeyHandlers[key]);
5878 }
5879 },
5880
5881 // events: [private] String[]
5882 // events which should be connected to the underlying editing area
5883 events: ["onKeyPress", "onKeyDown", "onKeyUp", "onClick"],
5884
5885 // captureEvents: [deprecated] String[]
5886 // Events which should be connected to the underlying editing
5887 // area, events in this array will be addListener with
5888 // capture=true.
5889 // TODO: looking at the code I don't see any distinction between events and captureEvents,
5890 // so get rid of this for 2.0 if not sooner
5891 captureEvents: [],
5892
5893 _editorCommandsLocalized: false,
5894 _localizeEditorCommands: function(){
5895 // summary:
5896 // When IE is running in a non-English locale, the API actually changes,
5897 // so that we have to say (for example) danraku instead of p (for paragraph).
5898 // Handle that here.
5899 // tags:
5900 // private
5901 if(this._editorCommandsLocalized){
5902 return;
5903 }
5904 this._editorCommandsLocalized = true;
5905
5906 //in IE, names for blockformat is locale dependent, so we cache the values here
5907
5908 //if the normal way fails, we try the hard way to get the list
5909
5910 //do not use _cacheLocalBlockFormatNames here, as it will
5911 //trigger security warning in IE7
5912
5913 //put p after div, so if IE returns Normal, we show it as paragraph
5914 //We can distinguish p and div if IE returns Normal, however, in order to detect that,
5915 //we have to call this.document.selection.createRange().parentElement() or such, which
5916 //could slow things down. Leave it as it is for now
5917 var formats = ['div', 'p', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul', 'address'];
5918 var localhtml = "", format, i=0;
5919 while((format=formats[i++])){
5920 //append a <br> after each element to separate the elements more reliably
5921 if(format.charAt(1) != 'l'){
5922 localhtml += "<"+format+"><span>content</span></"+format+"><br/>";
5923 }else{
5924 localhtml += "<"+format+"><li>content</li></"+format+"><br/>";
5925 }
5926 }
5927 //queryCommandValue returns empty if we hide editNode, so move it out of screen temporary
5928 var div = dojo.doc.createElement('div');
5929 dojo.style(div, {
5930 position: "absolute",
5931 top: "-2000px"
5932 });
5933 dojo.doc.body.appendChild(div);
5934 div.innerHTML = localhtml;
5935 var node = div.firstChild;
5936 while(node){
5937 dijit._editor.selection.selectElement(node.firstChild);
5938 dojo.withGlobal(this.window, "selectElement", dijit._editor.selection, [node.firstChild]);
5939 var nativename = node.tagName.toLowerCase();
5940 this._local2NativeFormatNames[nativename] = document.queryCommandValue("formatblock");
5941 //this.queryCommandValue("formatblock");
5942 this._native2LocalFormatNames[this._local2NativeFormatNames[nativename]] = nativename;
5943 node = node.nextSibling.nextSibling;
5944 }
5945 dojo.body().removeChild(div);
5946 },
5947
5948 open: function(/*DomNode?*/ element){
5949 // summary:
5950 // Transforms the node referenced in this.domNode into a rich text editing
5951 // node.
5952 // description:
5953 // Sets up the editing area asynchronously. This will result in
5954 // the creation and replacement with an iframe.
5955 //
5956 // A dojo.Deferred object is created at this.onLoadDeferred, and
5957 // users may attach to it to be informed when the rich-text area
5958 // initialization is finalized.
5959 // tags:
5960 // private
5961
5962 if(!this.onLoadDeferred || this.onLoadDeferred.fired >= 0){
5963 this.onLoadDeferred = new dojo.Deferred();
5964 }
5965
5966 if(!this.isClosed){ this.close(); }
5967 dojo.publish(dijit._scopeName + "._editor.RichText::open", [ this ]);
5968
5969 this._content = "";
5970 if(arguments.length == 1 && element.nodeName){ // else unchanged
5971 this.domNode = element;
5972 }
5973
5974 var dn = this.domNode;
5975
5976 // "html" will hold the innerHTML of the srcNodeRef and will be used to
5977 // initialize the editor.
5978 var html;
5979
5980 if(dojo.isString(this.value)){
5981 // Allow setting the editor content programmatically instead of
5982 // relying on the initial content being contained within the target
5983 // domNode.
5984 html = this.value;
5985 delete this.value;
5986 dn.innerHTML = "";
5987 }else if(dn.nodeName && dn.nodeName.toLowerCase() == "textarea"){
5988 // if we were created from a textarea, then we need to create a
5989 // new editing harness node.
5990 var ta = (this.textarea = dn);
5991 this.name = ta.name;
5992 html = ta.value;
5993 dn = this.domNode = dojo.doc.createElement("div");
5994 dn.setAttribute('widgetId', this.id);
5995 ta.removeAttribute('widgetId');
5996 dn.cssText = ta.cssText;
5997 dn.className += " " + ta.className;
5998 dojo.place(dn, ta, "before");
5999 var tmpFunc = dojo.hitch(this, function(){
6000 //some browsers refuse to submit display=none textarea, so
6001 //move the textarea off screen instead
6002 dojo.style(ta, {
6003 display: "block",
6004 position: "absolute",
6005 top: "-1000px"
6006 });
6007
6008 if(dojo.isIE){ //nasty IE bug: abnormal formatting if overflow is not hidden
6009 var s = ta.style;
6010 this.__overflow = s.overflow;
6011 s.overflow = "hidden";
6012 }
6013 });
6014 if(dojo.isIE){
6015 setTimeout(tmpFunc, 10);
6016 }else{
6017 tmpFunc();
6018 }
6019
6020 if(ta.form){
6021 dojo.connect(ta.form, "onsubmit", this, function(){
6022 // FIXME: should we be calling close() here instead?
6023 ta.value = this.getValue();
6024 });
6025 }
6026 }else{
6027 html = dijit._editor.getChildrenHtml(dn);
6028 dn.innerHTML = "";
6029 }
6030
6031 var content = dojo.contentBox(dn);
6032 this._oldHeight = content.h;
6033 this._oldWidth = content.w;
6034
6035 this.savedContent = html;
6036
6037 // If we're a list item we have to put in a blank line to force the
6038 // bullet to nicely align at the top of text
6039 if(dn.nodeName && dn.nodeName == "LI"){
6040 dn.innerHTML = " <br>";
6041 }
6042
6043 // Construct the editor div structure.
6044 this.header = dn.ownerDocument.createElement("div");
6045 dn.appendChild(this.header);
6046 this.editingArea = dn.ownerDocument.createElement("div");
6047 dn.appendChild(this.editingArea);
6048 this.footer = dn.ownerDocument.createElement("div");
6049 dn.appendChild(this.footer);
6050
6051 // User has pressed back/forward button so we lost the text in the editor, but it's saved
6052 // in a hidden <textarea> (which contains the data for all the editors on this page),
6053 // so get editor value from there
6054 if(this.name !== "" && (!dojo.config["useXDomain"] || dojo.config["allowXdRichTextSave"])){
6055 var saveTextarea = dojo.byId(dijit._scopeName + "._editor.RichText.savedContent");
6056 if(saveTextarea.value !== ""){
6057 var datas = saveTextarea.value.split(this._SEPARATOR), i=0, dat;
6058 while((dat=datas[i++])){
6059 var data = dat.split(":");
6060 if(data[0] == this.name){
6061 html = data[1];
6062 datas.splice(i, 1); // TODO: this has no effect
6063 break;
6064 }
6065 }
6066 }
6067
6068 // TODO: this is troublesome if this editor has been destroyed, should have global handler.
6069 // TODO: need to clear <textarea> in global handler
6070 dojo.addOnUnload(dojo.hitch(this, "_saveContent"));
6071 }
6072
6073 this.isClosed = false;
6074
6075 var ifr = (this.editorObject = this.iframe = dojo.doc.createElement('iframe'));
6076 ifr.id = this.id+"_iframe";
6077 this._iframeSrc = this._getIframeDocTxt();
6078 ifr.style.border = "none";
6079 ifr.style.width = "100%";
6080 if(this._layoutMode){
6081 // iframe should be 100% height, thus getting it's height from surrounding
6082 // <div> (which has the correct height set by Editor)
6083 ifr.style.height = "100%";
6084 }else{
6085 if(dojo.isIE >= 7){
6086 if(this.height){
6087 ifr.style.height = this.height;
6088 }
6089 if(this.minHeight){
6090 ifr.style.minHeight = this.minHeight;
6091 }
6092 }else{
6093 ifr.style.height = this.height ? this.height : this.minHeight;
6094 }
6095 }
6096 ifr.frameBorder = 0;
6097 ifr._loadFunc = dojo.hitch( this, function(win){
6098 this.window = win;
6099 this.document = this.window.document;
6100
6101 if(dojo.isIE){
6102 this._localizeEditorCommands();
6103 }
6104
6105 // Do final setup and set initial contents of editor
6106 this.onLoad(html);
6107 });
6108
6109 // Set the iframe's initial (blank) content.
6110 var s = 'javascript:parent.' + dijit._scopeName + '.byId("'+this.id+'")._iframeSrc';
6111 ifr.setAttribute('src', s);
6112 this.editingArea.appendChild(ifr);
6113
6114 // TODO: this is a guess at the default line-height, kinda works
6115 if(dn.nodeName == "LI"){
6116 dn.lastChild.style.marginTop = "-1.2em";
6117 }
6118
6119 dojo.addClass(this.domNode, this.baseClass);
6120 },
6121
6122 //static cache variables shared among all instance of this class
6123 _local2NativeFormatNames: {},
6124 _native2LocalFormatNames: {},
6125
6126 _getIframeDocTxt: function(){
6127 // summary:
6128 // Generates the boilerplate text of the document inside the iframe (ie, <html><head>...</head><body/></html>).
6129 // Editor content (if not blank) should be added afterwards.
6130 // tags:
6131 // private
6132 var _cs = dojo.getComputedStyle(this.domNode);
6133
6134 // The contents inside of <body>. The real contents are set later via a call to setValue().
6135 var html = "";
6136 var setBodyId = true;
6137 if(dojo.isIE || (!this.height && !dojo.isMoz)){
6138 // In auto-expand mode, need a wrapper div for AlwaysShowToolbar plugin to correctly
6139 // expand/contract the editor as the content changes.
6140 html = "<div id='dijitEditorBody'></div>";
6141 setBodyId = false;
6142 }else if(dojo.isMoz){
6143 // workaround bug where can't select then delete text (until user types something
6144 // into the editor)... and/or issue where typing doesn't erase selected text
6145 this._cursorToStart = true;
6146 html = "&nbsp;";
6147 }
6148
6149 var font = [ _cs.fontWeight, _cs.fontSize, _cs.fontFamily ].join(" ");
6150
6151 // line height is tricky - applying a units value will mess things up.
6152 // if we can't get a non-units value, bail out.
6153 var lineHeight = _cs.lineHeight;
6154 if(lineHeight.indexOf("px") >= 0){
6155 lineHeight = parseFloat(lineHeight)/parseFloat(_cs.fontSize);
6156 // console.debug(lineHeight);
6157 }else if(lineHeight.indexOf("em")>=0){
6158 lineHeight = parseFloat(lineHeight);
6159 }else{
6160 // If we can't get a non-units value, just default
6161 // it to the CSS spec default of 'normal'. Seems to
6162 // work better, esp on IE, than '1.0'
6163 lineHeight = "normal";
6164 }
6165 var userStyle = "";
6166 var self = this;
6167 this.style.replace(/(^|;)\s*(line-|font-?)[^;]+/ig, function(match){
6168 match = match.replace(/^;/ig,"") + ';';
6169 var s = match.split(":")[0];
6170 if(s){
6171 s = dojo.trim(s);
6172 s = s.toLowerCase();
6173 var i;
6174 var sC = "";
6175 for(i = 0; i < s.length; i++){
6176 var c = s.charAt(i);
6177 switch(c){
6178 case "-":
6179 i++;
6180 c = s.charAt(i).toUpperCase();
6181 default:
6182 sC += c;
6183 }
6184 }
6185 dojo.style(self.domNode, sC, "");
6186 }
6187 userStyle += match + ';';
6188 });
6189
6190
6191 // need to find any associated label element and update iframe document title
6192 var label=dojo.query('label[for="'+this.id+'"]');
6193
6194 return [
6195 this.isLeftToRight() ? "<html>\n<head>\n" : "<html dir='rtl'>\n<head>\n",
6196 (dojo.isMoz && label.length ? "<title>" + label[0].innerHTML + "</title>\n" : ""),
6197 "<meta http-equiv='Content-Type' content='text/html'>\n",
6198 "<style>\n",
6199 "\tbody,html {\n",
6200 "\t\tbackground:transparent;\n",
6201 "\t\tpadding: 1px 0 0 0;\n",
6202 "\t\tmargin: -1px 0 0 0;\n", // remove extraneous vertical scrollbar on safari and firefox
6203
6204 // Set the html/body sizing. Webkit always needs this, other browsers
6205 // only set it when height is defined (not auto-expanding), otherwise
6206 // scrollers do not appear.
6207 ((dojo.isWebKit)?"\t\twidth: 100%;\n":""),
6208 ((dojo.isWebKit)?"\t\theight: 100%;\n":""),
6209 "\t}\n",
6210
6211 // TODO: left positioning will cause contents to disappear out of view
6212 // if it gets too wide for the visible area
6213 "\tbody{\n",
6214 "\t\ttop:0px;\n",
6215 "\t\tleft:0px;\n",
6216 "\t\tright:0px;\n",
6217 "\t\tfont:", font, ";\n",
6218 ((this.height||dojo.isOpera) ? "" : "\t\tposition: fixed;\n"),
6219 // FIXME: IE 6 won't understand min-height?
6220 "\t\tmin-height:", this.minHeight, ";\n",
6221 "\t\tline-height:", lineHeight,";\n",
6222 "\t}\n",
6223 "\tp{ margin: 1em 0; }\n",
6224
6225 // Determine how scrollers should be applied. In autoexpand mode (height = "") no scrollers on y at all.
6226 // But in fixed height mode we want both x/y scrollers. Also, if it's using wrapping div and in auto-expand
6227 // (Mainly IE) we need to kill the y scroller on body and html.
6228 (!setBodyId && !this.height ? "\tbody,html {overflow-y: hidden;}\n" : ""),
6229 "\t#dijitEditorBody{overflow-x: auto; overflow-y:" + (this.height ? "auto;" : "hidden;") + "}\n",
6230 "\tli > ul:-moz-first-node, li > ol:-moz-first-node{ padding-top: 1.2em; }\n",
6231 "\tli{ min-height:1.2em; }\n",
6232 "</style>\n",
6233 this._applyEditingAreaStyleSheets(),"\n",
6234 "</head>\n<body ",
6235 (setBodyId?"id='dijitEditorBody' ":""),
6236 "onload='frameElement._loadFunc(window,document)' style='"+userStyle+"'>", html, "</body>\n</html>"
6237 ].join(""); // String
6238 },
6239
6240 _applyEditingAreaStyleSheets: function(){
6241 // summary:
6242 // apply the specified css files in styleSheets
6243 // tags:
6244 // private
6245 var files = [];
6246 if(this.styleSheets){
6247 files = this.styleSheets.split(';');
6248 this.styleSheets = '';
6249 }
6250
6251 //empty this.editingAreaStyleSheets here, as it will be filled in addStyleSheet
6252 files = files.concat(this.editingAreaStyleSheets);
6253 this.editingAreaStyleSheets = [];
6254
6255 var text='', i=0, url;
6256 while((url=files[i++])){
6257 var abstring = (new dojo._Url(dojo.global.location, url)).toString();
6258 this.editingAreaStyleSheets.push(abstring);
6259 text += '<link rel="stylesheet" type="text/css" href="'+abstring+'"/>';
6260 }
6261 return text;
6262 },
6263
6264 addStyleSheet: function(/*dojo._Url*/ uri){
6265 // summary:
6266 // add an external stylesheet for the editing area
6267 // uri:
6268 // A dojo.uri.Uri pointing to the url of the external css file
6269 var url=uri.toString();
6270
6271 //if uri is relative, then convert it to absolute so that it can be resolved correctly in iframe
6272 if(url.charAt(0) == '.' || (url.charAt(0) != '/' && !uri.host)){
6273 url = (new dojo._Url(dojo.global.location, url)).toString();
6274 }
6275
6276 if(dojo.indexOf(this.editingAreaStyleSheets, url) > -1){
6277// console.debug("dijit._editor.RichText.addStyleSheet: Style sheet "+url+" is already applied");
6278 return;
6279 }
6280
6281 this.editingAreaStyleSheets.push(url);
6282 this.onLoadDeferred.addCallback(dojo.hitch(function(){
6283 if(this.document.createStyleSheet){ //IE
6284 this.document.createStyleSheet(url);
6285 }else{ //other browser
6286 var head = this.document.getElementsByTagName("head")[0];
6287 var stylesheet = this.document.createElement("link");
6288 stylesheet.rel="stylesheet";
6289 stylesheet.type="text/css";
6290 stylesheet.href=url;
6291 head.appendChild(stylesheet);
6292 }
6293 }));
6294 },
6295
6296 removeStyleSheet: function(/*dojo._Url*/ uri){
6297 // summary:
6298 // remove an external stylesheet for the editing area
6299 var url=uri.toString();
6300 //if uri is relative, then convert it to absolute so that it can be resolved correctly in iframe
6301 if(url.charAt(0) == '.' || (url.charAt(0) != '/' && !uri.host)){
6302 url = (new dojo._Url(dojo.global.location, url)).toString();
6303 }
6304 var index = dojo.indexOf(this.editingAreaStyleSheets, url);
6305 if(index == -1){
6306// console.debug("dijit._editor.RichText.removeStyleSheet: Style sheet "+url+" has not been applied");
6307 return;
6308 }
6309 delete this.editingAreaStyleSheets[index];
6310 dojo.withGlobal(this.window,'query', dojo, ['link:[href="'+url+'"]']).orphan();
6311 },
6312
6313 // disabled: Boolean
6314 // The editor is disabled; the text cannot be changed.
6315 disabled: false,
6316
6317 _mozSettingProps: {'styleWithCSS':false},
6318 _setDisabledAttr: function(/*Boolean*/ value){
6319 this.disabled = value;
6320 if(!this.isLoaded){ return; } // this method requires init to be complete
6321 value = !!value;
6322 if(dojo.isIE || dojo.isWebKit || dojo.isOpera){
6323 var preventIEfocus = dojo.isIE && (this.isLoaded || !this.focusOnLoad);
6324 if(preventIEfocus){ this.editNode.unselectable = "on"; }
6325 this.editNode.contentEditable = !value;
6326 if(preventIEfocus){
6327 var _this = this;
6328 setTimeout(function(){ _this.editNode.unselectable = "off"; }, 0);
6329 }
6330 }else{ //moz
6331 try{
6332 this.document.designMode=(value?'off':'on');
6333 }catch(e){ return; } // ! _disabledOK
6334 if(!value && this._mozSettingProps){
6335 var ps = this._mozSettingProps;
6336 for(var n in ps){
6337 if(ps.hasOwnProperty(n)){
6338 try{
6339 this.document.execCommand(n,false,ps[n]);
6340 }catch(e2){}
6341 }
6342 }
6343 }
6344// this.document.execCommand('contentReadOnly', false, value);
6345// if(value){
6346// this.blur(); //to remove the blinking caret
6347// }
6348 }
6349 this._disabledOK = true;
6350 },
6351
6352/* Event handlers
6353 *****************/
6354
6355 onLoad: function(/*String*/ html){
6356 // summary:
6357 // Handler after the iframe finishes loading.
6358 // html: String
6359 // Editor contents should be set to this value
6360 // tags:
6361 // protected
6362
6363 // TODO: rename this to _onLoad, make empty public onLoad() method, deprecate/make protected onLoadDeferred handler?
6364
6365 if(!this.window.__registeredWindow){
6366 this.window.__registeredWindow = true;
6367 this._iframeRegHandle = dijit.registerIframe(this.iframe);
6368 }
6369 if(!dojo.isIE && (this.height || dojo.isMoz)){
6370 this.editNode=this.document.body;
6371 }else{
6372 // there's a wrapper div around the content, see _getIframeDocTxt().
6373 this.editNode=this.document.body.firstChild;
6374 var _this = this;
6375 if(dojo.isIE){ // #4996 IE wants to focus the BODY tag
6376 var tabStop = (this.tabStop = dojo.doc.createElement('<div tabIndex=-1>'));
6377 this.editingArea.appendChild(tabStop);
6378 this.iframe.onfocus = function(){ _this.editNode.setActive(); };
6379 }
6380 }
6381 this.focusNode = this.editNode; // for InlineEditBox
6382
6383
6384 var events = this.events.concat(this.captureEvents);
6385 var ap = this.iframe ? this.document : this.editNode;
6386 dojo.forEach(events, function(item){
6387 this.connect(ap, item.toLowerCase(), item);
6388 }, this);
6389
6390 if(dojo.isIE){ // IE contentEditable
6391 this.connect(this.document, "onmousedown", "_onIEMouseDown"); // #4996 fix focus
6392
6393 // give the node Layout on IE
6394 // TODO: this may no longer be needed, since we've reverted IE to using an iframe,
6395 // not contentEditable. Removing it would also probably remove the need for creating
6396 // the extra <div> in _getIframeDocTxt()
6397 this.editNode.style.zoom = 1.0;
6398 }else{
6399 this.connect(this.document, "onmousedown", function(){
6400 // Clear the moveToStart focus, as mouse
6401 // down will set cursor point. Required to properly
6402 // work with selection/position driven plugins and clicks in
6403 // the window. refs: #10678
6404 delete this._cursorToStart;
6405 });
6406 }
6407
6408 if(dojo.isWebKit){
6409 //WebKit sometimes doesn't fire right on selections, so the toolbar
6410 //doesn't update right. Therefore, help it out a bit with an additional
6411 //listener. A mouse up will typically indicate a display change, so fire this
6412 //and get the toolbar to adapt. Reference: #9532
6413 this._webkitListener = this.connect(this.document, "onmouseup", "onDisplayChanged");
6414 }
6415
6416 if(dojo.isIE){
6417 // Try to make sure 'hidden' elements aren't visible in edit mode (like browsers other than IE
6418 // do). See #9103
6419 try{
6420 this.document.execCommand('RespectVisibilityInDesign', true, null);
6421 }catch(e){/* squelch */}
6422 }
6423
6424 this.isLoaded = true;
6425
6426 this.set('disabled', this.disabled); // initialize content to editable (or not)
6427
6428 // Note that setValue() call will only work after isLoaded is set to true (above)
6429
6430 // Set up a function to allow delaying the setValue until a callback is fired
6431 // This ensures extensions like dijit.Editor have a way to hold the value set
6432 // until plugins load (and do things like register filters.
6433 var setContent = dojo.hitch(this, function(){
6434 this.setValue(html);
6435 if(this.onLoadDeferred){
6436 this.onLoadDeferred.callback(true);
6437 }
6438 this.onDisplayChanged();
6439 if(this.focusOnLoad){
6440 // after the document loads, then set focus after updateInterval expires so that
6441 // onNormalizedDisplayChanged has run to avoid input caret issues
6442 dojo.addOnLoad(dojo.hitch(this, function(){ setTimeout(dojo.hitch(this, "focus"), this.updateInterval); }));
6443 }
6444 // Save off the initial content now
6445 this.savedContent = this.getValue(true);
6446 });
6447 if(this.setValueDeferred){
6448 this.setValueDeferred.addCallback(setContent);
6449 }else{
6450 setContent();
6451 }
6452
6453 },
6454
6455 onKeyDown: function(/* Event */ e){
6456 // summary:
6457 // Handler for onkeydown event
6458 // tags:
6459 // protected
6460
6461 // we need this event at the moment to get the events from control keys
6462 // such as the backspace. It might be possible to add this to Dojo, so that
6463 // keyPress events can be emulated by the keyDown and keyUp detection.
6464
6465 if(e.keyCode === dojo.keys.TAB && this.isTabIndent ){
6466 dojo.stopEvent(e); //prevent tab from moving focus out of editor
6467
6468 // FIXME: this is a poor-man's indent/outdent. It would be
6469 // better if it added 4 "&nbsp;" chars in an undoable way.
6470 // Unfortunately pasteHTML does not prove to be undoable
6471 if(this.queryCommandEnabled((e.shiftKey ? "outdent" : "indent"))){
6472 this.execCommand((e.shiftKey ? "outdent" : "indent"));
6473 }
6474 }
6475 if(dojo.isIE){
6476 if(e.keyCode == dojo.keys.TAB && !this.isTabIndent){
6477 if(e.shiftKey && !e.ctrlKey && !e.altKey){
6478 // focus the BODY so the browser will tab away from it instead
6479 this.iframe.focus();
6480 }else if(!e.shiftKey && !e.ctrlKey && !e.altKey){
6481 // focus the BODY so the browser will tab away from it instead
6482 this.tabStop.focus();
6483 }
6484 }else if(e.keyCode === dojo.keys.BACKSPACE && this.document.selection.type === "Control"){
6485 // IE has a bug where if a non-text object is selected in the editor,
6486 // hitting backspace would act as if the browser's back button was
6487 // clicked instead of deleting the object. see #1069
6488 dojo.stopEvent(e);
6489 this.execCommand("delete");
6490 }else if((65 <= e.keyCode && e.keyCode <= 90) ||
6491 (e.keyCode>=37 && e.keyCode<=40) // FIXME: get this from connect() instead!
6492 ){ //arrow keys
6493 e.charCode = e.keyCode;
6494 this.onKeyPress(e);
6495 }
6496 }
6497 return true;
6498 },
6499
6500 onKeyUp: function(e){
6501 // summary:
6502 // Handler for onkeyup event
6503 // tags:
6504 // callback
6505 return;
6506 },
6507
6508 setDisabled: function(/*Boolean*/ disabled){
6509 // summary:
6510 // Deprecated, use set('disabled', ...) instead.
6511 // tags:
6512 // deprecated
6513 dojo.deprecated('dijit.Editor::setDisabled is deprecated','use dijit.Editor::attr("disabled",boolean) instead', 2.0);
6514 this.set('disabled',disabled);
6515 },
6516 _setValueAttr: function(/*String*/ value){
6517 // summary:
6518 // Registers that attr("value", foo) should call setValue(foo)
6519 this.setValue(value);
6520 },
6521 _setDisableSpellCheckAttr: function(/*Boolean*/ disabled){
6522 if(this.document){
6523 dojo.attr(this.document.body, "spellcheck", !disabled);
6524 }else{
6525 // try again after the editor is finished loading
6526 this.onLoadDeferred.addCallback(dojo.hitch(this, function(){
6527 dojo.attr(this.document.body, "spellcheck", !disabled);
6528 }));
6529 }
6530 this.disableSpellCheck = disabled;
6531 },
6532
6533 onKeyPress: function(e){
6534 // summary:
6535 // Handle the various key events
6536 // tags:
6537 // protected
6538
6539 var c = (e.keyChar && e.keyChar.toLowerCase()) || e.keyCode,
6540 handlers = this._keyHandlers[c],
6541 args = arguments;
6542
6543 if(handlers && !e.altKey){
6544 dojo.some(handlers, function(h){
6545 // treat meta- same as ctrl-, for benefit of mac users
6546 if(!(h.shift ^ e.shiftKey) && !(h.ctrl ^ (e.ctrlKey||e.metaKey))){
6547 if(!h.handler.apply(this, args)){
6548 e.preventDefault();
6549 }
6550 return true;
6551 }
6552 }, this);
6553 }
6554
6555 // function call after the character has been inserted
6556 if(!this._onKeyHitch){
6557 this._onKeyHitch = dojo.hitch(this, "onKeyPressed");
6558 }
6559 setTimeout(this._onKeyHitch, 1);
6560 return true;
6561 },
6562
6563 addKeyHandler: function(/*String*/ key, /*Boolean*/ ctrl, /*Boolean*/ shift, /*Function*/ handler){
6564 // summary:
6565 // Add a handler for a keyboard shortcut
6566 // description:
6567 // The key argument should be in lowercase if it is a letter character
6568 // tags:
6569 // protected
6570 if(!dojo.isArray(this._keyHandlers[key])){
6571 this._keyHandlers[key] = [];
6572 }
6573 //TODO: would be nice to make this a hash instead of an array for quick lookups
6574 this._keyHandlers[key].push({
6575 shift: shift || false,
6576 ctrl: ctrl || false,
6577 handler: handler
6578 });
6579 },
6580
6581 onKeyPressed: function(){
6582 // summary:
6583 // Handler for after the user has pressed a key, and the display has been updated.
6584 // (Runs on a timer so that it runs after the display is updated)
6585 // tags:
6586 // private
6587 this.onDisplayChanged(/*e*/); // can't pass in e
6588 },
6589
6590 onClick: function(/*Event*/ e){
6591 // summary:
6592 // Handler for when the user clicks.
6593 // tags:
6594 // private
6595
6596 // console.info('onClick',this._tryDesignModeOn);
6597 this.onDisplayChanged(e);
6598 },
6599
6600 _onIEMouseDown: function(/*Event*/ e){
6601 // summary:
6602 // IE only to prevent 2 clicks to focus
6603 // tags:
6604 // protected
6605
6606 if(!this._focused && !this.disabled){
6607 this.focus();
6608 }
6609 },
6610
6611 _onBlur: function(e){
6612 // summary:
6613 // Called from focus manager when focus has moved away from this editor
6614 // tags:
6615 // protected
6616
6617 // console.info('_onBlur')
6618
6619 this.inherited(arguments);
6620 var _c=this.getValue(true);
6621
6622 if(_c!=this.savedContent){
6623 this.onChange(_c);
6624 this.savedContent=_c;
6625 }
6626 },
6627 _onFocus: function(/*Event*/ e){
6628 // summary:
6629 // Called from focus manager when focus has moved into this editor
6630 // tags:
6631 // protected
6632
6633 // console.info('_onFocus')
6634 if(!this.disabled){
6635 if(!this._disabledOK){
6636 this.set('disabled', false);
6637 }
6638 this.inherited(arguments);
6639 }
6640 },
6641
6642 // TODO: why is this needed - should we deprecate this ?
6643 blur: function(){
6644 // summary:
6645 // Remove focus from this instance.
6646 // tags:
6647 // deprecated
6648 if(!dojo.isIE && this.window.document.documentElement && this.window.document.documentElement.focus){
6649 this.window.document.documentElement.focus();
6650 }else if(dojo.doc.body.focus){
6651 dojo.doc.body.focus();
6652 }
6653 },
6654
6655 focus: function(){
6656 // summary:
6657 // Move focus to this editor
6658 if(!this.isLoaded){
6659 this.focusOnLoad = true;
6660 return;
6661 }
6662 if(this._cursorToStart){
6663 delete this._cursorToStart;
6664 if(this.editNode.childNodes){
6665 this.placeCursorAtStart(); // this calls focus() so return
6666 return;
6667 }
6668 }
6669 if(!dojo.isIE){
6670 dijit.focus(this.iframe);
6671 }else if(this.editNode && this.editNode.focus){
6672 // editNode may be hidden in display:none div, lets just punt in this case
6673 //this.editNode.focus(); -> causes IE to scroll always (strict and quirks mode) to the top the Iframe
6674 // if we fire the event manually and let the browser handle the focusing, the latest
6675 // cursor position is focused like in FF
6676 this.iframe.fireEvent('onfocus', document.createEventObject()); // createEventObject only in IE
6677 // }else{
6678 // TODO: should we throw here?
6679 // console.debug("Have no idea how to focus into the editor!");
6680 }
6681 },
6682
6683 // _lastUpdate: 0,
6684 updateInterval: 200,
6685 _updateTimer: null,
6686 onDisplayChanged: function(/*Event*/ e){
6687 // summary:
6688 // This event will be fired everytime the display context
6689 // changes and the result needs to be reflected in the UI.
6690 // description:
6691 // If you don't want to have update too often,
6692 // onNormalizedDisplayChanged should be used instead
6693 // tags:
6694 // private
6695
6696 // var _t=new Date();
6697 if(this._updateTimer){
6698 clearTimeout(this._updateTimer);
6699 }
6700 if(!this._updateHandler){
6701 this._updateHandler = dojo.hitch(this,"onNormalizedDisplayChanged");
6702 }
6703 this._updateTimer = setTimeout(this._updateHandler, this.updateInterval);
6704 },
6705 onNormalizedDisplayChanged: function(){
6706 // summary:
6707 // This event is fired every updateInterval ms or more
6708 // description:
6709 // If something needs to happen immediately after a
6710 // user change, please use onDisplayChanged instead.
6711 // tags:
6712 // private
6713 delete this._updateTimer;
6714 },
6715 onChange: function(newContent){
6716 // summary:
6717 // This is fired if and only if the editor loses focus and
6718 // the content is changed.
6719 },
6720 _normalizeCommand: function(/*String*/ cmd, /*Anything?*/argument){
6721 // summary:
6722 // Used as the advice function by dojo.connect to map our
6723 // normalized set of commands to those supported by the target
6724 // browser.
6725 // tags:
6726 // private
6727
6728 var command = cmd.toLowerCase();
6729 if(command == "formatblock"){
6730 if(dojo.isSafari && argument === undefined){ command = "heading"; }
6731 }else if(command == "hilitecolor" && !dojo.isMoz){
6732 command = "backcolor";
6733 }
6734
6735 return command;
6736 },
6737
6738 _qcaCache: {},
6739 queryCommandAvailable: function(/*String*/ command){
6740 // summary:
6741 // Tests whether a command is supported by the host. Clients
6742 // SHOULD check whether a command is supported before attempting
6743 // to use it, behaviour for unsupported commands is undefined.
6744 // command:
6745 // The command to test for
6746 // tags:
6747 // private
6748
6749 // memoizing version. See _queryCommandAvailable for computing version
6750 var ca = this._qcaCache[command];
6751 if(ca !== undefined){ return ca; }
6752 return (this._qcaCache[command] = this._queryCommandAvailable(command));
6753 },
6754
6755 _queryCommandAvailable: function(/*String*/ command){
6756 // summary:
6757 // See queryCommandAvailable().
6758 // tags:
6759 // private
6760
6761 var ie = 1;
6762 var mozilla = 1 << 1;
6763 var webkit = 1 << 2;
6764 var opera = 1 << 3;
6765 var webkit420 = 1 << 4;
6766
6767 function isSupportedBy(browsers){
6768 return {
6769 ie: Boolean(browsers & ie),
6770 mozilla: Boolean(browsers & mozilla),
6771 webkit: Boolean(browsers & webkit),
6772 webkit420: Boolean(browsers & webkit420),
6773 opera: Boolean(browsers & opera)
6774 };
6775 }
6776
6777 var supportedBy = null;
6778
6779 switch(command.toLowerCase()){
6780 case "bold": case "italic": case "underline":
6781 case "subscript": case "superscript":
6782 case "fontname": case "fontsize":
6783 case "forecolor": case "hilitecolor":
6784 case "justifycenter": case "justifyfull": case "justifyleft":
6785 case "justifyright": case "delete": case "selectall": case "toggledir":
6786 supportedBy = isSupportedBy(mozilla | ie | webkit | opera);
6787 break;
6788
6789 case "createlink": case "unlink": case "removeformat":
6790 case "inserthorizontalrule": case "insertimage":
6791 case "insertorderedlist": case "insertunorderedlist":
6792 case "indent": case "outdent": case "formatblock":
6793 case "inserthtml": case "undo": case "redo": case "strikethrough": case "tabindent":
6794 supportedBy = isSupportedBy(mozilla | ie | opera | webkit420);
6795 break;
6796
6797 case "blockdirltr": case "blockdirrtl":
6798 case "dirltr": case "dirrtl":
6799 case "inlinedirltr": case "inlinedirrtl":
6800 supportedBy = isSupportedBy(ie);
6801 break;
6802 case "cut": case "copy": case "paste":
6803 supportedBy = isSupportedBy( ie | mozilla | webkit420);
6804 break;
6805
6806 case "inserttable":
6807 supportedBy = isSupportedBy(mozilla | ie);
6808 break;
6809
6810 case "insertcell": case "insertcol": case "insertrow":
6811 case "deletecells": case "deletecols": case "deleterows":
6812 case "mergecells": case "splitcell":
6813 supportedBy = isSupportedBy(ie | mozilla);
6814 break;
6815
6816 default: return false;
6817 }
6818
6819 return (dojo.isIE && supportedBy.ie) ||
6820 (dojo.isMoz && supportedBy.mozilla) ||
6821 (dojo.isWebKit && supportedBy.webkit) ||
6822 (dojo.isWebKit > 420 && supportedBy.webkit420) ||
6823 (dojo.isOpera && supportedBy.opera); // Boolean return true if the command is supported, false otherwise
6824 },
6825
6826 execCommand: function(/*String*/ command, argument){
6827 // summary:
6828 // Executes a command in the Rich Text area
6829 // command:
6830 // The command to execute
6831 // argument:
6832 // An optional argument to the command
6833 // tags:
6834 // protected
6835 var returnValue;
6836
6837 //focus() is required for IE to work
6838 //In addition, focus() makes sure after the execution of
6839 //the command, the editor receives the focus as expected
6840 this.focus();
6841
6842 command = this._normalizeCommand(command, argument);
6843
6844
6845 if(argument !== undefined){
6846 if(command == "heading"){
6847 throw new Error("unimplemented");
6848 }else if((command == "formatblock") && dojo.isIE){
6849 argument = '<'+argument+'>';
6850 }
6851 }
6852
6853 //Check to see if we have any over-rides for commands, they will be functions on this
6854 //widget of the form _commandImpl. If we don't, fall through to the basic native
6855 //exec command of the browser.
6856 var implFunc = "_" + command + "Impl";
6857 if(this[implFunc]){
6858 returnValue = this[implFunc](argument);
6859 }else{
6860 argument = arguments.length > 1 ? argument : null;
6861 if(argument || command!="createlink"){
6862 returnValue = this.document.execCommand(command, false, argument);
6863 }
6864 }
6865
6866 this.onDisplayChanged();
6867 return returnValue;
6868 },
6869
6870 queryCommandEnabled: function(/*String*/ command){
6871 // summary:
6872 // Check whether a command is enabled or not.
6873 // tags:
6874 // protected
6875 if(this.disabled || !this._disabledOK){ return false; }
6876 command = this._normalizeCommand(command);
6877 if(dojo.isMoz || dojo.isWebKit){
6878 if(command == "unlink"){ // mozilla returns true always
6879 // console.debug(this._sCall("hasAncestorElement", ['a']));
6880 return this._sCall("hasAncestorElement", ["a"]);
6881 }else if(command == "inserttable"){
6882 return true;
6883 }
6884 }
6885 //see #4109
6886 if(dojo.isWebKit){
6887 if(command == "copy"){
6888 command = "cut";
6889 }else if(command == "paste"){
6890 return true;
6891 }
6892 }
6893
6894 var elem = dojo.isIE ? this.document.selection.createRange() : this.document;
6895 try{
6896 return elem.queryCommandEnabled(command);
6897 }catch(e){
6898 //Squelch, occurs if editor is hidden on FF 3 (and maybe others.)
6899 return false;
6900 }
6901
6902 },
6903
6904 queryCommandState: function(command){
6905 // summary:
6906 // Check the state of a given command and returns true or false.
6907 // tags:
6908 // protected
6909
6910 if(this.disabled || !this._disabledOK){ return false; }
6911 command = this._normalizeCommand(command);
6912 try{
6913 return this.document.queryCommandState(command);
6914 }catch(e){
6915 //Squelch, occurs if editor is hidden on FF 3 (and maybe others.)
6916 return false;
6917 }
6918 },
6919
6920 queryCommandValue: function(command){
6921 // summary:
6922 // Check the value of a given command. This matters most for
6923 // custom selections and complex values like font value setting.
6924 // tags:
6925 // protected
6926
6927 if(this.disabled || !this._disabledOK){ return false; }
6928 var r;
6929 command = this._normalizeCommand(command);
6930 if(dojo.isIE && command == "formatblock"){
6931 r = this._native2LocalFormatNames[this.document.queryCommandValue(command)];
6932 }else if(dojo.isMoz && command === "hilitecolor"){
6933 var oldValue;
6934 try{
6935 oldValue = this.document.queryCommandValue("styleWithCSS");
6936 }catch(e){
6937 oldValue = false;
6938 }
6939 this.document.execCommand("styleWithCSS", false, true);
6940 r = this.document.queryCommandValue(command);
6941 this.document.execCommand("styleWithCSS", false, oldValue);
6942 }else{
6943 r = this.document.queryCommandValue(command);
6944 }
6945 return r;
6946 },
6947
6948 // Misc.
6949
6950 _sCall: function(name, args){
6951 // summary:
6952 // Run the named method of dijit._editor.selection over the
6953 // current editor instance's window, with the passed args.
6954 // tags:
6955 // private
6956 return dojo.withGlobal(this.window, name, dijit._editor.selection, args);
6957 },
6958
6959 // FIXME: this is a TON of code duplication. Why?
6960
6961 placeCursorAtStart: function(){
6962 // summary:
6963 // Place the cursor at the start of the editing area.
6964 // tags:
6965 // private
6966
6967 this.focus();
6968
6969 //see comments in placeCursorAtEnd
6970 var isvalid=false;
6971 if(dojo.isMoz){
6972 // TODO: Is this branch even necessary?
6973 var first=this.editNode.firstChild;
6974 while(first){
6975 if(first.nodeType == 3){
6976 if(first.nodeValue.replace(/^\s+|\s+$/g, "").length>0){
6977 isvalid=true;
6978 this._sCall("selectElement", [ first ]);
6979 break;
6980 }
6981 }else if(first.nodeType == 1){
6982 isvalid=true;
6983 var tg = first.tagName ? first.tagName.toLowerCase() : "";
6984 // Collapse before childless tags.
6985 if(/br|input|img|base|meta|area|basefont|hr|link/.test(tg)){
6986 this._sCall("selectElement", [ first ]);
6987 }else{
6988 // Collapse inside tags with children.
6989 this._sCall("selectElementChildren", [ first ]);
6990 }
6991 break;
6992 }
6993 first = first.nextSibling;
6994 }
6995 }else{
6996 isvalid=true;
6997 this._sCall("selectElementChildren", [ this.editNode ]);
6998 }
6999 if(isvalid){
7000 this._sCall("collapse", [ true ]);
7001 }
7002 },
7003
7004 placeCursorAtEnd: function(){
7005 // summary:
7006 // Place the cursor at the end of the editing area.
7007 // tags:
7008 // private
7009
7010 this.focus();
7011
7012 //In mozilla, if last child is not a text node, we have to use
7013 // selectElementChildren on this.editNode.lastChild otherwise the
7014 // cursor would be placed at the end of the closing tag of
7015 //this.editNode.lastChild
7016 var isvalid=false;
7017 if(dojo.isMoz){
7018 var last=this.editNode.lastChild;
7019 while(last){
7020 if(last.nodeType == 3){
7021 if(last.nodeValue.replace(/^\s+|\s+$/g, "").length>0){
7022 isvalid=true;
7023 this._sCall("selectElement", [ last ]);
7024 break;
7025 }
7026 }else if(last.nodeType == 1){
7027 isvalid=true;
7028 if(last.lastChild){
7029 this._sCall("selectElement", [ last.lastChild ]);
7030 }else{
7031 this._sCall("selectElement", [ last ]);
7032 }
7033 break;
7034 }
7035 last = last.previousSibling;
7036 }
7037 }else{
7038 isvalid=true;
7039 this._sCall("selectElementChildren", [ this.editNode ]);
7040 }
7041 if(isvalid){
7042 this._sCall("collapse", [ false ]);
7043 }
7044 },
7045
7046 getValue: function(/*Boolean?*/ nonDestructive){
7047 // summary:
7048 // Return the current content of the editing area (post filters
7049 // are applied). Users should call attr('value') instead.
7050 // nonDestructive:
7051 // defaults to false. Should the post-filtering be run over a copy
7052 // of the live DOM? Most users should pass "true" here unless they
7053 // *really* know that none of the installed filters are going to
7054 // mess up the editing session.
7055 // tags:
7056 // private
7057 if(this.textarea){
7058 if(this.isClosed || !this.isLoaded){
7059 return this.textarea.value;
7060 }
7061 }
7062
7063 return this._postFilterContent(null, nonDestructive);
7064 },
7065 _getValueAttr: function(){
7066 // summary:
7067 // Hook to make attr("value") work
7068 return this.getValue(true);
7069 },
7070
7071 setValue: function(/*String*/ html){
7072 // summary:
7073 // This function sets the content. No undo history is preserved.
7074 // Users should use set('value', ...) instead.
7075 // tags:
7076 // deprecated
7077
7078 // TODO: remove this and getValue() for 2.0, and move code to _setValueAttr()
7079
7080 if(!this.isLoaded){
7081 // try again after the editor is finished loading
7082 this.onLoadDeferred.addCallback(dojo.hitch(this, function(){
7083 this.setValue(html);
7084 }));
7085 return;
7086 }
7087 this._cursorToStart = true;
7088 if(this.textarea && (this.isClosed || !this.isLoaded)){
7089 this.textarea.value=html;
7090 }else{
7091 html = this._preFilterContent(html);
7092 var node = this.isClosed ? this.domNode : this.editNode;
7093
7094 // Use &nbsp; to avoid webkit problems where editor is disabled until the user clicks it
7095 if(!html && dojo.isWebKit){
7096 html = "&nbsp;";
7097 }
7098 node.innerHTML = html;
7099 this._preDomFilterContent(node);
7100 }
7101 this.onDisplayChanged();
7102 },
7103
7104 replaceValue: function(/*String*/ html){
7105 // summary:
7106 // This function set the content while trying to maintain the undo stack
7107 // (now only works fine with Moz, this is identical to setValue in all
7108 // other browsers)
7109 // tags:
7110 // protected
7111
7112 if(this.isClosed){
7113 this.setValue(html);
7114 }else if(this.window && this.window.getSelection && !dojo.isMoz){ // Safari
7115 // look ma! it's a totally f'd browser!
7116 this.setValue(html);
7117 }else if(this.window && this.window.getSelection){ // Moz
7118 html = this._preFilterContent(html);
7119 this.execCommand("selectall");
7120 if(!html){
7121 this._cursorToStart = true;
7122 html = "&nbsp;";
7123 }
7124 this.execCommand("inserthtml", html);
7125 this._preDomFilterContent(this.editNode);
7126 }else if(this.document && this.document.selection){//IE
7127 //In IE, when the first element is not a text node, say
7128 //an <a> tag, when replacing the content of the editing
7129 //area, the <a> tag will be around all the content
7130 //so for now, use setValue for IE too
7131 this.setValue(html);
7132 }
7133 },
7134
7135 _preFilterContent: function(/*String*/ html){
7136 // summary:
7137 // Filter the input before setting the content of the editing
7138 // area. DOM pre-filtering may happen after this
7139 // string-based filtering takes place but as of 1.2, this is not
7140 // guaranteed for operations such as the inserthtml command.
7141 // tags:
7142 // private
7143
7144 var ec = html;
7145 dojo.forEach(this.contentPreFilters, function(ef){ if(ef){ ec = ef(ec); } });
7146 return ec;
7147 },
7148 _preDomFilterContent: function(/*DomNode*/ dom){
7149 // summary:
7150 // filter the input's live DOM. All filter operations should be
7151 // considered to be "live" and operating on the DOM that the user
7152 // will be interacting with in their editing session.
7153 // tags:
7154 // private
7155 dom = dom || this.editNode;
7156 dojo.forEach(this.contentDomPreFilters, function(ef){
7157 if(ef && dojo.isFunction(ef)){
7158 ef(dom);
7159 }
7160 }, this);
7161 },
7162
7163 _postFilterContent: function(
7164 /*DomNode|DomNode[]|String?*/ dom,
7165 /*Boolean?*/ nonDestructive){
7166 // summary:
7167 // filter the output after getting the content of the editing area
7168 //
7169 // description:
7170 // post-filtering allows plug-ins and users to specify any number
7171 // of transforms over the editor's content, enabling many common
7172 // use-cases such as transforming absolute to relative URLs (and
7173 // vice-versa), ensuring conformance with a particular DTD, etc.
7174 // The filters are registered in the contentDomPostFilters and
7175 // contentPostFilters arrays. Each item in the
7176 // contentDomPostFilters array is a function which takes a DOM
7177 // Node or array of nodes as its only argument and returns the
7178 // same. It is then passed down the chain for further filtering.
7179 // The contentPostFilters array behaves the same way, except each
7180 // member operates on strings. Together, the DOM and string-based
7181 // filtering allow the full range of post-processing that should
7182 // be necessaray to enable even the most agressive of post-editing
7183 // conversions to take place.
7184 //
7185 // If nonDestructive is set to "true", the nodes are cloned before
7186 // filtering proceeds to avoid potentially destructive transforms
7187 // to the content which may still needed to be edited further.
7188 // Once DOM filtering has taken place, the serialized version of
7189 // the DOM which is passed is run through each of the
7190 // contentPostFilters functions.
7191 //
7192 // dom:
7193 // a node, set of nodes, which to filter using each of the current
7194 // members of the contentDomPostFilters and contentPostFilters arrays.
7195 //
7196 // nonDestructive:
7197 // defaults to "false". If true, ensures that filtering happens on
7198 // a clone of the passed-in content and not the actual node
7199 // itself.
7200 //
7201 // tags:
7202 // private
7203
7204 var ec;
7205 if(!dojo.isString(dom)){
7206 dom = dom || this.editNode;
7207 if(this.contentDomPostFilters.length){
7208 if(nonDestructive){
7209 dom = dojo.clone(dom);
7210 }
7211 dojo.forEach(this.contentDomPostFilters, function(ef){
7212 dom = ef(dom);
7213 });
7214 }
7215 ec = dijit._editor.getChildrenHtml(dom);
7216 }else{
7217 ec = dom;
7218 }
7219
7220 if(!dojo.trim(ec.replace(/^\xA0\xA0*/, '').replace(/\xA0\xA0*$/, '')).length){
7221 ec = "";
7222 }
7223
7224 // if(dojo.isIE){
7225 // //removing appended <P>&nbsp;</P> for IE
7226 // ec = ec.replace(/(?:<p>&nbsp;</p>[\n\r]*)+$/i,"");
7227 // }
7228 dojo.forEach(this.contentPostFilters, function(ef){
7229 ec = ef(ec);
7230 });
7231
7232 return ec;
7233 },
7234
7235 _saveContent: function(/*Event*/ e){
7236 // summary:
7237 // Saves the content in an onunload event if the editor has not been closed
7238 // tags:
7239 // private
7240
7241 var saveTextarea = dojo.byId(dijit._scopeName + "._editor.RichText.savedContent");
7242 if(saveTextarea.value){
7243 saveTextarea.value += this._SEPARATOR;
7244 }
7245 saveTextarea.value += this.name + ":" + this.getValue(true);
7246 },
7247
7248
7249 escapeXml: function(/*String*/ str, /*Boolean*/ noSingleQuotes){
7250 // summary:
7251 // Adds escape sequences for special characters in XML.
7252 // Optionally skips escapes for single quotes
7253 // tags:
7254 // private
7255
7256 str = str.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
7257 if(!noSingleQuotes){
7258 str = str.replace(/'/gm, "&#39;");
7259 }
7260 return str; // string
7261 },
7262
7263 getNodeHtml: function(/* DomNode */ node){
7264 // summary:
7265 // Deprecated. Use dijit._editor._getNodeHtml() instead.
7266 // tags:
7267 // deprecated
7268 dojo.deprecated('dijit.Editor::getNodeHtml is deprecated','use dijit._editor.getNodeHtml instead', 2);
7269 return dijit._editor.getNodeHtml(node); // String
7270 },
7271
7272 getNodeChildrenHtml: function(/* DomNode */ dom){
7273 // summary:
7274 // Deprecated. Use dijit._editor.getChildrenHtml() instead.
7275 // tags:
7276 // deprecated
7277 dojo.deprecated('dijit.Editor::getNodeChildrenHtml is deprecated','use dijit._editor.getChildrenHtml instead', 2);
7278 return dijit._editor.getChildrenHtml(dom);
7279 },
7280
7281 close: function(/*Boolean*/ save){
7282 // summary:
7283 // Kills the editor and optionally writes back the modified contents to the
7284 // element from which it originated.
7285 // save:
7286 // Whether or not to save the changes. If false, the changes are discarded.
7287 // tags:
7288 // private
7289
7290 if(this.isClosed){return false; }
7291
7292 if(!arguments.length){ save = true; }
7293 this._content = this.getValue();
7294 var changed = (this.savedContent != this._content);
7295
7296 // line height is squashed for iframes
7297 // FIXME: why was this here? if (this.iframe){ this.domNode.style.lineHeight = null; }
7298
7299 if(this.interval){ clearInterval(this.interval); }
7300
7301 if(this._webkitListener){
7302 //Cleaup of WebKit fix: #9532
7303 this.disconnect(this._webkitListener);
7304 delete this._webkitListener;
7305 }
7306
7307 // Guard against memory leaks on IE (see #9268)
7308 if(dojo.isIE){
7309 this.iframe.onfocus = null;
7310 }
7311 this.iframe._loadFunc = null;
7312
7313 if(this._iframeRegHandle){
7314 dijit.unregisterIframe(this._iframeRegHandle);
7315 delete this._iframeRegHandle;
7316 }
7317
7318 if(this.textarea){
7319 var s = this.textarea.style;
7320 s.position = "";
7321 s.left = s.top = "";
7322 if(dojo.isIE){
7323 s.overflow = this.__overflow;
7324 this.__overflow = null;
7325 }
7326 this.textarea.value = save ? this._content : this.savedContent;
7327 dojo.destroy(this.domNode);
7328 this.domNode = this.textarea;
7329 }else{
7330 // if(save){
7331 // why we treat moz differently? comment out to fix #1061
7332 // if(dojo.isMoz){
7333 // var nc = dojo.doc.createElement("span");
7334 // this.domNode.appendChild(nc);
7335 // nc.innerHTML = this.editNode.innerHTML;
7336 // }else{
7337 // this.domNode.innerHTML = this._content;
7338 // }
7339 // }
7340
7341 // Note that this destroys the iframe
7342 this.domNode.innerHTML = save ? this._content : this.savedContent;
7343 }
7344 delete this.iframe;
7345
7346 dojo.removeClass(this.domNode, this.baseClass);
7347 this.isClosed = true;
7348 this.isLoaded = false;
7349
7350 delete this.editNode;
7351 delete this.focusNode;
7352
7353 if(this.window && this.window._frameElement){
7354 this.window._frameElement = null;
7355 }
7356
7357 this.window = null;
7358 this.document = null;
7359 this.editingArea = null;
7360 this.editorObject = null;
7361
7362 return changed; // Boolean: whether the content has been modified
7363 },
7364
7365 destroy: function(){
7366 if(!this.isClosed){ this.close(false); }
7367 this.inherited(arguments);
7368 },
7369
7370 _removeMozBogus: function(/* String */ html){
7371 // summary:
7372 // Post filter to remove unwanted HTML attributes generated by mozilla
7373 // tags:
7374 // private
7375 return html.replace(/\stype="_moz"/gi, '').replace(/\s_moz_dirty=""/gi, '').replace(/_moz_resizing="(true|false)"/gi,''); // String
7376 },
7377 _removeWebkitBogus: function(/* String */ html){
7378 // summary:
7379 // Post filter to remove unwanted HTML attributes generated by webkit
7380 // tags:
7381 // private
7382 html = html.replace(/\sclass="webkit-block-placeholder"/gi, '');
7383 html = html.replace(/\sclass="apple-style-span"/gi, '');
7384 return html; // String
7385 },
7386 _normalizeFontStyle: function(/* String */ html){
7387 // summary:
7388 // Convert 'strong' and 'em' to 'b' and 'i'.
7389 // description:
7390 // Moz can not handle strong/em tags correctly, so to help
7391 // mozilla and also to normalize output, convert them to 'b' and 'i'.
7392 //
7393 // Note the IE generates 'strong' and 'em' rather than 'b' and 'i'
7394 // tags:
7395 // private
7396 return html.replace(/<(\/)?strong([ \>])/gi, '<$1b$2')
7397 .replace(/<(\/)?em([ \>])/gi, '<$1i$2' ); // String
7398 },
7399
7400 _preFixUrlAttributes: function(/* String */ html){
7401 // summary:
7402 // Pre-filter to do fixing to href attributes on <a> and <img> tags
7403 // tags:
7404 // private
7405 return html.replace(/(?:(<a(?=\s).*?\shref=)("|')(.*?)\2)|(?:(<a\s.*?href=)([^"'][^ >]+))/gi,
7406 '$1$4$2$3$5$2 _djrealurl=$2$3$5$2')
7407 .replace(/(?:(<img(?=\s).*?\ssrc=)("|')(.*?)\2)|(?:(<img\s.*?src=)([^"'][^ >]+))/gi,
7408 '$1$4$2$3$5$2 _djrealurl=$2$3$5$2'); // String
7409 },
7410
7411 /*****************************************************************************
7412 The following functions implement HTML manipulation commands for various
7413 browser/contentEditable implementations. The goal of them is to enforce
7414 standard behaviors of them.
7415 ******************************************************************************/
7416
7417 _inserthorizontalruleImpl: function(argument){
7418 // summary:
7419 // This function implements the insertion of HTML 'HR' tags.
7420 // into a point on the page. IE doesn't to it right, so
7421 // we have to use an alternate form
7422 // argument:
7423 // arguments to the exec command, if any.
7424 // tags:
7425 // protected
7426 if(dojo.isIE){
7427 return this._inserthtmlImpl("<hr>");
7428 }
7429 return this.document.execCommand("inserthorizontalrule", false, argument);
7430 },
7431
7432 _unlinkImpl: function(argument){
7433 // summary:
7434 // This function implements the unlink of an 'a' tag.
7435 // argument:
7436 // arguments to the exec command, if any.
7437 // tags:
7438 // protected
7439 if((this.queryCommandEnabled("unlink")) && (dojo.isMoz || dojo.isWebKit)){
7440 var a = this._sCall("getAncestorElement", [ "a" ]);
7441 this._sCall("selectElement", [ a ]);
7442 return this.document.execCommand("unlink", false, null);
7443 }
7444 return this.document.execCommand("unlink", false, argument);
7445 },
7446
7447 _hilitecolorImpl: function(argument){
7448 // summary:
7449 // This function implements the hilitecolor command
7450 // argument:
7451 // arguments to the exec command, if any.
7452 // tags:
7453 // protected
7454 var returnValue;
7455 if(dojo.isMoz){
7456 // mozilla doesn't support hilitecolor properly when useCSS is
7457 // set to false (bugzilla #279330)
7458 this.document.execCommand("styleWithCSS", false, true);
7459 returnValue = this.document.execCommand("hilitecolor", false, argument);
7460 this.document.execCommand("styleWithCSS", false, false);
7461 }else{
7462 returnValue = this.document.execCommand("hilitecolor", false, argument);
7463 }
7464 return returnValue;
7465 },
7466
7467 _backcolorImpl: function(argument){
7468 // summary:
7469 // This function implements the backcolor command
7470 // argument:
7471 // arguments to the exec command, if any.
7472 // tags:
7473 // protected
7474 if(dojo.isIE){
7475 // Tested under IE 6 XP2, no problem here, comment out
7476 // IE weirdly collapses ranges when we exec these commands, so prevent it
7477 // var tr = this.document.selection.createRange();
7478 argument = argument ? argument : null;
7479 }
7480 return this.document.execCommand("backcolor", false, argument);
7481 },
7482
7483 _forecolorImpl: function(argument){
7484 // summary:
7485 // This function implements the forecolor command
7486 // argument:
7487 // arguments to the exec command, if any.
7488 // tags:
7489 // protected
7490 if(dojo.isIE){
7491 // Tested under IE 6 XP2, no problem here, comment out
7492 // IE weirdly collapses ranges when we exec these commands, so prevent it
7493 // var tr = this.document.selection.createRange();
7494 argument = argument? argument : null;
7495 }
7496 return this.document.execCommand("forecolor", false, argument);
7497 },
7498
7499 _inserthtmlImpl: function(argument){
7500 // summary:
7501 // This function implements the insertion of HTML content into
7502 // a point on the page.
7503 // argument:
7504 // The content to insert, if any.
7505 // tags:
7506 // protected
7507 argument = this._preFilterContent(argument);
7508 var rv = true;
7509 if(dojo.isIE){
7510 var insertRange = this.document.selection.createRange();
7511 if(this.document.selection.type.toUpperCase() == 'CONTROL'){
7512 var n=insertRange.item(0);
7513 while(insertRange.length){
7514 insertRange.remove(insertRange.item(0));
7515 }
7516 n.outerHTML=argument;
7517 }else{
7518 insertRange.pasteHTML(argument);
7519 }
7520 insertRange.select();
7521 //insertRange.collapse(true);
7522 }else if(dojo.isMoz && !argument.length){
7523 //mozilla can not inserthtml an empty html to delete current selection
7524 //so we delete the selection instead in this case
7525 this._sCall("remove"); // FIXME
7526 }else{
7527 rv = this.document.execCommand("inserthtml", false, argument);
7528 }
7529 return rv;
7530 },
7531
7532 getHeaderHeight: function(){
7533 // summary:
7534 // A function for obtaining the height of the header node
7535 return this._getNodeChildrenHeight(this.header); // Number
7536 },
7537
7538 getFooterHeight: function(){
7539 // summary:
7540 // A function for obtaining the height of the footer node
7541 return this._getNodeChildrenHeight(this.footer); // Number
7542 },
7543
7544 _getNodeChildrenHeight: function(node){
7545 // summary:
7546 // An internal function for computing the cumulative height of all child nodes of 'node'
7547 // node:
7548 // The node to process the children of;
7549 var h = 0;
7550 if(node && node.childNodes){
7551 // IE didn't compute it right when position was obtained on the node directly is some cases,
7552 // so we have to walk over all the children manually.
7553 var i;
7554 for(i = 0; i < node.childNodes.length; i++){
7555 var size = dojo.position(node.childNodes[i]);
7556 h += size.h;
7557 }
7558 }
7559 return h; // Number
7560 }
7561});
7562
7563}
7564
7565if(!dojo._hasResource["dijit._KeyNavContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
7566dojo._hasResource["dijit._KeyNavContainer"] = true;
7567dojo.provide("dijit._KeyNavContainer");
7568
7569
7570dojo.declare("dijit._KeyNavContainer",
7571 dijit._Container,
7572 {
7573
7574 // summary:
7575 // A _Container with keyboard navigation of its children.
7576 // description:
7577 // To use this mixin, call connectKeyNavHandlers() in
7578 // postCreate() and call startupKeyNavChildren() in startup().
7579 // It provides normalized keyboard and focusing code for Container
7580 // widgets.
7581/*=====
7582 // focusedChild: [protected] Widget
7583 // The currently focused child widget, or null if there isn't one
7584 focusedChild: null,
7585=====*/
7586
7587 // tabIndex: Integer
7588 // Tab index of the container; same as HTML tabIndex attribute.
7589 // Note then when user tabs into the container, focus is immediately
7590 // moved to the first item in the container.
7591 tabIndex: "0",
7592
7593 _keyNavCodes: {},
7594
7595 connectKeyNavHandlers: function(/*dojo.keys[]*/ prevKeyCodes, /*dojo.keys[]*/ nextKeyCodes){
7596 // summary:
7597 // Call in postCreate() to attach the keyboard handlers
7598 // to the container.
7599 // preKeyCodes: dojo.keys[]
7600 // Key codes for navigating to the previous child.
7601 // nextKeyCodes: dojo.keys[]
7602 // Key codes for navigating to the next child.
7603 // tags:
7604 // protected
7605
7606 var keyCodes = (this._keyNavCodes = {});
7607 var prev = dojo.hitch(this, this.focusPrev);
7608 var next = dojo.hitch(this, this.focusNext);
7609 dojo.forEach(prevKeyCodes, function(code){ keyCodes[code] = prev; });
7610 dojo.forEach(nextKeyCodes, function(code){ keyCodes[code] = next; });
7611 this.connect(this.domNode, "onkeypress", "_onContainerKeypress");
7612 this.connect(this.domNode, "onfocus", "_onContainerFocus");
7613 },
7614
7615 startupKeyNavChildren: function(){
7616 // summary:
7617 // Call in startup() to set child tabindexes to -1
7618 // tags:
7619 // protected
7620 dojo.forEach(this.getChildren(), dojo.hitch(this, "_startupChild"));
7621 },
7622
7623 addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){
7624 // summary:
7625 // Add a child to our _Container
7626 dijit._KeyNavContainer.superclass.addChild.apply(this, arguments);
7627 this._startupChild(widget);
7628 },
7629
7630 focus: function(){
7631 // summary:
7632 // Default focus() implementation: focus the first child.
7633 this.focusFirstChild();
7634 },
7635
7636 focusFirstChild: function(){
7637 // summary:
7638 // Focus the first focusable child in the container.
7639 // tags:
7640 // protected
7641 var child = this._getFirstFocusableChild();
7642 if(child){ // edge case: Menu could be empty or hidden
7643 this.focusChild(child);
7644 }
7645 },
7646
7647 focusNext: function(){
7648 // summary:
7649 // Focus the next widget
7650 // tags:
7651 // protected
7652 var child = this._getNextFocusableChild(this.focusedChild, 1);
7653 this.focusChild(child);
7654 },
7655
7656 focusPrev: function(){
7657 // summary:
7658 // Focus the last focusable node in the previous widget
7659 // (ex: go to the ComboButton icon section rather than button section)
7660 // tags:
7661 // protected
7662 var child = this._getNextFocusableChild(this.focusedChild, -1);
7663 this.focusChild(child, true);
7664 },
7665
7666 focusChild: function(/*dijit._Widget*/ widget, /*Boolean*/ last){
7667 // summary:
7668 // Focus widget.
7669 // widget:
7670 // Reference to container's child widget
7671 // last:
7672 // If true and if widget has multiple focusable nodes, focus the
7673 // last one instead of the first one
7674 // tags:
7675 // protected
7676
7677 if(this.focusedChild && widget !== this.focusedChild){
7678 this._onChildBlur(this.focusedChild);
7679 }
7680 widget.focus(last ? "end" : "start");
7681 this.focusedChild = widget;
7682 },
7683
7684 _startupChild: function(/*dijit._Widget*/ widget){
7685 // summary:
7686 // Setup for each child widget
7687 // description:
7688 // Sets tabIndex=-1 on each child, so that the tab key will
7689 // leave the container rather than visiting each child.
7690 // tags:
7691 // private
7692
7693 widget.set("tabIndex", "-1");
7694
7695 this.connect(widget, "_onFocus", function(){
7696 // Set valid tabIndex so tabbing away from widget goes to right place, see #10272
7697 widget.set("tabIndex", this.tabIndex);
7698 });
7699 this.connect(widget, "_onBlur", function(){
7700 widget.set("tabIndex", "-1");
7701 });
7702 },
7703
7704 _onContainerFocus: function(evt){
7705 // summary:
7706 // Handler for when the container gets focus
7707 // description:
7708 // Initially the container itself has a tabIndex, but when it gets
7709 // focus, switch focus to first child...
7710 // tags:
7711 // private
7712
7713 // Note that we can't use _onFocus() because switching focus from the
7714 // _onFocus() handler confuses the focus.js code
7715 // (because it causes _onFocusNode() to be called recursively)
7716
7717 // focus bubbles on Firefox,
7718 // so just make sure that focus has really gone to the container
7719 if(evt.target !== this.domNode){ return; }
7720
7721 this.focusFirstChild();
7722
7723 // and then set the container's tabIndex to -1,
7724 // (don't remove as that breaks Safari 4)
7725 // so that tab or shift-tab will go to the fields after/before
7726 // the container, rather than the container itself
7727 dojo.attr(this.domNode, "tabIndex", "-1");
7728 },
7729
7730 _onBlur: function(evt){
7731 // When focus is moved away the container, and its descendant (popup) widgets,
7732 // then restore the container's tabIndex so that user can tab to it again.
7733 // Note that using _onBlur() so that this doesn't happen when focus is shifted
7734 // to one of my child widgets (typically a popup)
7735 if(this.tabIndex){
7736 dojo.attr(this.domNode, "tabIndex", this.tabIndex);
7737 }
7738 this.inherited(arguments);
7739 },
7740
7741 _onContainerKeypress: function(evt){
7742 // summary:
7743 // When a key is pressed, if it's an arrow key etc. then
7744 // it's handled here.
7745 // tags:
7746 // private
7747 if(evt.ctrlKey || evt.altKey){ return; }
7748 var func = this._keyNavCodes[evt.charOrCode];
7749 if(func){
7750 func();
7751 dojo.stopEvent(evt);
7752 }
7753 },
7754
7755 _onChildBlur: function(/*dijit._Widget*/ widget){
7756 // summary:
7757 // Called when focus leaves a child widget to go
7758 // to a sibling widget.
7759 // tags:
7760 // protected
7761 },
7762
7763 _getFirstFocusableChild: function(){
7764 // summary:
7765 // Returns first child that can be focused
7766 return this._getNextFocusableChild(null, 1); // dijit._Widget
7767 },
7768
7769 _getNextFocusableChild: function(child, dir){
7770 // summary:
7771 // Returns the next or previous focusable child, compared
7772 // to "child"
7773 // child: Widget
7774 // The current widget
7775 // dir: Integer
7776 // * 1 = after
7777 // * -1 = before
7778 if(child){
7779 child = this._getSiblingOfChild(child, dir);
7780 }
7781 var children = this.getChildren();
7782 for(var i=0; i < children.length; i++){
7783 if(!child){
7784 child = children[(dir>0) ? 0 : (children.length-1)];
7785 }
7786 if(child.isFocusable()){
7787 return child; // dijit._Widget
7788 }
7789 child = this._getSiblingOfChild(child, dir);
7790 }
7791 // no focusable child found
7792 return null; // dijit._Widget
7793 }
7794 }
7795);
7796
7797}
7798
7799if(!dojo._hasResource["dijit.ToolbarSeparator"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
7800dojo._hasResource["dijit.ToolbarSeparator"] = true;
7801dojo.provide("dijit.ToolbarSeparator");
7802
7803
7804
7805
7806dojo.declare("dijit.ToolbarSeparator",
7807 [ dijit._Widget, dijit._Templated ],
7808 {
7809 // summary:
7810 // A spacer between two `dijit.Toolbar` items
7811 templateString: '<div class="dijitToolbarSeparator dijitInline" waiRole="presentation"></div>',
7812 postCreate: function(){ dojo.setSelectable(this.domNode, false); },
7813 isFocusable: function(){
7814 // summary:
7815 // This widget isn't focusable, so pass along that fact.
7816 // tags:
7817 // protected
7818 return false;
7819 }
7820
7821 });
7822
7823
7824
7825}
7826
7827if(!dojo._hasResource["dijit.Toolbar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
7828dojo._hasResource["dijit.Toolbar"] = true;
7829dojo.provide("dijit.Toolbar");
7830
7831
7832
7833
7834
7835dojo.declare("dijit.Toolbar",
7836 [dijit._Widget, dijit._Templated, dijit._KeyNavContainer],
7837 {
7838 // summary:
7839 // A Toolbar widget, used to hold things like `dijit.Editor` buttons
7840
7841 templateString:
7842 '<div class="dijit" waiRole="toolbar" tabIndex="${tabIndex}" dojoAttachPoint="containerNode">' +
7843 // '<table style="table-layout: fixed" class="dijitReset dijitToolbarTable">' + // factor out style
7844 // '<tr class="dijitReset" dojoAttachPoint="containerNode"></tr>'+
7845 // '</table>' +
7846 '</div>',
7847
7848 baseClass: "dijitToolbar",
7849
7850 postCreate: function(){
7851 this.connectKeyNavHandlers(
7852 this.isLeftToRight() ? [dojo.keys.LEFT_ARROW] : [dojo.keys.RIGHT_ARROW],
7853 this.isLeftToRight() ? [dojo.keys.RIGHT_ARROW] : [dojo.keys.LEFT_ARROW]
7854 );
7855 this.inherited(arguments);
7856 },
7857
7858 startup: function(){
7859 if(this._started){ return; }
7860
7861 this.startupKeyNavChildren();
7862
7863 this.inherited(arguments);
7864 }
7865}
7866);
7867
7868// For back-compat, remove for 2.0
7869
7870
7871}
7872
7873if(!dojo._hasResource["dijit._HasDropDown"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
7874dojo._hasResource["dijit._HasDropDown"] = true;
7875dojo.provide("dijit._HasDropDown");
7876
7877
7878
7879
7880dojo.declare("dijit._HasDropDown",
7881 null,
7882 {
7883 // summary:
7884 // Mixin for widgets that need drop down ability.
7885
7886 // _buttonNode: [protected] DomNode
7887 // The button/icon/node to click to display the drop down.
7888 // Can be set via a dojoAttachPoint assignment.
7889 // If missing, then either focusNode or domNode (if focusNode is also missing) will be used.
7890 _buttonNode: null,
7891
7892 // _arrowWrapperNode: [protected] DomNode
7893 // Will set CSS class dijitUpArrow, dijitDownArrow, dijitRightArrow etc. on this node depending
7894 // on where the drop down is set to be positioned.
7895 // Can be set via a dojoAttachPoint assignment.
7896 // If missing, then _buttonNode will be used.
7897 _arrowWrapperNode: null,
7898
7899 // _popupStateNode: [protected] DomNode
7900 // The node to set the popupActive class on.
7901 // Can be set via a dojoAttachPoint assignment.
7902 // If missing, then focusNode or _buttonNode (if focusNode is missing) will be used.
7903 _popupStateNode: null,
7904
7905 // _aroundNode: [protected] DomNode
7906 // The node to display the popup around.
7907 // Can be set via a dojoAttachPoint assignment.
7908 // If missing, then domNode will be used.
7909 _aroundNode: null,
7910
7911 // dropDown: [protected] Widget
7912 // The widget to display as a popup. This widget *must* be
7913 // defined before the startup function is called.
7914 dropDown: null,
7915
7916 // autoWidth: [protected] Boolean
7917 // Set to true to make the drop down at least as wide as this
7918 // widget. Set to false if the drop down should just be its
7919 // default width
7920 autoWidth: true,
7921
7922 // forceWidth: [protected] Boolean
7923 // Set to true to make the drop down exactly as wide as this
7924 // widget. Overrides autoWidth.
7925 forceWidth: false,
7926
7927 // maxHeight: [protected] Integer
7928 // The max height for our dropdown. Set to 0 for no max height.
7929 // any dropdown taller than this will have scrollbars
7930 maxHeight: 0,
7931
7932 // dropDownPosition: [const] String[]
7933 // This variable controls the position of the drop down.
7934 // It's an array of strings with the following values:
7935 //
7936 // * before: places drop down to the left of the target node/widget, or to the right in
7937 // the case of RTL scripts like Hebrew and Arabic
7938 // * after: places drop down to the right of the target node/widget, or to the left in
7939 // the case of RTL scripts like Hebrew and Arabic
7940 // * above: drop down goes above target node
7941 // * below: drop down goes below target node
7942 //
7943 // The list is positions is tried, in order, until a position is found where the drop down fits
7944 // within the viewport.
7945 //
7946 dropDownPosition: ["below","above"],
7947
7948 // _stopClickEvents: Boolean
7949 // When set to false, the click events will not be stopped, in
7950 // case you want to use them in your subwidget
7951 _stopClickEvents: true,
7952
7953 _onDropDownMouseDown: function(/*Event*/ e){
7954 // summary:
7955 // Callback when the user mousedown's on the arrow icon
7956
7957 if(this.disabled || this.readOnly){ return; }
7958
7959 this._docHandler = this.connect(dojo.doc, "onmouseup", "_onDropDownMouseUp");
7960
7961 this.toggleDropDown();
7962 },
7963
7964 _onDropDownMouseUp: function(/*Event?*/ e){
7965 // summary:
7966 // Callback when the user lifts their mouse after mouse down on the arrow icon.
7967 // If the drop is a simple menu and the mouse is over the menu, we execute it, otherwise, we focus our
7968 // dropDown node. If the event is missing, then we are not
7969 // a mouseup event.
7970 //
7971 // This is useful for the common mouse movement pattern
7972 // with native browser <select> nodes:
7973 // 1. mouse down on the select node (probably on the arrow)
7974 // 2. move mouse to a menu item while holding down the mouse button
7975 // 3. mouse up. this selects the menu item as though the user had clicked it.
7976 if(e && this._docHandler){
7977 this.disconnect(this._docHandler);
7978 }
7979 var dropDown = this.dropDown, overMenu = false;
7980
7981 if(e && this._opened){
7982 // This code deals with the corner-case when the drop down covers the original widget,
7983 // because it's so large. In that case mouse-up shouldn't select a value from the menu.
7984 // Find out if our target is somewhere in our dropdown widget,
7985 // but not over our _buttonNode (the clickable node)
7986 var c = dojo.position(this._buttonNode, true);
7987 if(!(e.pageX >= c.x && e.pageX <= c.x + c.w) ||
7988 !(e.pageY >= c.y && e.pageY <= c.y + c.h)){
7989 var t = e.target;
7990 while(t && !overMenu){
7991 if(dojo.hasClass(t, "dijitPopup")){
7992 overMenu = true;
7993 }else{
7994 t = t.parentNode;
7995 }
7996 }
7997 if(overMenu){
7998 t = e.target;
7999 if(dropDown.onItemClick){
8000 var menuItem;
8001 while(t && !(menuItem = dijit.byNode(t))){
8002 t = t.parentNode;
8003 }
8004 if(menuItem && menuItem.onClick && menuItem.getParent){
8005 menuItem.getParent().onItemClick(menuItem, e);
8006 }
8007 }
8008 return;
8009 }
8010 }
8011 }
8012 if(this._opened && dropDown.focus){
8013 // Focus the dropdown widget - do it on a delay so that we
8014 // don't steal our own focus.
8015 window.setTimeout(dojo.hitch(dropDown, "focus"), 1);
8016 }
8017 },
8018
8019 _onDropDownClick: function(/*Event*/ e){
8020 // the drop down was already opened on mousedown/keydown; just need to call stopEvent()
8021 if(this._stopClickEvents){
8022 dojo.stopEvent(e);
8023 }
8024 },
8025
8026 _setupDropdown: function(){
8027 // summary:
8028 // set up nodes and connect our mouse and keypress events
8029 this._buttonNode = this._buttonNode || this.focusNode || this.domNode;
8030 this._popupStateNode = this._popupStateNode || this.focusNode || this._buttonNode;
8031 this._aroundNode = this._aroundNode || this.domNode;
8032 this.connect(this._buttonNode, "onmousedown", "_onDropDownMouseDown");
8033 this.connect(this._buttonNode, "onclick", "_onDropDownClick");
8034 this.connect(this._buttonNode, "onkeydown", "_onDropDownKeydown");
8035 this.connect(this._buttonNode, "onkeyup", "_onKey");
8036
8037 // If we have a _setStateClass function (which happens when
8038 // we are a form widget), then we need to connect our open/close
8039 // functions to it
8040 if(this._setStateClass){
8041 this.connect(this, "openDropDown", "_setStateClass");
8042 this.connect(this, "closeDropDown", "_setStateClass");
8043 }
8044
8045 // Add a class to the "dijitDownArrowButton" type class to _buttonNode so theme can set direction of arrow
8046 // based on where drop down will normally appear
8047 var defaultPos = {
8048 "after" : this.isLeftToRight() ? "Right" : "Left",
8049 "before" : this.isLeftToRight() ? "Left" : "Right",
8050 "above" : "Up",
8051 "below" : "Down",
8052 "left" : "Left",
8053 "right" : "Right"
8054 }[this.dropDownPosition[0]] || this.dropDownPosition[0] || "Down";
8055 dojo.addClass(this._arrowWrapperNode || this._buttonNode, "dijit" + defaultPos + "ArrowButton");
8056 },
8057
8058 postCreate: function(){
8059 this._setupDropdown();
8060 this.inherited(arguments);
8061 },
8062
8063 destroyDescendants: function(){
8064 if(this.dropDown){
8065 // Destroy the drop down, unless it's already been destroyed. This can happen because
8066 // the drop down is a direct child of <body> even though it's logically my child.
8067 if(!this.dropDown._destroyed){
8068 this.dropDown.destroyRecursive();
8069 }
8070 delete this.dropDown;
8071 }
8072 this.inherited(arguments);
8073 },
8074
8075 _onDropDownKeydown: function(/*Event*/ e){
8076 if(e.keyCode == dojo.keys.DOWN_ARROW || e.keyCode == dojo.keys.ENTER || e.keyCode == dojo.keys.SPACE){
8077 e.preventDefault(); // stop IE screen jump
8078 }
8079 },
8080
8081 _onKey: function(/*Event*/ e){
8082 // summary:
8083 // Callback when the user presses a key while focused on the button node
8084
8085 if(this.disabled || this.readOnly){ return; }
8086 var d = this.dropDown;
8087 if(d && this._opened && d.handleKey){
8088 if(d.handleKey(e) === false){ return; }
8089 }
8090 if(d && this._opened && e.keyCode == dojo.keys.ESCAPE){
8091 this.toggleDropDown();
8092 }else if(d && !this._opened &&
8093 (e.keyCode == dojo.keys.DOWN_ARROW || e.keyCode == dojo.keys.ENTER || e.keyCode == dojo.keys.SPACE)){
8094 this.toggleDropDown();
8095 if(d.focus){
8096 setTimeout(dojo.hitch(d, "focus"), 1);
8097 }
8098 }
8099 },
8100
8101 _onBlur: function(){
8102 // summary:
8103 // Called magically when focus has shifted away from this widget and it's dropdown
8104
8105 this.closeDropDown();
8106 // don't focus on button. the user has explicitly focused on something else.
8107 this.inherited(arguments);
8108 },
8109
8110 isLoaded: function(){
8111 // summary:
8112 // Returns whether or not the dropdown is loaded. This can
8113 // be overridden in order to force a call to loadDropDown().
8114 // tags:
8115 // protected
8116
8117 return true;
8118 },
8119
8120 loadDropDown: function(/* Function */ loadCallback){
8121 // summary:
8122 // Loads the data for the dropdown, and at some point, calls
8123 // the given callback
8124 // tags:
8125 // protected
8126
8127 loadCallback();
8128 },
8129
8130 toggleDropDown: function(){
8131 // summary:
8132 // Toggle the drop-down widget; if it is up, close it, if not, open it
8133 // tags:
8134 // protected
8135
8136 if(this.disabled || this.readOnly){ return; }
8137 this.focus();
8138 var dropDown = this.dropDown;
8139 if(!dropDown){ return; }
8140 if(!this._opened){
8141 // If we aren't loaded, load it first so there isn't a flicker
8142 if(!this.isLoaded()){
8143 this.loadDropDown(dojo.hitch(this, "openDropDown"));
8144 return;
8145 }else{
8146 this.openDropDown();
8147 }
8148 }else{
8149 this.closeDropDown();
8150 }
8151 },
8152
8153 openDropDown: function(){
8154 // summary:
8155 // Opens the dropdown for this widget - it returns the
8156 // return value of dijit.popup.open
8157 // tags:
8158 // protected
8159
8160 var dropDown = this.dropDown;
8161 var ddNode = dropDown.domNode;
8162 var self = this;
8163
8164 // Prepare our popup's height and honor maxHeight if it exists.
8165
8166 // TODO: isn't maxHeight dependent on the return value from dijit.popup.open(),
8167 // ie, dependent on how much space is available (BK)
8168
8169 if(!this._preparedNode){
8170 dijit.popup.moveOffScreen(ddNode);
8171 this._preparedNode = true;
8172 // Check if we have explicitly set width and height on the dropdown widget dom node
8173 if(ddNode.style.width){
8174 this._explicitDDWidth = true;
8175 }
8176 if(ddNode.style.height){
8177 this._explicitDDHeight = true;
8178 }
8179 }
8180
8181 // Code for resizing dropdown (height limitation, or increasing width to match my width)
8182 if(this.maxHeight || this.forceWidth || this.autoWidth){
8183 var myStyle = {
8184 display: "",
8185 visibility: "hidden"
8186 };
8187 if(!this._explicitDDWidth){
8188 myStyle.width = "";
8189 }
8190 if(!this._explicitDDHeight){
8191 myStyle.height = "";
8192 }
8193 dojo.style(ddNode, myStyle);
8194
8195 // Get size of drop down, and determine if vertical scroll bar needed
8196 var mb = dojo.marginBox(ddNode);
8197 var overHeight = (this.maxHeight && mb.h > this.maxHeight);
8198 dojo.style(ddNode, {
8199 overflowX: "hidden",
8200 overflowY: overHeight ? "auto" : "hidden"
8201 });
8202 if(overHeight){
8203 mb.h = this.maxHeight;
8204 if("w" in mb){
8205 mb.w += 16; // room for vertical scrollbar
8206 }
8207 }else{
8208 delete mb.h;
8209 }
8210 delete mb.t;
8211 delete mb.l;
8212
8213 // Adjust dropdown width to match or be larger than my width
8214 if(this.forceWidth){
8215 mb.w = this.domNode.offsetWidth;
8216 }else if(this.autoWidth){
8217 mb.w = Math.max(mb.w, this.domNode.offsetWidth);
8218 }else{
8219 delete mb.w;
8220 }
8221
8222 // And finally, resize the dropdown to calculated height and width
8223 if(dojo.isFunction(dropDown.resize)){
8224 dropDown.resize(mb);
8225 }else{
8226 dojo.marginBox(ddNode, mb);
8227 }
8228 }
8229
8230 var retVal = dijit.popup.open({
8231 parent: this,
8232 popup: dropDown,
8233 around: this._aroundNode,
8234 orient: dijit.getPopupAroundAlignment((this.dropDownPosition && this.dropDownPosition.length) ? this.dropDownPosition : ["below"],this.isLeftToRight()),
8235 onExecute: function(){
8236 self.closeDropDown(true);
8237 },
8238 onCancel: function(){
8239 self.closeDropDown(true);
8240 },
8241 onClose: function(){
8242 dojo.attr(self._popupStateNode, "popupActive", false);
8243 dojo.removeClass(self._popupStateNode, "dijitHasDropDownOpen");
8244 self._opened = false;
8245 self.state = "";
8246 }
8247 });
8248 dojo.attr(this._popupStateNode, "popupActive", "true");
8249 dojo.addClass(self._popupStateNode, "dijitHasDropDownOpen");
8250 this._opened=true;
8251 this.state="Opened";
8252 // TODO: set this.checked and call setStateClass(), to affect button look while drop down is shown
8253 return retVal;
8254 },
8255
8256 closeDropDown: function(/*Boolean*/ focus){
8257 // summary:
8258 // Closes the drop down on this widget
8259 // tags:
8260 // protected
8261
8262 if(this._opened){
8263 if(focus){ this.focus(); }
8264 dijit.popup.close(this.dropDown);
8265 this._opened = false;
8266 this.state = "";
8267 }
8268 }
8269
8270 }
8271);
8272
8273}
8274
8275if(!dojo._hasResource["dijit.form.Button"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8276dojo._hasResource["dijit.form.Button"] = true;
8277dojo.provide("dijit.form.Button");
8278
8279
8280
8281
8282
8283dojo.declare("dijit.form.Button",
8284 dijit.form._FormWidget,
8285 {
8286 // summary:
8287 // Basically the same thing as a normal HTML button, but with special styling.
8288 // description:
8289 // Buttons can display a label, an icon, or both.
8290 // A label should always be specified (through innerHTML) or the label
8291 // attribute. It can be hidden via showLabel=false.
8292 // example:
8293 // | <button dojoType="dijit.form.Button" onClick="...">Hello world</button>
8294 //
8295 // example:
8296 // | var button1 = new dijit.form.Button({label: "hello world", onClick: foo});
8297 // | dojo.body().appendChild(button1.domNode);
8298
8299 // label: HTML String
8300 // Text to display in button.
8301 // If the label is hidden (showLabel=false) then and no title has
8302 // been specified, then label is also set as title attribute of icon.
8303 label: "",
8304
8305 // showLabel: Boolean
8306 // Set this to true to hide the label text and display only the icon.
8307 // (If showLabel=false then iconClass must be specified.)
8308 // Especially useful for toolbars.
8309 // If showLabel=true, the label will become the title (a.k.a. tooltip/hint) of the icon.
8310 //
8311 // The exception case is for computers in high-contrast mode, where the label
8312 // will still be displayed, since the icon doesn't appear.
8313 showLabel: true,
8314
8315 // iconClass: String
8316 // Class to apply to DOMNode in button to make it display an icon
8317 iconClass: "",
8318
8319 // type: String
8320 // Defines the type of button. "button", "submit", or "reset".
8321 type: "button",
8322
8323 baseClass: "dijitButton",
8324
8325 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\">&#x25CF;</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"),
8326
8327 attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
8328 value: "valueNode",
8329 iconClass: { node: "iconNode", type: "class" }
8330 }),
8331
8332
8333 _onClick: function(/*Event*/ e){
8334 // summary:
8335 // Internal function to handle click actions
8336 if(this.disabled){
8337 return false;
8338 }
8339 this._clicked(); // widget click actions
8340 return this.onClick(e); // user click actions
8341 },
8342
8343 _onButtonClick: function(/*Event*/ e){
8344 // summary:
8345 // Handler when the user activates the button portion.
8346 if(this._onClick(e) === false){ // returning nothing is same as true
8347 e.preventDefault(); // needed for checkbox
8348 }else if(this.type == "submit" && !(this.valueNode||this.focusNode).form){ // see if a nonform widget needs to be signalled
8349 for(var node=this.domNode; node.parentNode/*#5935*/; node=node.parentNode){
8350 var widget=dijit.byNode(node);
8351 if(widget && typeof widget._onSubmit == "function"){
8352 widget._onSubmit(e);
8353 break;
8354 }
8355 }
8356 }else if(this.valueNode){
8357 this.valueNode.click();
8358 e.preventDefault(); // cancel BUTTON click and continue with hidden INPUT click
8359 }
8360 },
8361
8362 _fillContent: function(/*DomNode*/ source){
8363 // Overrides _Templated._fillContent().
8364 // If button label is specified as srcNodeRef.innerHTML rather than
8365 // this.params.label, handle it here.
8366 if(source && (!this.params || !("label" in this.params))){
8367 this.set('label', source.innerHTML);
8368 }
8369 },
8370
8371 postCreate: function(){
8372 dojo.setSelectable(this.focusNode, false);
8373 this.inherited(arguments);
8374 },
8375
8376 _setShowLabelAttr: function(val){
8377 if(this.containerNode){
8378 dojo.toggleClass(this.containerNode, "dijitDisplayNone", !val);
8379 }
8380 this.showLabel = val;
8381 },
8382
8383 onClick: function(/*Event*/ e){
8384 // summary:
8385 // Callback for when button is clicked.
8386 // If type="submit", return true to perform submit, or false to cancel it.
8387 // type:
8388 // callback
8389 return true; // Boolean
8390 },
8391
8392 _clicked: function(/*Event*/ e){
8393 // summary:
8394 // Internal overridable function for when the button is clicked
8395 },
8396
8397 setLabel: function(/*String*/ content){
8398 // summary:
8399 // Deprecated. Use set('label', ...) instead.
8400 dojo.deprecated("dijit.form.Button.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
8401 this.set("label", content);
8402 },
8403
8404 _setLabelAttr: function(/*String*/ content){
8405 // summary:
8406 // Hook for attr('label', ...) to work.
8407 // description:
8408 // Set the label (text) of the button; takes an HTML string.
8409 this.containerNode.innerHTML = this.label = content;
8410 if(this.showLabel == false && !this.params.title){
8411 this.titleNode.title = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
8412 }
8413 }
8414});
8415
8416
8417dojo.declare("dijit.form.DropDownButton", [dijit.form.Button, dijit._Container, dijit._HasDropDown], {
8418 // summary:
8419 // A button with a drop down
8420 //
8421 // example:
8422 // | <button dojoType="dijit.form.DropDownButton" label="Hello world">
8423 // | <div dojotype="dijit.Menu">...</div>
8424 // | </button>
8425 //
8426 // example:
8427 // | var button1 = new dijit.form.DropDownButton({ label: "hi", dropDown: new dijit.Menu(...) });
8428 // | dojo.body().appendChild(button1);
8429 //
8430
8431 baseClass : "dijitDropDownButton",
8432
8433 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\">&#9660;</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"),
8434
8435 _fillContent: function(){
8436 // Overrides Button._fillContent().
8437 //
8438 // My inner HTML contains both the button contents and a drop down widget, like
8439 // <DropDownButton> <span>push me</span> <Menu> ... </Menu> </DropDownButton>
8440 // The first node is assumed to be the button content. The widget is the popup.
8441
8442 if(this.srcNodeRef){ // programatically created buttons might not define srcNodeRef
8443 //FIXME: figure out how to filter out the widget and use all remaining nodes as button
8444 // content, not just nodes[0]
8445 var nodes = dojo.query("*", this.srcNodeRef);
8446 dijit.form.DropDownButton.superclass._fillContent.call(this, nodes[0]);
8447
8448 // save pointer to srcNode so we can grab the drop down widget after it's instantiated
8449 this.dropDownContainer = this.srcNodeRef;
8450 }
8451 },
8452
8453 startup: function(){
8454 if(this._started){ return; }
8455
8456 // the child widget from srcNodeRef is the dropdown widget. Insert it in the page DOM,
8457 // make it invisible, and store a reference to pass to the popup code.
8458 if(!this.dropDown){
8459 var dropDownNode = dojo.query("[widgetId]", this.dropDownContainer)[0];
8460 this.dropDown = dijit.byNode(dropDownNode);
8461 delete this.dropDownContainer;
8462 }
8463 dijit.popup.moveOffScreen(this.dropDown.domNode);
8464
8465 this.inherited(arguments);
8466 },
8467
8468 isLoaded: function(){
8469 // Returns whether or not we are loaded - if our dropdown has an href,
8470 // then we want to check that.
8471 var dropDown = this.dropDown;
8472 return (!dropDown.href || dropDown.isLoaded);
8473 },
8474
8475 loadDropDown: function(){
8476 // Loads our dropdown
8477 var dropDown = this.dropDown;
8478 if(!dropDown){ return; }
8479 if(!this.isLoaded()){
8480 var handler = dojo.connect(dropDown, "onLoad", this, function(){
8481 dojo.disconnect(handler);
8482 this.openDropDown();
8483 });
8484 dropDown.refresh();
8485 }else{
8486 this.openDropDown();
8487 }
8488 },
8489
8490 isFocusable: function(){
8491 // Overridden so that focus is handled by the _HasDropDown mixin, not by
8492 // the _FormWidget mixin.
8493 return this.inherited(arguments) && !this._mouseDown;
8494 }
8495});
8496
8497dojo.declare("dijit.form.ComboButton", dijit.form.DropDownButton, {
8498 // summary:
8499 // A combination button and drop-down button.
8500 // Users can click one side to "press" the button, or click an arrow
8501 // icon to display the drop down.
8502 //
8503 // example:
8504 // | <button dojoType="dijit.form.ComboButton" onClick="...">
8505 // | <span>Hello world</span>
8506 // | <div dojoType="dijit.Menu">...</div>
8507 // | </button>
8508 //
8509 // example:
8510 // | var button1 = new dijit.form.ComboButton({label: "hello world", onClick: foo, dropDown: "myMenu"});
8511 // | dojo.body().appendChild(button1.domNode);
8512 //
8513
8514 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\">&#9660;</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"),
8515
8516 attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap), {
8517 id: "",
8518 tabIndex: ["focusNode", "titleNode"],
8519 title: "titleNode"
8520 }),
8521
8522 // optionsTitle: String
8523 // Text that describes the options menu (accessibility)
8524 optionsTitle: "",
8525
8526 baseClass: "dijitComboButton",
8527
8528 // Set classes like dijitButtonContentsHover or dijitArrowButtonActive depending on
8529 // mouse action over specified node
8530 cssStateNodes: {
8531 "buttonNode": "dijitButtonNode",
8532 "titleNode": "dijitButtonContents",
8533 "_popupStateNode": "dijitDownArrowButton"
8534 },
8535
8536 _focusedNode: null,
8537
8538 _onButtonKeyPress: function(/*Event*/ evt){
8539 // summary:
8540 // Handler for right arrow key when focus is on left part of button
8541 if(evt.charOrCode == dojo.keys[this.isLeftToRight() ? "RIGHT_ARROW" : "LEFT_ARROW"]){
8542 dijit.focus(this._popupStateNode);
8543 dojo.stopEvent(evt);
8544 }
8545 },
8546
8547 _onArrowKeyPress: function(/*Event*/ evt){
8548 // summary:
8549 // Handler for left arrow key when focus is on right part of button
8550 if(evt.charOrCode == dojo.keys[this.isLeftToRight() ? "LEFT_ARROW" : "RIGHT_ARROW"]){
8551 dijit.focus(this.titleNode);
8552 dojo.stopEvent(evt);
8553 }
8554 },
8555
8556 focus: function(/*String*/ position){
8557 // summary:
8558 // Focuses this widget to according to position, if specified,
8559 // otherwise on arrow node
8560 // position:
8561 // "start" or "end"
8562
8563 dijit.focus(position == "start" ? this.titleNode : this._popupStateNode);
8564 }
8565});
8566
8567dojo.declare("dijit.form.ToggleButton", dijit.form.Button, {
8568 // summary:
8569 // A button that can be in two states (checked or not).
8570 // Can be base class for things like tabs or checkbox or radio buttons
8571
8572 baseClass: "dijitToggleButton",
8573
8574 // checked: Boolean
8575 // Corresponds to the native HTML <input> element's attribute.
8576 // In markup, specified as "checked='checked'" or just "checked".
8577 // True if the button is depressed, or the checkbox is checked,
8578 // or the radio button is selected, etc.
8579 checked: false,
8580
8581 attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap), {
8582 checked:"focusNode"
8583 }),
8584
8585 _clicked: function(/*Event*/ evt){
8586 this.set('checked', !this.checked);
8587 },
8588
8589 _setCheckedAttr: function(/*Boolean*/ value, /* Boolean? */ priorityChange){
8590 this.checked = value;
8591 dojo.attr(this.focusNode || this.domNode, "checked", value);
8592 dijit.setWaiState(this.focusNode || this.domNode, "pressed", value);
8593 this._handleOnChange(value, priorityChange);
8594 },
8595
8596 setChecked: function(/*Boolean*/ checked){
8597 // summary:
8598 // Deprecated. Use set('checked', true/false) instead.
8599 dojo.deprecated("setChecked("+checked+") is deprecated. Use set('checked',"+checked+") instead.", "", "2.0");
8600 this.set('checked', checked);
8601 },
8602
8603 reset: function(){
8604 // summary:
8605 // Reset the widget's value to what it was at initialization time
8606
8607 this._hasBeenBlurred = false;
8608
8609 // set checked state to original setting
8610 this.set('checked', this.params.checked || false);
8611 }
8612});
8613
8614}
8615
8616if(!dojo._hasResource["dijit._editor._Plugin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8617dojo._hasResource["dijit._editor._Plugin"] = true;
8618dojo.provide("dijit._editor._Plugin");
8619
8620
8621
8622dojo.declare("dijit._editor._Plugin", null, {
8623 // summary
8624 // Base class for a "plugin" to the editor, which is usually
8625 // a single button on the Toolbar and some associated code
8626
8627 constructor: function(/*Object?*/args, /*DomNode?*/node){
8628 this.params = args || {};
8629 dojo.mixin(this, this.params);
8630 this._connects=[];
8631 },
8632
8633 // editor: [const] dijit.Editor
8634 // Points to the parent editor
8635 editor: null,
8636
8637 // iconClassPrefix: [const] String
8638 // The CSS class name for the button node is formed from `iconClassPrefix` and `command`
8639 iconClassPrefix: "dijitEditorIcon",
8640
8641 // button: dijit._Widget?
8642 // Pointer to `dijit.form.Button` or other widget (ex: `dijit.form.FilteringSelect`)
8643 // that is added to the toolbar to control this plugin.
8644 // If not specified, will be created on initialization according to `buttonClass`
8645 button: null,
8646
8647 // command: String
8648 // String like "insertUnorderedList", "outdent", "justifyCenter", etc. that represents an editor command.
8649 // Passed to editor.execCommand() if `useDefaultCommand` is true.
8650 command: "",
8651
8652 // useDefaultCommand: Boolean
8653 // If true, this plugin executes by calling Editor.execCommand() with the argument specified in `command`.
8654 useDefaultCommand: true,
8655
8656 // buttonClass: Widget Class
8657 // Class of widget (ex: dijit.form.Button or dijit.form.FilteringSelect)
8658 // that is added to the toolbar to control this plugin.
8659 // This is used to instantiate the button, unless `button` itself is specified directly.
8660 buttonClass: dijit.form.Button,
8661
8662 getLabel: function(/*String*/key){
8663 // summary:
8664 // Returns the label to use for the button
8665 // tags:
8666 // private
8667 return this.editor.commands[key]; // String
8668 },
8669
8670 _initButton: function(){
8671 // summary:
8672 // Initialize the button or other widget that will control this plugin.
8673 // This code only works for plugins controlling built-in commands in the editor.
8674 // tags:
8675 // protected extension
8676 if(this.command.length){
8677 var label = this.getLabel(this.command),
8678 editor = this.editor,
8679 className = this.iconClassPrefix+" "+this.iconClassPrefix + this.command.charAt(0).toUpperCase() + this.command.substr(1);
8680 if(!this.button){
8681 var props = dojo.mixin({
8682 label: label,
8683 dir: editor.dir,
8684 lang: editor.lang,
8685 showLabel: false,
8686 iconClass: className,
8687 dropDown: this.dropDown,
8688 tabIndex: "-1"
8689 }, this.params || {});
8690 this.button = new this.buttonClass(props);
8691 }
8692 }
8693 },
8694
8695 destroy: function(){
8696 // summary:
8697 // Destroy this plugin
8698
8699 dojo.forEach(this._connects, dojo.disconnect);
8700 if(this.dropDown){
8701 this.dropDown.destroyRecursive();
8702 }
8703 },
8704
8705 connect: function(o, f, tf){
8706 // summary:
8707 // Make a dojo.connect() that is automatically disconnected when this plugin is destroyed.
8708 // Similar to `dijit._Widget.connect`.
8709 // tags:
8710 // protected
8711 this._connects.push(dojo.connect(o, f, this, tf));
8712 },
8713
8714 updateState: function(){
8715 // summary:
8716 // Change state of the plugin to respond to events in the editor.
8717 // description:
8718 // This is called on meaningful events in the editor, such as change of selection
8719 // or caret position (but not simple typing of alphanumeric keys). It gives the
8720 // plugin a chance to update the CSS of its button.
8721 //
8722 // For example, the "bold" plugin will highlight/unhighlight the bold button depending on whether the
8723 // characters next to the caret are bold or not.
8724 //
8725 // Only makes sense when `useDefaultCommand` is true, as it calls Editor.queryCommandEnabled(`command`).
8726 var e = this.editor,
8727 c = this.command,
8728 checked, enabled;
8729 if(!e || !e.isLoaded || !c.length){ return; }
8730 if(this.button){
8731 try{
8732 enabled = e.queryCommandEnabled(c);
8733 if(this.enabled !== enabled){
8734 this.enabled = enabled;
8735 this.button.set('disabled', !enabled);
8736 }
8737 if(typeof this.button.checked == 'boolean'){
8738 checked = e.queryCommandState(c);
8739 if(this.checked !== checked){
8740 this.checked = checked;
8741 this.button.set('checked', e.queryCommandState(c));
8742 }
8743 }
8744 }catch(e){
8745 console.log(e); // FIXME: we shouldn't have debug statements in our code. Log as an error?
8746 }
8747 }
8748 },
8749
8750 setEditor: function(/*dijit.Editor*/ editor){
8751 // summary:
8752 // Tell the plugin which Editor it is associated with.
8753
8754 // TODO: refactor code to just pass editor to constructor.
8755
8756 // FIXME: detach from previous editor!!
8757 this.editor = editor;
8758
8759 // FIXME: prevent creating this if we don't need to (i.e., editor can't handle our command)
8760 this._initButton();
8761
8762 // Processing for buttons that execute by calling editor.execCommand()
8763 if(this.button && this.useDefaultCommand){
8764 if(this.editor.queryCommandAvailable(this.command)){
8765 this.connect(this.button, "onClick",
8766 dojo.hitch(this.editor, "execCommand", this.command, this.commandArg)
8767 );
8768 }else{
8769 // hide button because editor doesn't support command (due to browser limitations)
8770 this.button.domNode.style.display = "none";
8771 }
8772 }
8773
8774 this.connect(this.editor, "onNormalizedDisplayChanged", "updateState");
8775 },
8776
8777 setToolbar: function(/*dijit.Toolbar*/ toolbar){
8778 // summary:
8779 // Tell the plugin to add it's controller widget (often a button)
8780 // to the toolbar. Does nothing if there is no controller widget.
8781
8782 // TODO: refactor code to just pass toolbar to constructor.
8783
8784 if(this.button){
8785 toolbar.addChild(this.button);
8786 }
8787 // console.debug("adding", this.button, "to:", toolbar);
8788 }
8789});
8790
8791}
8792
8793if(!dojo._hasResource["dijit._editor.plugins.EnterKeyHandling"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8794dojo._hasResource["dijit._editor.plugins.EnterKeyHandling"] = true;
8795dojo.provide("dijit._editor.plugins.EnterKeyHandling");
8796
8797
8798
8799dojo.declare("dijit._editor.plugins.EnterKeyHandling", dijit._editor._Plugin, {
8800 // summary:
8801 // This plugin tries to make all browsers behave consistently w.r.t
8802 // displaying paragraphs, specifically dealing with when the user presses
8803 // the ENTER key.
8804 //
8805 // It deals mainly with how the text appears on the screen (specifically
8806 // address the double-spaced line problem on IE), but also has some code
8807 // to normalize what attr('value') returns.
8808 //
8809 // description:
8810 // This plugin has three modes:
8811 //
8812 // * blockModeForEnter=BR
8813 // * blockModeForEnter=DIV
8814 // * blockModeForEnter=P
8815 //
8816 // In blockModeForEnter=P, the ENTER key semantically means "start a new
8817 // paragraph", whereas shift-ENTER means "new line in the current paragraph".
8818 // For example:
8819 //
8820 // | first paragraph <shift-ENTER>
8821 // | second line of first paragraph <ENTER>
8822 // |
8823 // | second paragraph
8824 //
8825 // In the other two modes, the ENTER key means to go to a new line in the
8826 // current paragraph, and users [visually] create a new paragraph by pressing ENTER twice.
8827 // For example, if the user enters text into an editor like this:
8828 //
8829 // | one <ENTER>
8830 // | two <ENTER>
8831 // | three <ENTER>
8832 // | <ENTER>
8833 // | four <ENTER>
8834 // | five <ENTER>
8835 // | six <ENTER>
8836 //
8837 // It will appear on the screen as two paragraphs of three lines each.
8838 //
8839 // blockNodeForEnter=BR
8840 // --------------------
8841 // On IE, typing the above keystrokes in the editor will internally produce DOM of:
8842 //
8843 // | <p>one</p>
8844 // | <p>two</p>
8845 // | <p>three</p>
8846 // | <p></p>
8847 // | <p>four</p>
8848 // | <p>five</p>
8849 // | <p>six</p>
8850 //
8851 // However, blockNodeForEnter=BR makes the Editor on IE display like other browsers, by
8852 // changing the CSS for the <p> node to not have top/bottom margins,
8853 // thus eliminating the double-spaced appearance.
8854 //
8855 // Also, attr('value') when used w/blockNodeForEnter=br on IE will return:
8856 //
8857 // | <p> one <br> two <br> three </p>
8858 // | <p> four <br> five <br> six </p>
8859 //
8860 // This output normalization implemented by a filter when the
8861 // editor writes out it's data, to convert consecutive <p>
8862 // nodes into a single <p> node with internal <br> separators.
8863 //
8864 // There's also a pre-filter to mirror the post-filter.
8865 // It converts a single <p> with <br> line breaks
8866 // into separate <p> nodes, and creates empty <p> nodes for spacing
8867 // between paragraphs.
8868 //
8869 // On FF typing the above keystrokes will internally generate:
8870 //
8871 // | one <br> two <br> three <br> <br> four <br> five <br> six <br>
8872 //
8873 // And on Safari it will generate:
8874 //
8875 // | "one"
8876 // | <div>two</div>
8877 // | <div>three</div>
8878 // | <div><br></div>
8879 // | <div>four</div>
8880 // | <div>five</div>
8881 // | <div>six</div>
8882 //
8883 // Thus, Safari and FF already look correct although semantically their content is a bit strange.
8884 // On Safari or Firefox blockNodeForEnter=BR uses the builtin editor command "insertBrOnReturn",
8885 // but that doesn't seem to do anything.
8886 // Thus, attr('value') on safari/FF returns the browser-specific HTML listed above,
8887 // rather than the semantically meaningful value that IE returns: <p>one<br>two</p> <p>three<br>four</p>.
8888 //
8889 // (Note: originally based on http://bugs.dojotoolkit.org/ticket/2859)
8890 //
8891 // blockNodeForEnter=P
8892 // -------------------
8893 // Plugin will monitor keystrokes and update the editor's content on the fly,
8894 // so that the ENTER key will create a new <p> on FF and Safari (it already
8895 // works that way by default on IE).
8896 //
8897 // blockNodeForEnter=DIV
8898 // ---------------------
8899 // Follows the same code path as blockNodeForEnter=P but inserting a <div>
8900 // on ENTER key. Although it produces strange internal DOM, like this:
8901 //
8902 // | <div>paragraph one</div>
8903 // | <div>paragraph one, line 2</div>
8904 // | <div>&nbsp;</div>
8905 // | <div>paragraph two</div>
8906 //
8907 // it does provide a consistent look on all browsers, and the on-the-fly DOM updating
8908 // can be useful for collaborative editing.
8909
8910 // blockNodeForEnter: String
8911 // This property decides the behavior of Enter key. It can be either P,
8912 // DIV, BR, or empty (which means disable this feature). Anything else
8913 // will trigger errors.
8914 //
8915 // See class description for more details.
8916 blockNodeForEnter: 'BR',
8917
8918 constructor: function(args){
8919 if(args){
8920 dojo.mixin(this,args);
8921 }
8922 },
8923
8924 setEditor: function(editor){
8925 // Overrides _Plugin.setEditor().
8926 this.editor = editor;
8927 if(this.blockNodeForEnter == 'BR'){
8928 if(dojo.isIE){
8929 editor.contentDomPreFilters.push(dojo.hitch(this, "regularPsToSingleLinePs"));
8930 editor.contentDomPostFilters.push(dojo.hitch(this, "singleLinePsToRegularPs"));
8931 editor.onLoadDeferred.addCallback(dojo.hitch(this, "_fixNewLineBehaviorForIE"));
8932 }else{
8933 editor.onLoadDeferred.addCallback(dojo.hitch(this,function(d){
8934 try{
8935 this.editor.document.execCommand("insertBrOnReturn", false, true);
8936 }catch(e){}
8937 return d;
8938 }));
8939 }
8940 }else if(this.blockNodeForEnter){
8941 // add enter key handler
8942 // FIXME: need to port to the new event code!!
8943 dojo['require']('dijit._editor.range');
8944 var h = dojo.hitch(this,this.handleEnterKey);
8945 editor.addKeyHandler(13, 0, 0, h); //enter
8946 editor.addKeyHandler(13, 0, 1, h); //shift+enter
8947 this.connect(this.editor,'onKeyPressed','onKeyPressed');
8948 }
8949 },
8950 onKeyPressed: function(e){
8951 // summary:
8952 // Handler for keypress events.
8953 // tags:
8954 // private
8955 if(this._checkListLater){
8956 if(dojo.withGlobal(this.editor.window, 'isCollapsed', dijit)){
8957 var liparent=dojo.withGlobal(this.editor.window, 'getAncestorElement', dijit._editor.selection, ['LI']);
8958 if(!liparent){
8959 // circulate the undo detection code by calling RichText::execCommand directly
8960 dijit._editor.RichText.prototype.execCommand.call(this.editor, 'formatblock',this.blockNodeForEnter);
8961 // set the innerHTML of the new block node
8962 var block = dojo.withGlobal(this.editor.window, 'getAncestorElement', dijit._editor.selection, [this.blockNodeForEnter]);
8963 if(block){
8964 block.innerHTML=this.bogusHtmlContent;
8965 if(dojo.isIE){
8966 // move to the start by moving backwards one char
8967 var r = this.editor.document.selection.createRange();
8968 r.move('character',-1);
8969 r.select();
8970 }
8971 }else{
8972 console.error('onKeyPressed: Cannot find the new block node'); // FIXME
8973 }
8974 }else{
8975 if(dojo.isMoz){
8976 if(liparent.parentNode.parentNode.nodeName == 'LI'){
8977 liparent=liparent.parentNode.parentNode;
8978 }
8979 }
8980 var fc=liparent.firstChild;
8981 if(fc && fc.nodeType == 1 && (fc.nodeName == 'UL' || fc.nodeName == 'OL')){
8982 liparent.insertBefore(fc.ownerDocument.createTextNode('\xA0'),fc);
8983 var newrange = dijit.range.create(this.editor.window);
8984 newrange.setStart(liparent.firstChild,0);
8985 var selection = dijit.range.getSelection(this.editor.window, true);
8986 selection.removeAllRanges();
8987 selection.addRange(newrange);
8988 }
8989 }
8990 }
8991 this._checkListLater = false;
8992 }
8993 if(this._pressedEnterInBlock){
8994 // the new created is the original current P, so we have previousSibling below
8995 if(this._pressedEnterInBlock.previousSibling){
8996 this.removeTrailingBr(this._pressedEnterInBlock.previousSibling);
8997 }
8998 delete this._pressedEnterInBlock;
8999 }
9000 },
9001
9002 // bogusHtmlContent: [private] String
9003 // HTML to stick into a new empty block
9004 bogusHtmlContent: '&nbsp;',
9005
9006 // blockNodes: [private] Regex
9007 // Regex for testing if a given tag is a block level (display:block) tag
9008 blockNodes: /^(?:P|H1|H2|H3|H4|H5|H6|LI)$/,
9009
9010 handleEnterKey: function(e){
9011 // summary:
9012 // Handler for enter key events when blockModeForEnter is DIV or P.
9013 // description:
9014 // Manually handle enter key event to make the behavior consistent across
9015 // all supported browsers. See class description for details.
9016 // tags:
9017 // private
9018
9019 var selection, range, newrange, doc=this.editor.document,br;
9020 if(e.shiftKey){ // shift+enter always generates <br>
9021 var parent = dojo.withGlobal(this.editor.window, "getParentElement", dijit._editor.selection);
9022 var header = dijit.range.getAncestor(parent,this.blockNodes);
9023 if(header){
9024 if(!e.shiftKey && header.tagName == 'LI'){
9025 return true; // let browser handle
9026 }
9027 selection = dijit.range.getSelection(this.editor.window);
9028 range = selection.getRangeAt(0);
9029 if(!range.collapsed){
9030 range.deleteContents();
9031 selection = dijit.range.getSelection(this.editor.window);
9032 range = selection.getRangeAt(0);
9033 }
9034 if(dijit.range.atBeginningOfContainer(header, range.startContainer, range.startOffset)){
9035 if(e.shiftKey){
9036 br=doc.createElement('br');
9037 newrange = dijit.range.create(this.editor.window);
9038 header.insertBefore(br,header.firstChild);
9039 newrange.setStartBefore(br.nextSibling);
9040 selection.removeAllRanges();
9041 selection.addRange(newrange);
9042 }else{
9043 dojo.place(br, header, "before");
9044 }
9045 }else if(dijit.range.atEndOfContainer(header, range.startContainer, range.startOffset)){
9046 newrange = dijit.range.create(this.editor.window);
9047 br=doc.createElement('br');
9048 if(e.shiftKey){
9049 header.appendChild(br);
9050 header.appendChild(doc.createTextNode('\xA0'));
9051 newrange.setStart(header.lastChild,0);
9052 }else{
9053 dojo.place(br, header, "after");
9054 newrange.setStartAfter(header);
9055 }
9056
9057 selection.removeAllRanges();
9058 selection.addRange(newrange);
9059 }else{
9060 return true; // let browser handle
9061 }
9062 }else{
9063 // don't change this: do not call this.execCommand, as that may have other logic in subclass
9064 dijit._editor.RichText.prototype.execCommand.call(this.editor, 'inserthtml', '<br>');
9065 }
9066 return false;
9067 }
9068 var _letBrowserHandle = true;
9069
9070 // first remove selection
9071 selection = dijit.range.getSelection(this.editor.window);
9072 range = selection.getRangeAt(0);
9073 if(!range.collapsed){
9074 range.deleteContents();
9075 selection = dijit.range.getSelection(this.editor.window);
9076 range = selection.getRangeAt(0);
9077 }
9078
9079 var block = dijit.range.getBlockAncestor(range.endContainer, null, this.editor.editNode);
9080 var blockNode = block.blockNode;
9081
9082 // if this is under a LI or the parent of the blockNode is LI, just let browser to handle it
9083 if((this._checkListLater = (blockNode && (blockNode.nodeName == 'LI' || blockNode.parentNode.nodeName == 'LI')))){
9084 if(dojo.isMoz){
9085 // press enter in middle of P may leave a trailing <br/>, let's remove it later
9086 this._pressedEnterInBlock = blockNode;
9087 }
9088 // if this li only contains spaces, set the content to empty so the browser will outdent this item
9089 if(/^(\s|&nbsp;|\xA0|<span\b[^>]*\bclass=['"]Apple-style-span['"][^>]*>(\s|&nbsp;|\xA0)<\/span>)?(<br>)?$/.test(blockNode.innerHTML)){
9090 // empty LI node
9091 blockNode.innerHTML = '';
9092 if(dojo.isWebKit){ // WebKit tosses the range when innerHTML is reset
9093 newrange = dijit.range.create(this.editor.window);
9094 newrange.setStart(blockNode, 0);
9095 selection.removeAllRanges();
9096 selection.addRange(newrange);
9097 }
9098 this._checkListLater = false; // nothing to check since the browser handles outdent
9099 }
9100 return true;
9101 }
9102
9103 // text node directly under body, let's wrap them in a node
9104 if(!block.blockNode || block.blockNode===this.editor.editNode){
9105 try{
9106 dijit._editor.RichText.prototype.execCommand.call(this.editor, 'formatblock',this.blockNodeForEnter);
9107 }catch(e2){ /*squelch FF3 exception bug when editor content is a single BR*/ }
9108 // get the newly created block node
9109 // FIXME
9110 block = {blockNode:dojo.withGlobal(this.editor.window, "getAncestorElement", dijit._editor.selection, [this.blockNodeForEnter]),
9111 blockContainer: this.editor.editNode};
9112 if(block.blockNode){
9113 if(block.blockNode != this.editor.editNode &&
9114 (!(block.blockNode.textContent || block.blockNode.innerHTML).replace(/^\s+|\s+$/g, "").length)){
9115 this.removeTrailingBr(block.blockNode);
9116 return false;
9117 }
9118 }else{ // we shouldn't be here if formatblock worked
9119 block.blockNode = this.editor.editNode;
9120 }
9121 selection = dijit.range.getSelection(this.editor.window);
9122 range = selection.getRangeAt(0);
9123 }
9124
9125 var newblock = doc.createElement(this.blockNodeForEnter);
9126 newblock.innerHTML=this.bogusHtmlContent;
9127 this.removeTrailingBr(block.blockNode);
9128 if(dijit.range.atEndOfContainer(block.blockNode, range.endContainer, range.endOffset)){
9129 if(block.blockNode === block.blockContainer){
9130 block.blockNode.appendChild(newblock);
9131 }else{
9132 dojo.place(newblock, block.blockNode, "after");
9133 }
9134 _letBrowserHandle = false;
9135 // lets move caret to the newly created block
9136 newrange = dijit.range.create(this.editor.window);
9137 newrange.setStart(newblock, 0);
9138 selection.removeAllRanges();
9139 selection.addRange(newrange);
9140 if(this.editor.height){
9141 dojo.window.scrollIntoView(newblock);
9142 }
9143 }else if(dijit.range.atBeginningOfContainer(block.blockNode,
9144 range.startContainer, range.startOffset)){
9145 dojo.place(newblock, block.blockNode, block.blockNode === block.blockContainer ? "first" : "before");
9146 if(newblock.nextSibling && this.editor.height){
9147 // position input caret - mostly WebKit needs this
9148 newrange = dijit.range.create(this.editor.window);
9149 newrange.setStart(newblock.nextSibling, 0);
9150 selection.removeAllRanges();
9151 selection.addRange(newrange);
9152 // browser does not scroll the caret position into view, do it manually
9153 dojo.window.scrollIntoView(newblock.nextSibling);
9154 }
9155 _letBrowserHandle = false;
9156 }else{ //press enter in the middle of P/DIV/Whatever/
9157 if(block.blockNode === block.blockContainer){
9158 block.blockNode.appendChild(newblock);
9159 }else{
9160 dojo.place(newblock, block.blockNode, "after");
9161 }
9162 _letBrowserHandle = false;
9163
9164 // Clone any block level styles.
9165 if(block.blockNode.style){
9166 if(newblock.style){
9167 if(block.blockNode.style.cssText){
9168 newblock.style.cssText = block.blockNode.style.cssText;
9169 }
9170 }
9171 }
9172
9173 // Okay, we probably have to split.
9174 var rs = range.startContainer;
9175 if(rs && rs.nodeType == 3){
9176 // Text node, we have to split it.
9177 var nodeToMove, tNode;
9178 var txt = rs.nodeValue;
9179 var startNode = doc.createTextNode(txt.substring(0, range.startOffset));
9180 var endNode = doc.createTextNode(txt.substring(range.startOffset, txt.length));
9181
9182 // Place the split, then remove original nodes.
9183 dojo.place(startNode, rs, "before");
9184 dojo.place(endNode, rs, "after");
9185 dojo.destroy(rs);
9186
9187 // Okay, we split the text. Now we need to see if we're
9188 // parented to the block element we're splitting and if
9189 // not, we have to split all the way up. Ugh.
9190 var parentC = startNode.parentNode;
9191 while(parentC !== block.blockNode){
9192 var tg = parentC.tagName;
9193 var newTg = doc.createElement(tg);
9194 // Clone over any 'style' data.
9195 if(parentC.style){
9196 if(newTg.style){
9197 if(parentC.style.cssText){
9198 newTg.style.cssText = parentC.style.cssText;
9199 }
9200 }
9201 }
9202
9203 nodeToMove = endNode;
9204 while(nodeToMove){
9205 tNode = nodeToMove.nextSibling;
9206 newTg.appendChild(nodeToMove);
9207 nodeToMove = tNode;
9208 }
9209 dojo.place(newTg, parentC, "after");
9210 startNode = parentC;
9211 endNode = newTg;
9212 parentC = parentC.parentNode;
9213 }
9214
9215 // Lastly, move the split out tags to the new block.
9216 // as they should now be split properly.
9217 nodeToMove = endNode;
9218 if(nodeToMove.nodeType == 1 || (nodeToMove.nodeType == 3 && nodeToMove.nodeValue)){
9219 // Non-blank text and non-text nodes need to clear out that blank space
9220 // before moving the contents.
9221 newblock.innerHTML = "";
9222 }
9223 while(nodeToMove){
9224 tNode = nodeToMove.nextSibling;
9225 newblock.appendChild(nodeToMove);
9226 nodeToMove = tNode;
9227 }
9228 }
9229
9230 //lets move caret to the newly created block
9231 newrange = dijit.range.create(this.editor.window);
9232 newrange.setStart(newblock, 0);
9233 selection.removeAllRanges();
9234 selection.addRange(newrange);
9235 if(this.editor.height){
9236 dijit.scrollIntoView(newblock);
9237 }
9238 if(dojo.isMoz){
9239 // press enter in middle of P may leave a trailing <br/>, let's remove it later
9240 this._pressedEnterInBlock = block.blockNode;
9241 }
9242 }
9243 return _letBrowserHandle;
9244 },
9245
9246 removeTrailingBr: function(container){
9247 // summary:
9248 // If last child of container is a <br>, then remove it.
9249 // tags:
9250 // private
9251 var para = /P|DIV|LI/i.test(container.tagName) ?
9252 container : dijit._editor.selection.getParentOfType(container,['P','DIV','LI']);
9253
9254 if(!para){ return; }
9255 if(para.lastChild){
9256 if((para.childNodes.length > 1 && para.lastChild.nodeType == 3 && /^[\s\xAD]*$/.test(para.lastChild.nodeValue)) ||
9257 para.lastChild.tagName=='BR'){
9258
9259 dojo.destroy(para.lastChild);
9260 }
9261 }
9262 if(!para.childNodes.length){
9263 para.innerHTML=this.bogusHtmlContent;
9264 }
9265 },
9266 _fixNewLineBehaviorForIE: function(d){
9267 // summary:
9268 // Insert CSS so <p> nodes don't have spacing around them,
9269 // thus hiding the fact that ENTER key on IE is creating new
9270 // paragraphs
9271
9272 // cannot use !important since there may be custom user styling;
9273 var doc = this.editor.document;
9274 if(doc.__INSERTED_EDITIOR_NEWLINE_CSS === undefined){
9275 var style = dojo.create("style", {type: "text/css"}, doc.getElementsByTagName("head")[0]);
9276 style.styleSheet.cssText = "p{margin:0;}"; // cannot use !important since there may be custom user styling;
9277 this.editor.document.__INSERTED_EDITIOR_NEWLINE_CSS = true;
9278 }
9279 return d;
9280 },
9281 regularPsToSingleLinePs: function(element, noWhiteSpaceInEmptyP){
9282 // summary:
9283 // Converts a <p> node containing <br>'s into multiple <p> nodes.
9284 // description:
9285 // See singleLinePsToRegularPs(). This method does the
9286 // opposite thing, and is used as a pre-filter when loading the
9287 // editor, to mirror the effects of the post-filter at end of edit.
9288 // tags:
9289 // private
9290 function wrapLinesInPs(el){
9291 // move "lines" of top-level text nodes into ps
9292 function wrapNodes(nodes){
9293 // nodes are assumed to all be siblings
9294 var newP = nodes[0].ownerDocument.createElement('p'); // FIXME: not very idiomatic
9295 nodes[0].parentNode.insertBefore(newP, nodes[0]);
9296 dojo.forEach(nodes, function(node){
9297 newP.appendChild(node);
9298 });
9299 }
9300
9301 var currentNodeIndex = 0;
9302 var nodesInLine = [];
9303 var currentNode;
9304 while(currentNodeIndex < el.childNodes.length){
9305 currentNode = el.childNodes[currentNodeIndex];
9306 if( currentNode.nodeType==3 || // text node
9307 (currentNode.nodeType==1 && currentNode.nodeName!='BR' && dojo.style(currentNode, "display")!="block")
9308 ){
9309 nodesInLine.push(currentNode);
9310 }else{
9311 // hit line delimiter; process nodesInLine if there are any
9312 var nextCurrentNode = currentNode.nextSibling;
9313 if(nodesInLine.length){
9314 wrapNodes(nodesInLine);
9315 currentNodeIndex = (currentNodeIndex+1)-nodesInLine.length;
9316 if(currentNode.nodeName=="BR"){
9317 dojo.destroy(currentNode);
9318 }
9319 }
9320 nodesInLine = [];
9321 }
9322 currentNodeIndex++;
9323 }
9324 if(nodesInLine.length){ wrapNodes(nodesInLine); }
9325 }
9326
9327 function splitP(el){
9328 // split a paragraph into seperate paragraphs at BRs
9329 var currentNode = null;
9330 var trailingNodes = [];
9331 var lastNodeIndex = el.childNodes.length-1;
9332 for(var i=lastNodeIndex; i>=0; i--){
9333 currentNode = el.childNodes[i];
9334 if(currentNode.nodeName=="BR"){
9335 var newP = currentNode.ownerDocument.createElement('p');
9336 dojo.place(newP, el, "after");
9337 if(trailingNodes.length==0 && i != lastNodeIndex){
9338 newP.innerHTML = "&nbsp;"
9339 }
9340 dojo.forEach(trailingNodes, function(node){
9341 newP.appendChild(node);
9342 });
9343 dojo.destroy(currentNode);
9344 trailingNodes = [];
9345 }else{
9346 trailingNodes.unshift(currentNode);
9347 }
9348 }
9349 }
9350
9351 var pList = [];
9352 var ps = element.getElementsByTagName('p');
9353 dojo.forEach(ps, function(p){ pList.push(p); });
9354 dojo.forEach(pList, function(p){
9355 var prevSib = p.previousSibling;
9356 if( (prevSib) && (prevSib.nodeType == 1) &&
9357 (prevSib.nodeName == 'P' || dojo.style(prevSib, 'display') != 'block')
9358 ){
9359 var newP = p.parentNode.insertBefore(this.document.createElement('p'), p);
9360 // this is essential to prevent IE from losing the P.
9361 // if it's going to be innerHTML'd later we need
9362 // to add the &nbsp; to _really_ force the issue
9363 newP.innerHTML = noWhiteSpaceInEmptyP ? "" : "&nbsp;";
9364 }
9365 splitP(p);
9366 },this.editor);
9367 wrapLinesInPs(element);
9368 return element;
9369 },
9370
9371 singleLinePsToRegularPs: function(element){
9372 // summary:
9373 // Called as post-filter.
9374 // Apparently collapses adjacent <p> nodes into a single <p>
9375 // nodes with <br> separating each line.
9376 //
9377 // example:
9378 // Given this input:
9379 // | <p>line 1</p>
9380 // | <p>line 2</p>
9381 // | <ol>
9382 // | <li>item 1
9383 // | <li>item 2
9384 // | </ol>
9385 // | <p>line 3</p>
9386 // | <p>line 4</p>
9387 //
9388 // Will convert to:
9389 // | <p>line 1<br>line 2</p>
9390 // | <ol>
9391 // | <li>item 1
9392 // | <li>item 2
9393 // | </ol>
9394 // | <p>line 3<br>line 4</p>
9395 //
9396 // Not sure why this situation would even come up after the pre-filter and
9397 // the enter-key-handling code.
9398 //
9399 // tags:
9400 // private
9401
9402 function getParagraphParents(node){
9403 // summary:
9404 // Used to get list of all nodes that contain paragraphs.
9405 // Seems like that would just be the very top node itself, but apparently not.
9406 var ps = node.getElementsByTagName('p');
9407 var parents = [];
9408 for(var i=0; i<ps.length; i++){
9409 var p = ps[i];
9410 var knownParent = false;
9411 for(var k=0; k < parents.length; k++){
9412 if(parents[k] === p.parentNode){
9413 knownParent = true;
9414 break;
9415 }
9416 }
9417 if(!knownParent){
9418 parents.push(p.parentNode);
9419 }
9420 }
9421 return parents;
9422 }
9423
9424 function isParagraphDelimiter(node){
9425 return (!node.childNodes.length || node.innerHTML=="&nbsp;");
9426 }
9427
9428 var paragraphContainers = getParagraphParents(element);
9429 for(var i=0; i<paragraphContainers.length; i++){
9430 var container = paragraphContainers[i];
9431 var firstPInBlock = null;
9432 var node = container.firstChild;
9433 var deleteNode = null;
9434 while(node){
9435 if(node.nodeType != 1 || node.tagName != 'P' ||
9436 (node.getAttributeNode('style') || {/*no style*/}).specified){
9437 firstPInBlock = null;
9438 }else if(isParagraphDelimiter(node)){
9439 deleteNode = node;
9440 firstPInBlock = null;
9441 }else{
9442 if(firstPInBlock == null){
9443 firstPInBlock = node;
9444 }else{
9445 if( (!firstPInBlock.lastChild || firstPInBlock.lastChild.nodeName != 'BR') &&
9446 (node.firstChild) &&
9447 (node.firstChild.nodeName != 'BR')
9448 ){
9449 firstPInBlock.appendChild(this.editor.document.createElement('br'));
9450 }
9451 while(node.firstChild){
9452 firstPInBlock.appendChild(node.firstChild);
9453 }
9454 deleteNode = node;
9455 }
9456 }
9457 node = node.nextSibling;
9458 if(deleteNode){
9459 dojo.destroy(deleteNode);
9460 deleteNode = null;
9461 }
9462 }
9463 }
9464 return element;
9465 }
9466});
9467
9468}
9469
9470if(!dojo._hasResource["dijit.Editor"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9471dojo._hasResource["dijit.Editor"] = true;
9472dojo.provide("dijit.Editor");
9473
9474
9475
9476
9477
9478
9479
9480
9481
9482
9483
9484
9485
9486dojo.declare(
9487 "dijit.Editor",
9488 dijit._editor.RichText,
9489 {
9490 // summary:
9491 // A rich text Editing widget
9492 //
9493 // description:
9494 // This widget provides basic WYSIWYG editing features, based on the browser's
9495 // underlying rich text editing capability, accompanied by a toolbar (`dijit.Toolbar`).
9496 // A plugin model is available to extend the editor's capabilities as well as the
9497 // the options available in the toolbar. Content generation may vary across
9498 // browsers, and clipboard operations may have different results, to name
9499 // a few limitations. Note: this widget should not be used with the HTML
9500 // &lt;TEXTAREA&gt; tag -- see dijit._editor.RichText for details.
9501
9502 // plugins: Object[]
9503 // A list of plugin names (as strings) or instances (as objects)
9504 // for this widget.
9505 //
9506 // When declared in markup, it might look like:
9507 // | plugins="['bold',{name:'dijit._editor.plugins.FontChoice', command:'fontName', generic:true}]"
9508 plugins: null,
9509
9510 // extraPlugins: Object[]
9511 // A list of extra plugin names which will be appended to plugins array
9512 extraPlugins: null,
9513
9514 constructor: function(){
9515 // summary:
9516 // Runs on widget initialization to setup arrays etc.
9517 // tags:
9518 // private
9519
9520 if(!dojo.isArray(this.plugins)){
9521 this.plugins=["undo","redo","|","cut","copy","paste","|","bold","italic","underline","strikethrough","|",
9522 "insertOrderedList","insertUnorderedList","indent","outdent","|","justifyLeft","justifyRight","justifyCenter","justifyFull",
9523 "dijit._editor.plugins.EnterKeyHandling" /*, "createLink"*/];
9524 }
9525
9526 this._plugins=[];
9527 this._editInterval = this.editActionInterval * 1000;
9528
9529 //IE will always lose focus when other element gets focus, while for FF and safari,
9530 //when no iframe is used, focus will be lost whenever another element gets focus.
9531 //For IE, we can connect to onBeforeDeactivate, which will be called right before
9532 //the focus is lost, so we can obtain the selected range. For other browsers,
9533 //no equivelent of onBeforeDeactivate, so we need to do two things to make sure
9534 //selection is properly saved before focus is lost: 1) when user clicks another
9535 //element in the page, in which case we listen to mousedown on the entire page and
9536 //see whether user clicks out of a focus editor, if so, save selection (focus will
9537 //only lost after onmousedown event is fired, so we can obtain correct caret pos.)
9538 //2) when user tabs away from the editor, which is handled in onKeyDown below.
9539 if(dojo.isIE){
9540 this.events.push("onBeforeDeactivate");
9541 this.events.push("onBeforeActivate");
9542 }
9543 },
9544
9545 postCreate: function(){
9546 //for custom undo/redo, if enabled.
9547 this._steps=this._steps.slice(0);
9548 this._undoedSteps=this._undoedSteps.slice(0);
9549
9550 if(dojo.isArray(this.extraPlugins)){
9551 this.plugins=this.plugins.concat(this.extraPlugins);
9552 }
9553
9554 // Set up a deferred so that the value isn't applied to the editor
9555 // until all the plugins load, needed to avoid timing condition
9556 // reported in #10537.
9557 this.setValueDeferred = new dojo.Deferred();
9558
9559 this.inherited(arguments);
9560
9561 this.commands = dojo.i18n.getLocalization("dijit._editor", "commands", this.lang);
9562
9563 if(!this.toolbar){
9564 // if we haven't been assigned a toolbar, create one
9565 this.toolbar = new dijit.Toolbar({
9566 dir: this.dir,
9567 lang: this.lang
9568 });
9569 this.header.appendChild(this.toolbar.domNode);
9570 }
9571
9572 dojo.forEach(this.plugins, this.addPlugin, this);
9573
9574 // Okay, denote the value can now be set.
9575 this.setValueDeferred.callback(true);
9576
9577 dojo.addClass(this.iframe.parentNode, "dijitEditorIFrameContainer");
9578 dojo.addClass(this.iframe, "dijitEditorIFrame");
9579 dojo.attr(this.iframe, "allowTransparency", true);
9580
9581 if(dojo.isWebKit){
9582 // Disable selecting the entire editor by inadvertant double-clicks.
9583 // on buttons, title bar, etc. Otherwise clicking too fast on
9584 // a button such as undo/redo selects the entire editor.
9585 dojo.style(this.domNode, "KhtmlUserSelect", "none");
9586 }
9587 this.toolbar.startup();
9588 this.onNormalizedDisplayChanged(); //update toolbar button status
9589 },
9590 destroy: function(){
9591 dojo.forEach(this._plugins, function(p){
9592 if(p && p.destroy){
9593 p.destroy();
9594 }
9595 });
9596 this._plugins=[];
9597 this.toolbar.destroyRecursive();
9598 delete this.toolbar;
9599 this.inherited(arguments);
9600 },
9601 addPlugin: function(/*String||Object*/plugin, /*Integer?*/index){
9602 // summary:
9603 // takes a plugin name as a string or a plugin instance and
9604 // adds it to the toolbar and associates it with this editor
9605 // instance. The resulting plugin is added to the Editor's
9606 // plugins array. If index is passed, it's placed in the plugins
9607 // array at that index. No big magic, but a nice helper for
9608 // passing in plugin names via markup.
9609 //
9610 // plugin: String, args object or plugin instance
9611 //
9612 // args:
9613 // This object will be passed to the plugin constructor
9614 //
9615 // index: Integer
9616 // Used when creating an instance from
9617 // something already in this.plugins. Ensures that the new
9618 // instance is assigned to this.plugins at that index.
9619 var args=dojo.isString(plugin)?{name:plugin}:plugin;
9620 if(!args.setEditor){
9621 var o={"args":args,"plugin":null,"editor":this};
9622 dojo.publish(dijit._scopeName + ".Editor.getPlugin",[o]);
9623 if(!o.plugin){
9624 var pc = dojo.getObject(args.name);
9625 if(pc){
9626 o.plugin=new pc(args);
9627 }
9628 }
9629 if(!o.plugin){
9630 console.warn('Cannot find plugin',plugin);
9631 return;
9632 }
9633 plugin=o.plugin;
9634 }
9635 if(arguments.length > 1){
9636 this._plugins[index] = plugin;
9637 }else{
9638 this._plugins.push(plugin);
9639 }
9640 plugin.setEditor(this);
9641 if(dojo.isFunction(plugin.setToolbar)){
9642 plugin.setToolbar(this.toolbar);
9643 }
9644 },
9645 //the following 3 functions are required to make the editor play nice under a layout widget, see #4070
9646 startup: function(){
9647 // summary:
9648 // Exists to make Editor work as a child of a layout widget.
9649 // Developers don't need to call this method.
9650 // tags:
9651 // protected
9652 //console.log('startup',arguments);
9653 },
9654 resize: function(size){
9655 // summary:
9656 // Resize the editor to the specified size, see `dijit.layout._LayoutWidget.resize`
9657 if(size){
9658 // we've been given a height/width for the entire editor (toolbar + contents), calls layout()
9659 // to split the allocated size between the toolbar and the contents
9660 dijit.layout._LayoutWidget.prototype.resize.apply(this, arguments);
9661 }
9662 /*
9663 else{
9664 // do nothing, the editor is already laid out correctly. The user has probably specified
9665 // the height parameter, which was used to set a size on the iframe
9666 }
9667 */
9668 },
9669 layout: function(){
9670 // summary:
9671 // Called from `dijit.layout._LayoutWidget.resize`. This shouldn't be called directly
9672 // tags:
9673 // protected
9674
9675 // Converts the iframe (or rather the <div> surrounding it) to take all the available space
9676 // except what's needed for the header (toolbars) and footer (breadcrumbs, etc).
9677 // A class was added to the iframe container and some themes style it, so we have to
9678 // calc off the added margins and padding too. See tracker: #10662
9679 var areaHeight = (this._contentBox.h -
9680 (this.getHeaderHeight() + this.getFooterHeight() +
9681 dojo._getPadBorderExtents(this.iframe.parentNode).h +
9682 dojo._getMarginExtents(this.iframe.parentNode).h));
9683 this.editingArea.style.height = areaHeight + "px";
9684 if(this.iframe){
9685 this.iframe.style.height="100%";
9686 }
9687 this._layoutMode = true;
9688 },
9689 _onIEMouseDown: function(/*Event*/ e){
9690 // summary:
9691 // IE only to prevent 2 clicks to focus
9692 // tags:
9693 // private
9694 var outsideClientArea;
9695 // IE 8's componentFromPoint is broken, which is a shame since it
9696 // was smaller code, but oh well. We have to do this brute force
9697 // to detect if the click was scroller or not.
9698 var b = this.document.body;
9699 var clientWidth = b.clientWidth;
9700 var clientHeight = b.clientHeight;
9701 var clientLeft = b.clientLeft;
9702 var offsetWidth = b.offsetWidth;
9703 var offsetHeight = b.offsetHeight;
9704 var offsetLeft = b.offsetLeft;
9705
9706 //Check for vertical scroller click.
9707 bodyDir = b.dir?b.dir.toLowerCase():""
9708 if(bodyDir != "rtl"){
9709 if(clientWidth < offsetWidth && e.x > clientWidth && e.x < offsetWidth){
9710 // Check the click was between width and offset width, if so, scroller
9711 outsideClientArea = true;
9712 }
9713 }else{
9714 // RTL mode, we have to go by the left offsets.
9715 if(e.x < clientLeft && e.x > offsetLeft){
9716 // Check the click was between width and offset width, if so, scroller
9717 outsideClientArea = true;
9718 }
9719 }
9720 if(!outsideClientArea){
9721 // Okay, might be horiz scroller, check that.
9722 if(clientHeight < offsetHeight && e.y > clientHeight && e.y < offsetHeight){
9723 // Horizontal scroller.
9724 outsideClientArea = true;
9725 }
9726 }
9727 if(!outsideClientArea){
9728 delete this._cursorToStart; // Remove the force to cursor to start position.
9729 delete this._savedSelection; // new mouse position overrides old selection
9730 if(e.target.tagName == "BODY"){
9731 setTimeout(dojo.hitch(this, "placeCursorAtEnd"), 0);
9732 }
9733 this.inherited(arguments);
9734 }
9735 },
9736 onBeforeActivate: function(e){
9737 this._restoreSelection();
9738 },
9739 onBeforeDeactivate: function(e){
9740 // summary:
9741 // Called on IE right before focus is lost. Saves the selected range.
9742 // tags:
9743 // private
9744 if(this.customUndo){
9745 this.endEditing(true);
9746 }
9747 //in IE, the selection will be lost when other elements get focus,
9748 //let's save focus before the editor is deactivated
9749 if(e.target.tagName != "BODY"){
9750 this._saveSelection();
9751 }
9752 //console.log('onBeforeDeactivate',this);
9753 },
9754
9755 /* beginning of custom undo/redo support */
9756
9757 // customUndo: Boolean
9758 // Whether we shall use custom undo/redo support instead of the native
9759 // browser support. By default, we only enable customUndo for IE, as it
9760 // has broken native undo/redo support. Note: the implementation does
9761 // support other browsers which have W3C DOM2 Range API implemented.
9762 // It was also enabled on WebKit, to fix undo/redo enablement. (#9613)
9763 customUndo: dojo.isIE || dojo.isWebKit,
9764
9765 // editActionInterval: Integer
9766 // When using customUndo, not every keystroke will be saved as a step.
9767 // Instead typing (including delete) will be grouped together: after
9768 // a user stops typing for editActionInterval seconds, a step will be
9769 // saved; if a user resume typing within editActionInterval seconds,
9770 // the timeout will be restarted. By default, editActionInterval is 3
9771 // seconds.
9772 editActionInterval: 3,
9773
9774 beginEditing: function(cmd){
9775 // summary:
9776 // Called to note that the user has started typing alphanumeric characters, if it's not already noted.
9777 // Deals with saving undo; see editActionInterval parameter.
9778 // tags:
9779 // private
9780 if(!this._inEditing){
9781 this._inEditing=true;
9782 this._beginEditing(cmd);
9783 }
9784 if(this.editActionInterval>0){
9785 if(this._editTimer){
9786 clearTimeout(this._editTimer);
9787 }
9788 this._editTimer = setTimeout(dojo.hitch(this, this.endEditing), this._editInterval);
9789 }
9790 },
9791 _steps:[],
9792 _undoedSteps:[],
9793 execCommand: function(cmd){
9794 // summary:
9795 // Main handler for executing any commands to the editor, like paste, bold, etc.
9796 // Called by plugins, but not meant to be called by end users.
9797 // tags:
9798 // protected
9799 if(this.customUndo && (cmd == 'undo' || cmd == 'redo')){
9800 return this[cmd]();
9801 }else{
9802 if(this.customUndo){
9803 this.endEditing();
9804 this._beginEditing();
9805 }
9806 var r;
9807 try{
9808 r = this.inherited('execCommand', arguments);
9809 if(dojo.isWebKit && cmd == 'paste' && !r){ //see #4598: safari does not support invoking paste from js
9810 throw { code: 1011 }; // throw an object like Mozilla's error
9811 }
9812 }catch(e){
9813 //TODO: when else might we get an exception? Do we need the Mozilla test below?
9814 if(e.code == 1011 /* Mozilla: service denied */ && /copy|cut|paste/.test(cmd)){
9815 // Warn user of platform limitation. Cannot programmatically access clipboard. See ticket #4136
9816 var sub = dojo.string.substitute,
9817 accel = {cut:'X', copy:'C', paste:'V'};
9818 alert(sub(this.commands.systemShortcut,
9819 [this.commands[cmd], sub(this.commands[dojo.isMac ? 'appleKey' : 'ctrlKey'], [accel[cmd]])]));
9820 }
9821 r = false;
9822 }
9823 if(this.customUndo){
9824 this._endEditing();
9825 }
9826 return r;
9827 }
9828 },
9829 queryCommandEnabled: function(cmd){
9830 // summary:
9831 // Returns true if specified editor command is enabled.
9832 // Used by the plugins to know when to highlight/not highlight buttons.
9833 // tags:
9834 // protected
9835 if(this.customUndo && (cmd == 'undo' || cmd == 'redo')){
9836 return cmd == 'undo' ? (this._steps.length > 1) : (this._undoedSteps.length > 0);
9837 }else{
9838 return this.inherited('queryCommandEnabled',arguments);
9839 }
9840 },
9841 _moveToBookmark: function(b){
9842 // summary:
9843 // Selects the text specified in bookmark b
9844 // tags:
9845 // private
9846 var bookmark = b.mark;
9847 var mark = b.mark;
9848 var col = b.isCollapsed;
9849 var r, sNode, eNode, sel;
9850 if(mark){
9851 if(dojo.isIE){
9852 if(dojo.isArray(mark)){
9853 //IE CONTROL, have to use the native bookmark.
9854 bookmark = [];
9855 dojo.forEach(mark,function(n){
9856 bookmark.push(dijit.range.getNode(n,this.editNode));
9857 },this);
9858 dojo.withGlobal(this.window,'moveToBookmark',dijit,[{mark: bookmark, isCollapsed: col}]);
9859 }else{
9860 if(mark.startContainer && mark.endContainer){
9861 // Use the pseudo WC3 range API. This works better for positions
9862 // than the IE native bookmark code.
9863 sel = dijit.range.getSelection(this.window);
9864 if(sel && sel.removeAllRanges){
9865 sel.removeAllRanges();
9866 r = dijit.range.create(this.window);
9867 sNode = dijit.range.getNode(mark.startContainer,this.editNode);
9868 eNode = dijit.range.getNode(mark.endContainer,this.editNode);
9869 if(sNode && eNode){
9870 // Okay, we believe we found the position, so add it into the selection
9871 // There are cases where it may not be found, particularly in undo/redo, when
9872 // IE changes the underlying DOM on us (wraps text in a <p> tag or similar.
9873 // So, in those cases, don't bother restoring selection.
9874 r.setStart(sNode,mark.startOffset);
9875 r.setEnd(eNode,mark.endOffset);
9876 sel.addRange(r);
9877 }
9878 }
9879 }
9880 }
9881 }else{//w3c range
9882 sel = dijit.range.getSelection(this.window);
9883 if(sel && sel.removeAllRanges){
9884 sel.removeAllRanges();
9885 r = dijit.range.create(this.window);
9886 sNode = dijit.range.getNode(mark.startContainer,this.editNode);
9887 eNode = dijit.range.getNode(mark.endContainer,this.editNode);
9888 if(sNode && eNode){
9889 // Okay, we believe we found the position, so add it into the selection
9890 // There are cases where it may not be found, particularly in undo/redo, when
9891 // formatting as been done and so on, so don't restore selection then.
9892 r.setStart(sNode,mark.startOffset);
9893 r.setEnd(eNode,mark.endOffset);
9894 sel.addRange(r);
9895 }
9896 }
9897 }
9898 }
9899 },
9900 _changeToStep: function(from, to){
9901 // summary:
9902 // Reverts editor to "to" setting, from the undo stack.
9903 // tags:
9904 // private
9905 this.setValue(to.text);
9906 var b=to.bookmark;
9907 if(!b){ return; }
9908 this._moveToBookmark(b);
9909 },
9910 undo: function(){
9911 // summary:
9912 // Handler for editor undo (ex: ctrl-z) operation
9913 // tags:
9914 // private
9915 //console.log('undo');
9916 var ret = false;
9917 if(!this._undoRedoActive){
9918 this._undoRedoActive = true;
9919 this.endEditing(true);
9920 var s=this._steps.pop();
9921 if(s && this._steps.length>0){
9922 this.focus();
9923 this._changeToStep(s,this._steps[this._steps.length-1]);
9924 this._undoedSteps.push(s);
9925 this.onDisplayChanged();
9926 delete this._undoRedoActive;
9927 ret = true;
9928 }
9929 delete this._undoRedoActive;
9930 }
9931 return ret;
9932 },
9933 redo: function(){
9934 // summary:
9935 // Handler for editor redo (ex: ctrl-y) operation
9936 // tags:
9937 // private
9938 //console.log('redo');
9939 var ret = false;
9940 if(!this._undoRedoActive){
9941 this._undoRedoActive = true;
9942 this.endEditing(true);
9943 var s=this._undoedSteps.pop();
9944 if(s && this._steps.length>0){
9945 this.focus();
9946 this._changeToStep(this._steps[this._steps.length-1],s);
9947 this._steps.push(s);
9948 this.onDisplayChanged();
9949 ret = true;
9950 }
9951 delete this._undoRedoActive;
9952 }
9953 return ret;
9954 },
9955 endEditing: function(ignore_caret){
9956 // summary:
9957 // Called to note that the user has stopped typing alphanumeric characters, if it's not already noted.
9958 // Deals with saving undo; see editActionInterval parameter.
9959 // tags:
9960 // private
9961 if(this._editTimer){
9962 clearTimeout(this._editTimer);
9963 }
9964 if(this._inEditing){
9965 this._endEditing(ignore_caret);
9966 this._inEditing=false;
9967 }
9968 },
9969
9970 _getBookmark: function(){
9971 // summary:
9972 // Get the currently selected text
9973 // tags:
9974 // protected
9975 var b=dojo.withGlobal(this.window,dijit.getBookmark);
9976 var tmp=[];
9977 if(b && b.mark){
9978 var mark = b.mark;
9979 if(dojo.isIE){
9980 // Try to use the pseudo range API on IE for better accuracy.
9981 var sel = dijit.range.getSelection(this.window);
9982 if(!dojo.isArray(mark)){
9983 if(sel){
9984 var range;
9985 if(sel.rangeCount){
9986 range = sel.getRangeAt(0);
9987 }
9988 if(range){
9989 b.mark = range.cloneRange();
9990 }else{
9991 b.mark = dojo.withGlobal(this.window,dijit.getBookmark);
9992 }
9993 }
9994 }else{
9995 // Control ranges (img, table, etc), handle differently.
9996 dojo.forEach(b.mark,function(n){
9997 tmp.push(dijit.range.getIndex(n,this.editNode).o);
9998 },this);
9999 b.mark = tmp;
10000 }
10001 }
10002 try{
10003 if(b.mark && b.mark.startContainer){
10004 tmp=dijit.range.getIndex(b.mark.startContainer,this.editNode).o;
10005 b.mark={startContainer:tmp,
10006 startOffset:b.mark.startOffset,
10007 endContainer:b.mark.endContainer===b.mark.startContainer?tmp:dijit.range.getIndex(b.mark.endContainer,this.editNode).o,
10008 endOffset:b.mark.endOffset};
10009 }
10010 }catch(e){
10011 b.mark = null;
10012 }
10013 }
10014 return b;
10015 },
10016 _beginEditing: function(cmd){
10017 // summary:
10018 // Called when the user starts typing alphanumeric characters.
10019 // Deals with saving undo; see editActionInterval parameter.
10020 // tags:
10021 // private
10022 if(this._steps.length === 0){
10023 // You want to use the editor content without post filtering
10024 // to make sure selection restores right for the 'initial' state.
10025 // and undo is called. So not using this.savedContent, as it was 'processed'
10026 // and the line-up for selections may have been altered.
10027 this._steps.push({'text':dijit._editor.getChildrenHtml(this.editNode),'bookmark':this._getBookmark()});
10028 }
10029 },
10030 _endEditing: function(ignore_caret){
10031 // summary:
10032 // Called when the user stops typing alphanumeric characters.
10033 // Deals with saving undo; see editActionInterval parameter.
10034 // tags:
10035 // private
10036 // Avoid filtering to make sure selections restore.
10037 var v = dijit._editor.getChildrenHtml(this.editNode);
10038
10039 this._undoedSteps=[];//clear undoed steps
10040 this._steps.push({text: v, bookmark: this._getBookmark()});
10041 },
10042 onKeyDown: function(e){
10043 // summary:
10044 // Handler for onkeydown event.
10045 // tags:
10046 // private
10047
10048 //We need to save selection if the user TAB away from this editor
10049 //no need to call _saveSelection for IE, as that will be taken care of in onBeforeDeactivate
10050 if(!dojo.isIE && !this.iframe && e.keyCode == dojo.keys.TAB && !this.tabIndent){
10051 this._saveSelection();
10052 }
10053 if(!this.customUndo){
10054 this.inherited(arguments);
10055 return;
10056 }
10057 var k = e.keyCode, ks = dojo.keys;
10058 if(e.ctrlKey && !e.altKey){//undo and redo only if the special right Alt + z/y are not pressed #5892
10059 if(k == 90 || k == 122){ //z
10060 dojo.stopEvent(e);
10061 this.undo();
10062 return;
10063 }else if(k == 89 || k == 121){ //y
10064 dojo.stopEvent(e);
10065 this.redo();
10066 return;
10067 }
10068 }
10069 this.inherited(arguments);
10070
10071 switch(k){
10072 case ks.ENTER:
10073 case ks.BACKSPACE:
10074 case ks.DELETE:
10075 this.beginEditing();
10076 break;
10077 case 88: //x
10078 case 86: //v
10079 if(e.ctrlKey && !e.altKey && !e.metaKey){
10080 this.endEditing();//end current typing step if any
10081 if(e.keyCode == 88){
10082 this.beginEditing('cut');
10083 //use timeout to trigger after the cut is complete
10084 setTimeout(dojo.hitch(this, this.endEditing), 1);
10085 }else{
10086 this.beginEditing('paste');
10087 //use timeout to trigger after the paste is complete
10088 setTimeout(dojo.hitch(this, this.endEditing), 1);
10089 }
10090 break;
10091 }
10092 //pass through
10093 default:
10094 if(!e.ctrlKey && !e.altKey && !e.metaKey && (e.keyCode<dojo.keys.F1 || e.keyCode>dojo.keys.F15)){
10095 this.beginEditing();
10096 break;
10097 }
10098 //pass through
10099 case ks.ALT:
10100 this.endEditing();
10101 break;
10102 case ks.UP_ARROW:
10103 case ks.DOWN_ARROW:
10104 case ks.LEFT_ARROW:
10105 case ks.RIGHT_ARROW:
10106 case ks.HOME:
10107 case ks.END:
10108 case ks.PAGE_UP:
10109 case ks.PAGE_DOWN:
10110 this.endEditing(true);
10111 break;
10112 //maybe ctrl+backspace/delete, so don't endEditing when ctrl is pressed
10113 case ks.CTRL:
10114 case ks.SHIFT:
10115 case ks.TAB:
10116 break;
10117 }
10118 },
10119 _onBlur: function(){
10120 // summary:
10121 // Called from focus manager when focus has moved away from this editor
10122 // tags:
10123 // protected
10124
10125 //this._saveSelection();
10126 this.inherited('_onBlur',arguments);
10127 this.endEditing(true);
10128 },
10129 _saveSelection: function(){
10130 // summary:
10131 // Save the currently selected text in _savedSelection attribute
10132 // tags:
10133 // private
10134 this._savedSelection=this._getBookmark();
10135 //console.log('save selection',this._savedSelection,this);
10136 },
10137 _restoreSelection: function(){
10138 // summary:
10139 // Re-select the text specified in _savedSelection attribute;
10140 // see _saveSelection().
10141 // tags:
10142 // private
10143 if(this._savedSelection){
10144 // Clear off cursor to start, we're deliberately going to a selection.
10145 delete this._cursorToStart;
10146 // only restore the selection if the current range is collapsed
10147 // if not collapsed, then it means the editor does not lose
10148 // selection and there is no need to restore it
10149 if(dojo.withGlobal(this.window,'isCollapsed',dijit)){
10150 this._moveToBookmark(this._savedSelection);
10151 }
10152 delete this._savedSelection;
10153 }
10154 },
10155
10156 onClick: function(){
10157 // summary:
10158 // Handler for when editor is clicked
10159 // tags:
10160 // protected
10161 this.endEditing(true);
10162 this.inherited(arguments);
10163 },
10164
10165 _setDisabledAttr: function(/*Boolean*/ value){
10166 if(!this.disabled && value){
10167 // Disable editor: disable all enabled buttons and remember that list
10168 this._buttonEnabledPlugins = dojo.filter(this._plugins, function(p){
10169 if (p && p.button && !p.button.get("disabled")) {
10170 p.button.set("disabled", true);
10171 return true;
10172 }
10173 return false;
10174 });
10175 }else if(this.disabled && !value){
10176 // Enable editor: we only want to enable the buttons that should be
10177 // enabled (for example, the outdent button shouldn't be enabled if the current
10178 // text can't be outdented).
10179 dojo.forEach(this._buttonEnabledPlugins, function(p){
10180 p.button.attr("disabled", false);
10181 p.updateState && p.updateState(); // just in case something changed, like caret position
10182 });
10183 }
10184
10185 this.inherited(arguments);
10186 },
10187
10188 _setStateClass: function(){
10189 this.inherited(arguments);
10190
10191 // Let theme set the editor's text color based on editor enabled/disabled state.
10192 // We need to jump through hoops because the main document (where the theme CSS is)
10193 // is separate from the iframe's document.
10194 if(this.document && this.document.body){
10195 dojo.style(this.document.body, "color", dojo.style(this.iframe, "color"));
10196 }
10197 }
10198 }
10199);
10200
10201// Register the "default plugins", ie, the built-in editor commands
10202dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){
10203 if(o.plugin){ return; }
10204 var args = o.args, p;
10205 var _p = dijit._editor._Plugin;
10206 var name = args.name;
10207 switch(name){
10208 case "undo": case "redo": case "cut": case "copy": case "paste": case "insertOrderedList":
10209 case "insertUnorderedList": case "indent": case "outdent": case "justifyCenter":
10210 case "justifyFull": case "justifyLeft": case "justifyRight": case "delete":
10211 case "selectAll": case "removeFormat": case "unlink":
10212 case "insertHorizontalRule":
10213 p = new _p({ command: name });
10214 break;
10215
10216 case "bold": case "italic": case "underline": case "strikethrough":
10217 case "subscript": case "superscript":
10218 p = new _p({ buttonClass: dijit.form.ToggleButton, command: name });
10219 break;
10220 case "|":
10221 p = new _p({ button: new dijit.ToolbarSeparator(), setEditor: function(editor) {this.editor = editor;} });
10222 }
10223// console.log('name',name,p);
10224 o.plugin=p;
10225});
10226
10227}
10228
10229if(!dojo._hasResource["dojo.regexp"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
10230dojo._hasResource["dojo.regexp"] = true;
10231dojo.provide("dojo.regexp");
10232
10233/*=====
10234dojo.regexp = {
10235 // summary: Regular expressions and Builder resources
10236};
10237=====*/
10238
10239dojo.regexp.escapeString = function(/*String*/str, /*String?*/except){
10240 // summary:
10241 // Adds escape sequences for special characters in regular expressions
10242 // except:
10243 // a String with special characters to be left unescaped
10244
10245 return str.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(ch){
10246 if(except && except.indexOf(ch) != -1){
10247 return ch;
10248 }
10249 return "\\" + ch;
10250 }); // String
10251}
10252
10253dojo.regexp.buildGroupRE = function(/*Object|Array*/arr, /*Function*/re, /*Boolean?*/nonCapture){
10254 // summary:
10255 // Builds a regular expression that groups subexpressions
10256 // description:
10257 // A utility function used by some of the RE generators. The
10258 // subexpressions are constructed by the function, re, in the second
10259 // parameter. re builds one subexpression for each elem in the array
10260 // a, in the first parameter. Returns a string for a regular
10261 // expression that groups all the subexpressions.
10262 // arr:
10263 // A single value or an array of values.
10264 // re:
10265 // A function. Takes one parameter and converts it to a regular
10266 // expression.
10267 // nonCapture:
10268 // If true, uses non-capturing match, otherwise matches are retained
10269 // by regular expression. Defaults to false
10270
10271 // case 1: a is a single value.
10272 if(!(arr instanceof Array)){
10273 return re(arr); // String
10274 }
10275
10276 // case 2: a is an array
10277 var b = [];
10278 for(var i = 0; i < arr.length; i++){
10279 // convert each elem to a RE
10280 b.push(re(arr[i]));
10281 }
10282
10283 // join the REs as alternatives in a RE group.
10284 return dojo.regexp.group(b.join("|"), nonCapture); // String
10285}
10286
10287dojo.regexp.group = function(/*String*/expression, /*Boolean?*/nonCapture){
10288 // summary:
10289 // adds group match to expression
10290 // nonCapture:
10291 // If true, uses non-capturing match, otherwise matches are retained
10292 // by regular expression.
10293 return "(" + (nonCapture ? "?:":"") + expression + ")"; // String
10294}
10295
10296}
10297
10298if(!dojo._hasResource["dojo.data.util.sorter"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
10299dojo._hasResource["dojo.data.util.sorter"] = true;
10300dojo.provide("dojo.data.util.sorter");
10301
10302dojo.data.util.sorter.basicComparator = function( /*anything*/ a,
10303 /*anything*/ b){
10304 // summary:
10305 // Basic comparision function that compares if an item is greater or less than another item
10306 // description:
10307 // returns 1 if a > b, -1 if a < b, 0 if equal.
10308 // 'null' values (null, undefined) are treated as larger values so that they're pushed to the end of the list.
10309 // And compared to each other, null is equivalent to undefined.
10310
10311 //null is a problematic compare, so if null, we set to undefined.
10312 //Makes the check logic simple, compact, and consistent
10313 //And (null == undefined) === true, so the check later against null
10314 //works for undefined and is less bytes.
10315 var r = -1;
10316 if(a === null){
10317 a = undefined;
10318 }
10319 if(b === null){
10320 b = undefined;
10321 }
10322 if(a == b){
10323 r = 0;
10324 }else if(a > b || a == null){
10325 r = 1;
10326 }
10327 return r; //int {-1,0,1}
10328};
10329
10330dojo.data.util.sorter.createSortFunction = function( /* attributes array */sortSpec,
10331 /*dojo.data.core.Read*/ store){
10332 // summary:
10333 // Helper function to generate the sorting function based off the list of sort attributes.
10334 // description:
10335 // The sort function creation will look for a property on the store called 'comparatorMap'. If it exists
10336 // it will look in the mapping for comparisons function for the attributes. If one is found, it will
10337 // use it instead of the basic comparator, which is typically used for strings, ints, booleans, and dates.
10338 // Returns the sorting function for this particular list of attributes and sorting directions.
10339 //
10340 // sortSpec: array
10341 // A JS object that array that defines out what attribute names to sort on and whether it should be descenting or asending.
10342 // The objects should be formatted as follows:
10343 // {
10344 // attribute: "attributeName-string" || attribute,
10345 // descending: true|false; // Default is false.
10346 // }
10347 // store: object
10348 // The datastore object to look up item values from.
10349 //
10350 var sortFunctions=[];
10351
10352 function createSortFunction(attr, dir, comp, s){
10353 //Passing in comp and s (comparator and store), makes this
10354 //function much faster.
10355 return function(itemA, itemB){
10356 var a = s.getValue(itemA, attr);
10357 var b = s.getValue(itemB, attr);
10358 return dir * comp(a,b); //int
10359 };
10360 }
10361 var sortAttribute;
10362 var map = store.comparatorMap;
10363 var bc = dojo.data.util.sorter.basicComparator;
10364 for(var i = 0; i < sortSpec.length; i++){
10365 sortAttribute = sortSpec[i];
10366 var attr = sortAttribute.attribute;
10367 if(attr){
10368 var dir = (sortAttribute.descending) ? -1 : 1;
10369 var comp = bc;
10370 if(map){
10371 if(typeof attr !== "string" && ("toString" in attr)){
10372 attr = attr.toString();
10373 }
10374 comp = map[attr] || bc;
10375 }
10376 sortFunctions.push(createSortFunction(attr,
10377 dir, comp, store));
10378 }
10379 }
10380 return function(rowA, rowB){
10381 var i=0;
10382 while(i < sortFunctions.length){
10383 var ret = sortFunctions[i++](rowA, rowB);
10384 if(ret !== 0){
10385 return ret;//int
10386 }
10387 }
10388 return 0; //int
10389 }; // Function
10390};
10391
10392}
10393
10394if(!dojo._hasResource["dojo.data.util.simpleFetch"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
10395dojo._hasResource["dojo.data.util.simpleFetch"] = true;
10396dojo.provide("dojo.data.util.simpleFetch");
10397
10398
10399dojo.data.util.simpleFetch.fetch = function(/* Object? */ request){
10400 // summary:
10401 // The simpleFetch mixin is designed to serve as a set of function(s) that can
10402 // be mixed into other datastore implementations to accelerate their development.
10403 // The simpleFetch mixin should work well for any datastore that can respond to a _fetchItems()
10404 // call by returning an array of all the found items that matched the query. The simpleFetch mixin
10405 // is not designed to work for datastores that respond to a fetch() call by incrementally
10406 // loading items, or sequentially loading partial batches of the result
10407 // set. For datastores that mixin simpleFetch, simpleFetch
10408 // implements a fetch method that automatically handles eight of the fetch()
10409 // arguments -- onBegin, onItem, onComplete, onError, start, count, sort and scope
10410 // The class mixing in simpleFetch should not implement fetch(),
10411 // but should instead implement a _fetchItems() method. The _fetchItems()
10412 // method takes three arguments, the keywordArgs object that was passed
10413 // to fetch(), a callback function to be called when the result array is
10414 // available, and an error callback to be called if something goes wrong.
10415 // The _fetchItems() method should ignore any keywordArgs parameters for
10416 // start, count, onBegin, onItem, onComplete, onError, sort, and scope.
10417 // The _fetchItems() method needs to correctly handle any other keywordArgs
10418 // parameters, including the query parameter and any optional parameters
10419 // (such as includeChildren). The _fetchItems() method should create an array of
10420 // result items and pass it to the fetchHandler along with the original request object
10421 // -- or, the _fetchItems() method may, if it wants to, create an new request object
10422 // with other specifics about the request that are specific to the datastore and pass
10423 // that as the request object to the handler.
10424 //
10425 // For more information on this specific function, see dojo.data.api.Read.fetch()
10426 request = request || {};
10427 if(!request.store){
10428 request.store = this;
10429 }
10430 var self = this;
10431
10432 var _errorHandler = function(errorData, requestObject){
10433 if(requestObject.onError){
10434 var scope = requestObject.scope || dojo.global;
10435 requestObject.onError.call(scope, errorData, requestObject);
10436 }
10437 };
10438
10439 var _fetchHandler = function(items, requestObject){
10440 var oldAbortFunction = requestObject.abort || null;
10441 var aborted = false;
10442
10443 var startIndex = requestObject.start?requestObject.start:0;
10444 var endIndex = (requestObject.count && (requestObject.count !== Infinity))?(startIndex + requestObject.count):items.length;
10445
10446 requestObject.abort = function(){
10447 aborted = true;
10448 if(oldAbortFunction){
10449 oldAbortFunction.call(requestObject);
10450 }
10451 };
10452
10453 var scope = requestObject.scope || dojo.global;
10454 if(!requestObject.store){
10455 requestObject.store = self;
10456 }
10457 if(requestObject.onBegin){
10458 requestObject.onBegin.call(scope, items.length, requestObject);
10459 }
10460 if(requestObject.sort){
10461 items.sort(dojo.data.util.sorter.createSortFunction(requestObject.sort, self));
10462 }
10463 if(requestObject.onItem){
10464 for(var i = startIndex; (i < items.length) && (i < endIndex); ++i){
10465 var item = items[i];
10466 if(!aborted){
10467 requestObject.onItem.call(scope, item, requestObject);
10468 }
10469 }
10470 }
10471 if(requestObject.onComplete && !aborted){
10472 var subset = null;
10473 if(!requestObject.onItem){
10474 subset = items.slice(startIndex, endIndex);
10475 }
10476 requestObject.onComplete.call(scope, subset, requestObject);
10477 }
10478 };
10479 this._fetchItems(request, _fetchHandler, _errorHandler);
10480 return request; // Object
10481};
10482
10483}
10484
10485if(!dojo._hasResource["dojo.data.util.filter"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
10486dojo._hasResource["dojo.data.util.filter"] = true;
10487dojo.provide("dojo.data.util.filter");
10488
10489dojo.data.util.filter.patternToRegExp = function(/*String*/pattern, /*boolean?*/ ignoreCase){
10490 // summary:
10491 // Helper function to convert a simple pattern to a regular expression for matching.
10492 // description:
10493 // Returns a regular expression object that conforms to the defined conversion rules.
10494 // For example:
10495 // ca* -> /^ca.*$/
10496 // *ca* -> /^.*ca.*$/
10497 // *c\*a* -> /^.*c\*a.*$/
10498 // *c\*a?* -> /^.*c\*a..*$/
10499 // and so on.
10500 //
10501 // pattern: string
10502 // A simple matching pattern to convert that follows basic rules:
10503 // * Means match anything, so ca* means match anything starting with ca
10504 // ? Means match single character. So, b?b will match to bob and bab, and so on.
10505 // \ is an escape character. So for example, \* means do not treat * as a match, but literal character *.
10506 // To use a \ as a character in the string, it must be escaped. So in the pattern it should be
10507 // represented by \\ to be treated as an ordinary \ character instead of an escape.
10508 //
10509 // ignoreCase:
10510 // An optional flag to indicate if the pattern matching should be treated as case-sensitive or not when comparing
10511 // By default, it is assumed case sensitive.
10512
10513 var rxp = "^";
10514 var c = null;
10515 for(var i = 0; i < pattern.length; i++){
10516 c = pattern.charAt(i);
10517 switch(c){
10518 case '\\':
10519 rxp += c;
10520 i++;
10521 rxp += pattern.charAt(i);
10522 break;
10523 case '*':
10524 rxp += ".*"; break;
10525 case '?':
10526 rxp += "."; break;
10527 case '$':
10528 case '^':
10529 case '/':
10530 case '+':
10531 case '.':
10532 case '|':
10533 case '(':
10534 case ')':
10535 case '{':
10536 case '}':
10537 case '[':
10538 case ']':
10539 rxp += "\\"; //fallthrough
10540 default:
10541 rxp += c;
10542 }
10543 }
10544 rxp += "$";
10545 if(ignoreCase){
10546 return new RegExp(rxp,"mi"); //RegExp
10547 }else{
10548 return new RegExp(rxp,"m"); //RegExp
10549 }
10550
10551};
10552
10553}
10554
10555if(!dojo._hasResource["dijit.form.TextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
10556dojo._hasResource["dijit.form.TextBox"] = true;
10557dojo.provide("dijit.form.TextBox");
10558
10559
10560
10561dojo.declare(
10562 "dijit.form.TextBox",
10563 dijit.form._FormValueWidget,
10564 {
10565 // summary:
10566 // A base class for textbox form inputs
10567
10568 // trim: Boolean
10569 // Removes leading and trailing whitespace if true. Default is false.
10570 trim: false,
10571
10572 // uppercase: Boolean
10573 // Converts all characters to uppercase if true. Default is false.
10574 uppercase: false,
10575
10576 // lowercase: Boolean
10577 // Converts all characters to lowercase if true. Default is false.
10578 lowercase: false,
10579
10580 // propercase: Boolean
10581 // Converts the first character of each word to uppercase if true.
10582 propercase: false,
10583
10584 // maxLength: String
10585 // HTML INPUT tag maxLength declaration.
10586 maxLength: "",
10587
10588 // selectOnClick: [const] Boolean
10589 // If true, all text will be selected when focused with mouse
10590 selectOnClick: false,
10591
10592 // placeHolder: String
10593 // Defines a hint to help users fill out the input field (as defined in HTML 5).
10594 // This should only contain plain text (no html markup).
10595 placeHolder: "",
10596
10597 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"),
10598 _singleNodeTemplate: '<input class="dijit dijitReset dijitLeft dijitInputField" dojoAttachPoint="textbox,focusNode" autocomplete="off" type="${type}" ${!nameAttrSetting} />',
10599
10600 _buttonInputDisabled: dojo.isIE ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
10601
10602 baseClass: "dijitTextBox",
10603
10604 attributeMap: dojo.delegate(dijit.form._FormValueWidget.prototype.attributeMap, {
10605 maxLength: "focusNode"
10606 }),
10607
10608 postMixInProperties: function(){
10609 var type = this.type.toLowerCase();
10610 if(this.templateString.toLowerCase() == "input" || ((type == "hidden" || type == "file") && this.templateString == dijit.form.TextBox.prototype.templateString)){
10611 this.templateString = this._singleNodeTemplate;
10612 }
10613 this.inherited(arguments);
10614 },
10615
10616 _setPlaceHolderAttr: function(v){
10617 this.placeHolder = v;
10618 if(!this._phspan){
10619 this._attachPoints.push('_phspan');
10620 /* dijitInputField class gives placeHolder same padding as the input field
10621 * parent node already has dijitInputField class but it doesn't affect this <span>
10622 * since it's position: absolute.
10623 */
10624 this._phspan = dojo.create('span',{className:'dijitPlaceHolder dijitInputField'},this.textbox,'after');
10625 }
10626 this._phspan.innerHTML="";
10627 this._phspan.appendChild(document.createTextNode(v));
10628
10629 this._updatePlaceHolder();
10630 },
10631
10632 _updatePlaceHolder: function(){
10633 if(this._phspan){
10634 this._phspan.style.display=(this.placeHolder&&!this._focused&&!this.textbox.value)?"":"none";
10635 }
10636 },
10637
10638 _getValueAttr: function(){
10639 // summary:
10640 // Hook so attr('value') works as we like.
10641 // description:
10642 // For `dijit.form.TextBox` this basically returns the value of the <input>.
10643 //
10644 // For `dijit.form.MappedTextBox` subclasses, which have both
10645 // a "displayed value" and a separate "submit value",
10646 // This treats the "displayed value" as the master value, computing the
10647 // submit value from it via this.parse().
10648 return this.parse(this.get('displayedValue'), this.constraints);
10649 },
10650
10651 _setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
10652 // summary:
10653 // Hook so attr('value', ...) works.
10654 //
10655 // description:
10656 // Sets the value of the widget to "value" which can be of
10657 // any type as determined by the widget.
10658 //
10659 // value:
10660 // The visual element value is also set to a corresponding,
10661 // but not necessarily the same, value.
10662 //
10663 // formattedValue:
10664 // If specified, used to set the visual element value,
10665 // otherwise a computed visual value is used.
10666 //
10667 // priorityChange:
10668 // If true, an onChange event is fired immediately instead of
10669 // waiting for the next blur event.
10670
10671 var filteredValue;
10672 if(value !== undefined){
10673 // TODO: this is calling filter() on both the display value and the actual value.
10674 // I added a comment to the filter() definition about this, but it should be changed.
10675 filteredValue = this.filter(value);
10676 if(typeof formattedValue != "string"){
10677 if(filteredValue !== null && ((typeof filteredValue != "number") || !isNaN(filteredValue))){
10678 formattedValue = this.filter(this.format(filteredValue, this.constraints));
10679 }else{ formattedValue = ''; }
10680 }
10681 }
10682 if(formattedValue != null && formattedValue != undefined && ((typeof formattedValue) != "number" || !isNaN(formattedValue)) && this.textbox.value != formattedValue){
10683 this.textbox.value = formattedValue;
10684 }
10685
10686 this._updatePlaceHolder();
10687
10688 this.inherited(arguments, [filteredValue, priorityChange]);
10689 },
10690
10691 // displayedValue: String
10692 // For subclasses like ComboBox where the displayed value
10693 // (ex: Kentucky) and the serialized value (ex: KY) are different,
10694 // this represents the displayed value.
10695 //
10696 // Setting 'displayedValue' through attr('displayedValue', ...)
10697 // updates 'value', and vice-versa. Otherwise 'value' is updated
10698 // from 'displayedValue' periodically, like onBlur etc.
10699 //
10700 // TODO: move declaration to MappedTextBox?
10701 // Problem is that ComboBox references displayedValue,
10702 // for benefit of FilteringSelect.
10703 displayedValue: "",
10704
10705 getDisplayedValue: function(){
10706 // summary:
10707 // Deprecated. Use set('displayedValue') instead.
10708 // tags:
10709 // deprecated
10710 dojo.deprecated(this.declaredClass+"::getDisplayedValue() is deprecated. Use set('displayedValue') instead.", "", "2.0");
10711 return this.get('displayedValue');
10712 },
10713
10714 _getDisplayedValueAttr: function(){
10715 // summary:
10716 // Hook so attr('displayedValue') works.
10717 // description:
10718 // Returns the displayed value (what the user sees on the screen),
10719 // after filtering (ie, trimming spaces etc.).
10720 //
10721 // For some subclasses of TextBox (like ComboBox), the displayed value
10722 // is different from the serialized value that's actually
10723 // sent to the server (see dijit.form.ValidationTextBox.serialize)
10724
10725 return this.filter(this.textbox.value);
10726 },
10727
10728 setDisplayedValue: function(/*String*/value){
10729 // summary:
10730 // Deprecated. Use set('displayedValue', ...) instead.
10731 // tags:
10732 // deprecated
10733 dojo.deprecated(this.declaredClass+"::setDisplayedValue() is deprecated. Use set('displayedValue', ...) instead.", "", "2.0");
10734 this.set('displayedValue', value);
10735 },
10736
10737 _setDisplayedValueAttr: function(/*String*/value){
10738 // summary:
10739 // Hook so attr('displayedValue', ...) works.
10740 // description:
10741 // Sets the value of the visual element to the string "value".
10742 // The widget value is also set to a corresponding,
10743 // but not necessarily the same, value.
10744
10745 if(value === null || value === undefined){ value = '' }
10746 else if(typeof value != "string"){ value = String(value) }
10747 this.textbox.value = value;
10748 this._setValueAttr(this.get('value'), undefined, value);
10749 },
10750
10751 format: function(/* String */ value, /* Object */ constraints){
10752 // summary:
10753 // Replacable function to convert a value to a properly formatted string.
10754 // tags:
10755 // protected extension
10756 return ((value == null || value == undefined) ? "" : (value.toString ? value.toString() : value));
10757 },
10758
10759 parse: function(/* String */ value, /* Object */ constraints){
10760 // summary:
10761 // Replacable function to convert a formatted string to a value
10762 // tags:
10763 // protected extension
10764
10765 return value; // String
10766 },
10767
10768 _refreshState: function(){
10769 // summary:
10770 // After the user types some characters, etc., this method is
10771 // called to check the field for validity etc. The base method
10772 // in `dijit.form.TextBox` does nothing, but subclasses override.
10773 // tags:
10774 // protected
10775 },
10776
10777 _onInput: function(e){
10778 if(e && e.type && /key/i.test(e.type) && e.keyCode){
10779 switch(e.keyCode){
10780 case dojo.keys.SHIFT:
10781 case dojo.keys.ALT:
10782 case dojo.keys.CTRL:
10783 case dojo.keys.TAB:
10784 return;
10785 }
10786 }
10787 if(this.intermediateChanges){
10788 var _this = this;
10789 // the setTimeout allows the key to post to the widget input box
10790 setTimeout(function(){ _this._handleOnChange(_this.get('value'), false); }, 0);
10791 }
10792 this._refreshState();
10793 },
10794
10795 postCreate: function(){
10796 // setting the value here is needed since value="" in the template causes "undefined"
10797 // and setting in the DOM (instead of the JS object) helps with form reset actions
10798 if(dojo.isIE){ // IE INPUT tag fontFamily has to be set directly using STYLE
10799 var s = dojo.getComputedStyle(this.domNode);
10800 if(s){
10801 var ff = s.fontFamily;
10802 if(ff){
10803 var inputs = this.domNode.getElementsByTagName("INPUT");
10804 if(inputs){
10805 for(var i=0; i < inputs.length; i++){
10806 inputs[i].style.fontFamily = ff;
10807 }
10808 }
10809 }
10810 }
10811 }
10812 this.textbox.setAttribute("value", this.textbox.value); // DOM and JS values shuld be the same
10813 this.inherited(arguments);
10814 if(dojo.isMoz || dojo.isOpera){
10815 this.connect(this.textbox, "oninput", this._onInput);
10816 }else{
10817 this.connect(this.textbox, "onkeydown", this._onInput);
10818 this.connect(this.textbox, "onkeyup", this._onInput);
10819 this.connect(this.textbox, "onpaste", this._onInput);
10820 this.connect(this.textbox, "oncut", this._onInput);
10821 }
10822 },
10823
10824 _blankValue: '', // if the textbox is blank, what value should be reported
10825 filter: function(val){
10826 // summary:
10827 // Auto-corrections (such as trimming) that are applied to textbox
10828 // value on blur or form submit.
10829 // description:
10830 // For MappedTextBox subclasses, this is called twice
10831 // - once with the display value
10832 // - once the value as set/returned by attr('value', ...)
10833 // and attr('value'), ex: a Number for NumberTextBox.
10834 //
10835 // In the latter case it does corrections like converting null to NaN. In
10836 // the former case the NumberTextBox.filter() method calls this.inherited()
10837 // to execute standard trimming code in TextBox.filter().
10838 //
10839 // TODO: break this into two methods in 2.0
10840 //
10841 // tags:
10842 // protected extension
10843 if(val === null){ return this._blankValue; }
10844 if(typeof val != "string"){ return val; }
10845 if(this.trim){
10846 val = dojo.trim(val);
10847 }
10848 if(this.uppercase){
10849 val = val.toUpperCase();
10850 }
10851 if(this.lowercase){
10852 val = val.toLowerCase();
10853 }
10854 if(this.propercase){
10855 val = val.replace(/[^\s]+/g, function(word){
10856 return word.substring(0,1).toUpperCase() + word.substring(1);
10857 });
10858 }
10859 return val;
10860 },
10861
10862 _setBlurValue: function(){
10863 this._setValueAttr(this.get('value'), true);
10864 },
10865
10866 _onBlur: function(e){
10867 if(this.disabled){ return; }
10868 this._setBlurValue();
10869 this.inherited(arguments);
10870
10871 if(this._selectOnClickHandle){
10872 this.disconnect(this._selectOnClickHandle);
10873 }
10874 if(this.selectOnClick && dojo.isMoz){
10875 this.textbox.selectionStart = this.textbox.selectionEnd = undefined; // clear selection so that the next mouse click doesn't reselect
10876 }
10877
10878 this._updatePlaceHolder();
10879 },
10880
10881 _onFocus: function(/*String*/ by){
10882 if(this.disabled || this.readOnly){ return; }
10883
10884 // Select all text on focus via click if nothing already selected.
10885 // Since mouse-up will clear the selection need to defer selection until after mouse-up.
10886 // Don't do anything on focus by tabbing into the widget since there's no associated mouse-up event.
10887 if(this.selectOnClick && by == "mouse"){
10888 this._selectOnClickHandle = this.connect(this.domNode, "onmouseup", function(){
10889 // Only select all text on first click; otherwise users would have no way to clear
10890 // the selection.
10891 this.disconnect(this._selectOnClickHandle);
10892
10893 // Check if the user selected some text manually (mouse-down, mouse-move, mouse-up)
10894 // and if not, then select all the text
10895 var textIsNotSelected;
10896 if(dojo.isIE){
10897 var range = dojo.doc.selection.createRange();
10898 var parent = range.parentElement();
10899 textIsNotSelected = parent == this.textbox && range.text.length == 0;
10900 }else{
10901 textIsNotSelected = this.textbox.selectionStart == this.textbox.selectionEnd;
10902 }
10903 if(textIsNotSelected){
10904 dijit.selectInputText(this.textbox);
10905 }
10906 });
10907 }
10908
10909 this._updatePlaceHolder();
10910
10911 this._refreshState();
10912 this.inherited(arguments);
10913 },
10914
10915 reset: function(){
10916 // Overrides dijit._FormWidget.reset().
10917 // Additionally resets the displayed textbox value to ''
10918 this.textbox.value = '';
10919 this.inherited(arguments);
10920 }
10921 }
10922);
10923
10924dijit.selectInputText = function(/*DomNode*/element, /*Number?*/ start, /*Number?*/ stop){
10925 // summary:
10926 // Select text in the input element argument, from start (default 0), to stop (default end).
10927
10928 // TODO: use functions in _editor/selection.js?
10929 var _window = dojo.global;
10930 var _document = dojo.doc;
10931 element = dojo.byId(element);
10932 if(isNaN(start)){ start = 0; }
10933 if(isNaN(stop)){ stop = element.value ? element.value.length : 0; }
10934 dijit.focus(element);
10935 if(_document["selection"] && dojo.body()["createTextRange"]){ // IE
10936 if(element.createTextRange){
10937 var range = element.createTextRange();
10938 with(range){
10939 collapse(true);
10940 moveStart("character", -99999); // move to 0
10941 moveStart("character", start); // delta from 0 is the correct position
10942 moveEnd("character", stop-start);
10943 select();
10944 }
10945 }
10946 }else if(_window["getSelection"]){
10947 if(element.setSelectionRange){
10948 element.setSelectionRange(start, stop);
10949 }
10950 }
10951};
10952
10953}
10954
10955if(!dojo._hasResource["dijit.Tooltip"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
10956dojo._hasResource["dijit.Tooltip"] = true;
10957dojo.provide("dijit.Tooltip");
10958
10959
10960
10961
10962dojo.declare(
10963 "dijit._MasterTooltip",
10964 [dijit._Widget, dijit._Templated],
10965 {
10966 // summary:
10967 // Internal widget that holds the actual tooltip markup,
10968 // which occurs once per page.
10969 // Called by Tooltip widgets which are just containers to hold
10970 // the markup
10971 // tags:
10972 // protected
10973
10974 // duration: Integer
10975 // Milliseconds to fade in/fade out
10976 duration: dijit.defaultDuration,
10977
10978 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"),
10979
10980 postCreate: function(){
10981 dojo.body().appendChild(this.domNode);
10982
10983 this.bgIframe = new dijit.BackgroundIframe(this.domNode);
10984
10985 // Setup fade-in and fade-out functions.
10986 this.fadeIn = dojo.fadeIn({ node: this.domNode, duration: this.duration, onEnd: dojo.hitch(this, "_onShow") });
10987 this.fadeOut = dojo.fadeOut({ node: this.domNode, duration: this.duration, onEnd: dojo.hitch(this, "_onHide") });
10988
10989 },
10990
10991 show: function(/*String*/ innerHTML, /*DomNode*/ aroundNode, /*String[]?*/ position, /*Boolean*/ rtl){
10992 // summary:
10993 // Display tooltip w/specified contents to right of specified node
10994 // (To left if there's no space on the right, or if rtl == true)
10995
10996 if(this.aroundNode && this.aroundNode === aroundNode){
10997 return;
10998 }
10999
11000 if(this.fadeOut.status() == "playing"){
11001 // previous tooltip is being hidden; wait until the hide completes then show new one
11002 this._onDeck=arguments;
11003 return;
11004 }
11005 this.containerNode.innerHTML=innerHTML;
11006
11007 var pos = dijit.placeOnScreenAroundElement(this.domNode, aroundNode, dijit.getPopupAroundAlignment((position && position.length) ? position : dijit.Tooltip.defaultPosition, !rtl), dojo.hitch(this, "orient"));
11008
11009 // show it
11010 dojo.style(this.domNode, "opacity", 0);
11011 this.fadeIn.play();
11012 this.isShowingNow = true;
11013 this.aroundNode = aroundNode;
11014 },
11015
11016 orient: function(/* DomNode */ node, /* String */ aroundCorner, /* String */ tooltipCorner){
11017 // summary:
11018 // Private function to set CSS for tooltip node based on which position it's in.
11019 // This is called by the dijit popup code.
11020 // tags:
11021 // protected
11022
11023 node.className = "dijitTooltip " +
11024 {
11025 "BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
11026 "TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
11027 "BR-TR": "dijitTooltipBelow dijitTooltipABRight",
11028 "TR-BR": "dijitTooltipAbove dijitTooltipABRight",
11029 "BR-BL": "dijitTooltipRight",
11030 "BL-BR": "dijitTooltipLeft"
11031 }[aroundCorner + "-" + tooltipCorner];
11032 },
11033
11034 _onShow: function(){
11035 // summary:
11036 // Called at end of fade-in operation
11037 // tags:
11038 // protected
11039 if(dojo.isIE){
11040 // the arrow won't show up on a node w/an opacity filter
11041 this.domNode.style.filter="";
11042 }
11043 },
11044
11045 hide: function(aroundNode){
11046 // summary:
11047 // Hide the tooltip
11048 if(this._onDeck && this._onDeck[1] == aroundNode){
11049 // this hide request is for a show() that hasn't even started yet;
11050 // just cancel the pending show()
11051 this._onDeck=null;
11052 }else if(this.aroundNode === aroundNode){
11053 // this hide request is for the currently displayed tooltip
11054 this.fadeIn.stop();
11055 this.isShowingNow = false;
11056 this.aroundNode = null;
11057 this.fadeOut.play();
11058 }else{
11059 // just ignore the call, it's for a tooltip that has already been erased
11060 }
11061 },
11062
11063 _onHide: function(){
11064 // summary:
11065 // Called at end of fade-out operation
11066 // tags:
11067 // protected
11068
11069 this.domNode.style.cssText=""; // to position offscreen again
11070 this.containerNode.innerHTML="";
11071 if(this._onDeck){
11072 // a show request has been queued up; do it now
11073 this.show.apply(this, this._onDeck);
11074 this._onDeck=null;
11075 }
11076 }
11077
11078 }
11079);
11080
11081dijit.showTooltip = function(/*String*/ innerHTML, /*DomNode*/ aroundNode, /*String[]?*/ position, /*Boolean*/ rtl){
11082 // summary:
11083 // Display tooltip w/specified contents in specified position.
11084 // See description of dijit.Tooltip.defaultPosition for details on position parameter.
11085 // If position is not specified then dijit.Tooltip.defaultPosition is used.
11086 if(!dijit._masterTT){ dijit._masterTT = new dijit._MasterTooltip(); }
11087 return dijit._masterTT.show(innerHTML, aroundNode, position, rtl);
11088};
11089
11090dijit.hideTooltip = function(aroundNode){
11091 // summary:
11092 // Hide the tooltip
11093 if(!dijit._masterTT){ dijit._masterTT = new dijit._MasterTooltip(); }
11094 return dijit._masterTT.hide(aroundNode);
11095};
11096
11097dojo.declare(
11098 "dijit.Tooltip",
11099 dijit._Widget,
11100 {
11101 // summary:
11102 // Pops up a tooltip (a help message) when you hover over a node.
11103
11104 // label: String
11105 // Text to display in the tooltip.
11106 // Specified as innerHTML when creating the widget from markup.
11107 label: "",
11108
11109 // showDelay: Integer
11110 // Number of milliseconds to wait after hovering over/focusing on the object, before
11111 // the tooltip is displayed.
11112 showDelay: 400,
11113
11114 // connectId: [const] String[]
11115 // Id's of domNodes to attach the tooltip to.
11116 // When user hovers over any of the specified dom nodes, the tooltip will appear.
11117 //
11118 // Note: Currently connectId can only be specified on initialization, it cannot
11119 // be changed via attr('connectId', ...)
11120 //
11121 // Note: in 2.0 this will be renamed to connectIds for less confusion.
11122 connectId: [],
11123
11124 // position: String[]
11125 // See description of `dijit.Tooltip.defaultPosition` for details on position parameter.
11126 position: [],
11127
11128 constructor: function(){
11129 // Map id's of nodes I'm connected to to a list of the this.connect() handles
11130 this._nodeConnectionsById = {};
11131 },
11132
11133 _setConnectIdAttr: function(newIds){
11134 for(var oldId in this._nodeConnectionsById){
11135 this.removeTarget(oldId);
11136 }
11137 dojo.forEach(dojo.isArrayLike(newIds) ? newIds : [newIds], this.addTarget, this);
11138 },
11139
11140 _getConnectIdAttr: function(){
11141 var ary = [];
11142 for(var id in this._nodeConnectionsById){
11143 ary.push(id);
11144 }
11145 return ary;
11146 },
11147
11148 addTarget: function(/*DOMNODE || String*/ id){
11149 // summary:
11150 // Attach tooltip to specified node, if it's not already connected
11151 var node = dojo.byId(id);
11152 if(!node){ return; }
11153 if(node.id in this._nodeConnectionsById){ return; }//Already connected
11154
11155 this._nodeConnectionsById[node.id] = [
11156 this.connect(node, "onmouseenter", "_onTargetMouseEnter"),
11157 this.connect(node, "onmouseleave", "_onTargetMouseLeave"),
11158 this.connect(node, "onfocus", "_onTargetFocus"),
11159 this.connect(node, "onblur", "_onTargetBlur")
11160 ];
11161 },
11162
11163 removeTarget: function(/*DOMNODE || String*/ node){
11164 // summary:
11165 // Detach tooltip from specified node
11166
11167 // map from DOMNode back to plain id string
11168 var id = node.id || node;
11169
11170 if(id in this._nodeConnectionsById){
11171 dojo.forEach(this._nodeConnectionsById[id], this.disconnect, this);
11172 delete this._nodeConnectionsById[id];
11173 }
11174 },
11175
11176 postCreate: function(){
11177 dojo.addClass(this.domNode,"dijitTooltipData");
11178 },
11179
11180 startup: function(){
11181 this.inherited(arguments);
11182
11183 // If this tooltip was created in a template, or for some other reason the specified connectId[s]
11184 // didn't exist during the widget's initialization, then connect now.
11185 var ids = this.connectId;
11186 dojo.forEach(dojo.isArrayLike(ids) ? ids : [ids], this.addTarget, this);
11187 },
11188
11189 _onTargetMouseEnter: function(/*Event*/ e){
11190 // summary:
11191 // Handler for mouseenter event on the target node
11192 // tags:
11193 // private
11194 this._onHover(e);
11195 },
11196
11197 _onTargetMouseLeave: function(/*Event*/ e){
11198 // summary:
11199 // Handler for mouseleave event on the target node
11200 // tags:
11201 // private
11202 this._onUnHover(e);
11203 },
11204
11205 _onTargetFocus: function(/*Event*/ e){
11206 // summary:
11207 // Handler for focus event on the target node
11208 // tags:
11209 // private
11210
11211 this._focus = true;
11212 this._onHover(e);
11213 },
11214
11215 _onTargetBlur: function(/*Event*/ e){
11216 // summary:
11217 // Handler for blur event on the target node
11218 // tags:
11219 // private
11220
11221 this._focus = false;
11222 this._onUnHover(e);
11223 },
11224
11225 _onHover: function(/*Event*/ e){
11226 // summary:
11227 // Despite the name of this method, it actually handles both hover and focus
11228 // events on the target node, setting a timer to show the tooltip.
11229 // tags:
11230 // private
11231 if(!this._showTimer){
11232 var target = e.target;
11233 this._showTimer = setTimeout(dojo.hitch(this, function(){this.open(target)}), this.showDelay);
11234 }
11235 },
11236
11237 _onUnHover: function(/*Event*/ e){
11238 // summary:
11239 // Despite the name of this method, it actually handles both mouseleave and blur
11240 // events on the target node, hiding the tooltip.
11241 // tags:
11242 // private
11243
11244 // keep a tooltip open if the associated element still has focus (even though the
11245 // mouse moved away)
11246 if(this._focus){ return; }
11247
11248 if(this._showTimer){
11249 clearTimeout(this._showTimer);
11250 delete this._showTimer;
11251 }
11252 this.close();
11253 },
11254
11255 open: function(/*DomNode*/ target){
11256 // summary:
11257 // Display the tooltip; usually not called directly.
11258 // tags:
11259 // private
11260
11261 if(this._showTimer){
11262 clearTimeout(this._showTimer);
11263 delete this._showTimer;
11264 }
11265 dijit.showTooltip(this.label || this.domNode.innerHTML, target, this.position, !this.isLeftToRight());
11266
11267 this._connectNode = target;
11268 this.onShow(target, this.position);
11269 },
11270
11271 close: function(){
11272 // summary:
11273 // Hide the tooltip or cancel timer for show of tooltip
11274 // tags:
11275 // private
11276
11277 if(this._connectNode){
11278 // if tooltip is currently shown
11279 dijit.hideTooltip(this._connectNode);
11280 delete this._connectNode;
11281 this.onHide();
11282 }
11283 if(this._showTimer){
11284 // if tooltip is scheduled to be shown (after a brief delay)
11285 clearTimeout(this._showTimer);
11286 delete this._showTimer;
11287 }
11288 },
11289
11290 onShow: function(target, position){
11291 // summary:
11292 // Called when the tooltip is shown
11293 // tags:
11294 // callback
11295 },
11296
11297 onHide: function(){
11298 // summary:
11299 // Called when the tooltip is hidden
11300 // tags:
11301 // callback
11302 },
11303
11304 uninitialize: function(){
11305 this.close();
11306 this.inherited(arguments);
11307 }
11308 }
11309);
11310
11311// dijit.Tooltip.defaultPosition: String[]
11312// This variable controls the position of tooltips, if the position is not specified to
11313// the Tooltip widget or *TextBox widget itself. It's an array of strings with the following values:
11314//
11315// * before: places tooltip to the left of the target node/widget, or to the right in
11316// the case of RTL scripts like Hebrew and Arabic
11317// * after: places tooltip to the right of the target node/widget, or to the left in
11318// the case of RTL scripts like Hebrew and Arabic
11319// * above: tooltip goes above target node
11320// * below: tooltip goes below target node
11321//
11322// The list is positions is tried, in order, until a position is found where the tooltip fits
11323// within the viewport.
11324//
11325// Be careful setting this parameter. A value of "above" may work fine until the user scrolls
11326// the screen so that there's no room above the target node. Nodes with drop downs, like
11327// DropDownButton or FilteringSelect, are especially problematic, in that you need to be sure
11328// that the drop down and tooltip don't overlap, even when the viewport is scrolled so that there
11329// is only room below (or above) the target node, but not both.
11330dijit.Tooltip.defaultPosition = ["after", "before"];
11331
11332}
11333
11334if(!dojo._hasResource["dijit.form.ValidationTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11335dojo._hasResource["dijit.form.ValidationTextBox"] = true;
11336dojo.provide("dijit.form.ValidationTextBox");
11337
11338
11339
11340
11341
11342
11343
11344
11345/*=====
11346 dijit.form.ValidationTextBox.__Constraints = function(){
11347 // locale: String
11348 // locale used for validation, picks up value from this widget's lang attribute
11349 // _flags_: anything
11350 // various flags passed to regExpGen function
11351 this.locale = "";
11352 this._flags_ = "";
11353 }
11354=====*/
11355
11356dojo.declare(
11357 "dijit.form.ValidationTextBox",
11358 dijit.form.TextBox,
11359 {
11360 // summary:
11361 // Base class for textbox widgets with the ability to validate content of various types and provide user feedback.
11362 // tags:
11363 // protected
11364
11365 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=\"&Chi; \" 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"),
11366 baseClass: "dijitTextBox dijitValidationTextBox",
11367
11368 // required: Boolean
11369 // User is required to enter data into this field.
11370 required: false,
11371
11372 // promptMessage: String
11373 // If defined, display this hint string immediately on focus to the textbox, if empty.
11374 // Think of this like a tooltip that tells the user what to do, not an error message
11375 // that tells the user what they've done wrong.
11376 //
11377 // Message disappears when user starts typing.
11378 promptMessage: "",
11379
11380 // invalidMessage: String
11381 // The message to display if value is invalid.
11382 // The translated string value is read from the message file by default.
11383 // Set to "" to use the promptMessage instead.
11384 invalidMessage: "$_unset_$",
11385
11386 // missingMessage: String
11387 // The message to display if value is empty and the field is required.
11388 // The translated string value is read from the message file by default.
11389 // Set to "" to use the invalidMessage instead.
11390 missingMessage: "$_unset_$",
11391
11392 // constraints: dijit.form.ValidationTextBox.__Constraints
11393 // user-defined object needed to pass parameters to the validator functions
11394 constraints: {},
11395
11396 // regExp: [extension protected] String
11397 // regular expression string used to validate the input
11398 // Do not specify both regExp and regExpGen
11399 regExp: ".*",
11400
11401 regExpGen: function(/*dijit.form.ValidationTextBox.__Constraints*/constraints){
11402 // summary:
11403 // Overridable function used to generate regExp when dependent on constraints.
11404 // Do not specify both regExp and regExpGen.
11405 // tags:
11406 // extension protected
11407 return this.regExp; // String
11408 },
11409
11410 // state: [readonly] String
11411 // Shows current state (ie, validation result) of input (Normal, Warning, or Error)
11412 state: "",
11413
11414 // tooltipPosition: String[]
11415 // See description of `dijit.Tooltip.defaultPosition` for details on this parameter.
11416 tooltipPosition: [],
11417
11418 _setValueAttr: function(){
11419 // summary:
11420 // Hook so attr('value', ...) works.
11421 this.inherited(arguments);
11422 this.validate(this._focused);
11423 },
11424
11425 validator: function(/*anything*/value, /*dijit.form.ValidationTextBox.__Constraints*/constraints){
11426 // summary:
11427 // Overridable function used to validate the text input against the regular expression.
11428 // tags:
11429 // protected
11430 return (new RegExp("^(?:" + this.regExpGen(constraints) + ")"+(this.required?"":"?")+"$")).test(value) &&
11431 (!this.required || !this._isEmpty(value)) &&
11432 (this._isEmpty(value) || this.parse(value, constraints) !== undefined); // Boolean
11433 },
11434
11435 _isValidSubset: function(){
11436 // summary:
11437 // Returns true if the value is either already valid or could be made valid by appending characters.
11438 // This is used for validation while the user [may be] still typing.
11439 return this.textbox.value.search(this._partialre) == 0;
11440 },
11441
11442 isValid: function(/*Boolean*/ isFocused){
11443 // summary:
11444 // Tests if value is valid.
11445 // Can override with your own routine in a subclass.
11446 // tags:
11447 // protected
11448 return this.validator(this.textbox.value, this.constraints);
11449 },
11450
11451 _isEmpty: function(value){
11452 // summary:
11453 // Checks for whitespace
11454 return /^\s*$/.test(value); // Boolean
11455 },
11456
11457 getErrorMessage: function(/*Boolean*/ isFocused){
11458 // summary:
11459 // Return an error message to show if appropriate
11460 // tags:
11461 // protected
11462 return (this.required && this._isEmpty(this.textbox.value)) ? this.missingMessage : this.invalidMessage; // String
11463 },
11464
11465 getPromptMessage: function(/*Boolean*/ isFocused){
11466 // summary:
11467 // Return a hint message to show when widget is first focused
11468 // tags:
11469 // protected
11470 return this.promptMessage; // String
11471 },
11472
11473 _maskValidSubsetError: true,
11474 validate: function(/*Boolean*/ isFocused){
11475 // summary:
11476 // Called by oninit, onblur, and onkeypress.
11477 // description:
11478 // Show missing or invalid messages if appropriate, and highlight textbox field.
11479 // tags:
11480 // protected
11481 var message = "";
11482 var isValid = this.disabled || this.isValid(isFocused);
11483 if(isValid){ this._maskValidSubsetError = true; }
11484 var isEmpty = this._isEmpty(this.textbox.value);
11485 var isValidSubset = !isValid && !isEmpty && isFocused && this._isValidSubset();
11486 this.state = ((isValid || ((!this._hasBeenBlurred || isFocused) && isEmpty) || isValidSubset) && this._maskValidSubsetError) ? "" : "Error";
11487 if(this.state == "Error"){ this._maskValidSubsetError = isFocused; } // we want the error to show up afer a blur and refocus
11488 this._setStateClass();
11489 dijit.setWaiState(this.focusNode, "invalid", isValid ? "false" : "true");
11490 if(isFocused){
11491 if(this.state == "Error"){
11492 message = this.getErrorMessage(true);
11493 }else{
11494 message = this.getPromptMessage(true); // show the prompt whever there's no error
11495 }
11496 this._maskValidSubsetError = true; // since we're focused, always mask warnings
11497 }
11498 this.displayMessage(message);
11499 return isValid;
11500 },
11501
11502 // _message: String
11503 // Currently displayed message
11504 _message: "",
11505
11506 displayMessage: function(/*String*/ message){
11507 // summary:
11508 // Overridable method to display validation errors/hints.
11509 // By default uses a tooltip.
11510 // tags:
11511 // extension
11512 if(this._message == message){ return; }
11513 this._message = message;
11514 dijit.hideTooltip(this.domNode);
11515 if(message){
11516 dijit.showTooltip(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
11517 }
11518 },
11519
11520 _refreshState: function(){
11521 // Overrides TextBox._refreshState()
11522 this.validate(this._focused);
11523 this.inherited(arguments);
11524 },
11525
11526 //////////// INITIALIZATION METHODS ///////////////////////////////////////
11527
11528 constructor: function(){
11529 this.constraints = {};
11530 },
11531
11532 _setConstraintsAttr: function(/* Object */ constraints){
11533 if(!constraints.locale && this.lang){
11534 constraints.locale = this.lang;
11535 }
11536 this.constraints = constraints;
11537 this._computePartialRE();
11538 },
11539
11540 _computePartialRE: function(){
11541 var p = this.regExpGen(this.constraints);
11542 this.regExp = p;
11543 var partialre = "";
11544 // parse the regexp and produce a new regexp that matches valid subsets
11545 // if the regexp is .* then there's no use in matching subsets since everything is valid
11546 if(p != ".*"){ this.regExp.replace(/\\.|\[\]|\[.*?[^\\]{1}\]|\{.*?\}|\(\?[=:!]|./g,
11547 function (re){
11548 switch(re.charAt(0)){
11549 case '{':
11550 case '+':
11551 case '?':
11552 case '*':
11553 case '^':
11554 case '$':
11555 case '|':
11556 case '(':
11557 partialre += re;
11558 break;
11559 case ")":
11560 partialre += "|$)";
11561 break;
11562 default:
11563 partialre += "(?:"+re+"|$)";
11564 break;
11565 }
11566 }
11567 );}
11568 try{ // this is needed for now since the above regexp parsing needs more test verification
11569 "".search(partialre);
11570 }catch(e){ // should never be here unless the original RE is bad or the parsing is bad
11571 partialre = this.regExp;
11572 console.warn('RegExp error in ' + this.declaredClass + ': ' + this.regExp);
11573 } // should never be here unless the original RE is bad or the parsing is bad
11574 this._partialre = "^(?:" + partialre + ")$";
11575 },
11576
11577 postMixInProperties: function(){
11578 this.inherited(arguments);
11579 this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
11580 if(this.invalidMessage == "$_unset_$"){ this.invalidMessage = this.messages.invalidMessage; }
11581 if(!this.invalidMessage){ this.invalidMessage = this.promptMessage; }
11582 if(this.missingMessage == "$_unset_$"){ this.missingMessage = this.messages.missingMessage; }
11583 if(!this.missingMessage){ this.missingMessage = this.invalidMessage; }
11584 this._setConstraintsAttr(this.constraints); // this needs to happen now (and later) due to codependency on _set*Attr calls attachPoints
11585 },
11586
11587 _setDisabledAttr: function(/*Boolean*/ value){
11588 this.inherited(arguments); // call FormValueWidget._setDisabledAttr()
11589 this._refreshState();
11590 },
11591
11592 _setRequiredAttr: function(/*Boolean*/ value){
11593 this.required = value;
11594 dijit.setWaiState(this.focusNode, "required", value);
11595 this._refreshState();
11596 },
11597
11598 reset:function(){
11599 // Overrides dijit.form.TextBox.reset() by also
11600 // hiding errors about partial matches
11601 this._maskValidSubsetError = true;
11602 this.inherited(arguments);
11603 },
11604
11605 _onBlur: function(){
11606 this.displayMessage('');
11607 this.inherited(arguments);
11608 }
11609 }
11610);
11611
11612dojo.declare(
11613 "dijit.form.MappedTextBox",
11614 dijit.form.ValidationTextBox,
11615 {
11616 // summary:
11617 // A dijit.form.ValidationTextBox subclass which provides a base class for widgets that have
11618 // a visible formatted display value, and a serializable
11619 // value in a hidden input field which is actually sent to the server.
11620 // description:
11621 // The visible display may
11622 // be locale-dependent and interactive. The value sent to the server is stored in a hidden
11623 // input field which uses the `name` attribute declared by the original widget. That value sent
11624 // to the server is defined by the dijit.form.MappedTextBox.serialize method and is typically
11625 // locale-neutral.
11626 // tags:
11627 // protected
11628
11629 postMixInProperties: function(){
11630 this.inherited(arguments);
11631
11632 // we want the name attribute to go to the hidden <input>, not the displayed <input>,
11633 // so override _FormWidget.postMixInProperties() setting of nameAttrSetting
11634 this.nameAttrSetting = "";
11635 },
11636
11637 serialize: function(/*anything*/val, /*Object?*/options){
11638 // summary:
11639 // Overridable function used to convert the attr('value') result to a canonical
11640 // (non-localized) string. For example, will print dates in ISO format, and
11641 // numbers the same way as they are represented in javascript.
11642 // tags:
11643 // protected extension
11644 return val.toString ? val.toString() : ""; // String
11645 },
11646
11647 toString: function(){
11648 // summary:
11649 // Returns widget as a printable string using the widget's value
11650 // tags:
11651 // protected
11652 var val = this.filter(this.get('value')); // call filter in case value is nonstring and filter has been customized
11653 return val != null ? (typeof val == "string" ? val : this.serialize(val, this.constraints)) : ""; // String
11654 },
11655
11656 validate: function(){
11657 // Overrides `dijit.form.TextBox.validate`
11658 this.valueNode.value = this.toString();
11659 return this.inherited(arguments);
11660 },
11661
11662 buildRendering: function(){
11663 // Overrides `dijit._Templated.buildRendering`
11664
11665 this.inherited(arguments);
11666
11667 // Create a hidden <input> node with the serialized value used for submit
11668 // (as opposed to the displayed value).
11669 // Passing in name as markup rather than calling dojo.create() with an attrs argument
11670 // to make dojo.query(input[name=...]) work on IE. (see #8660)
11671 this.valueNode = dojo.place("<input type='hidden'" + (this.name ? " name='" + this.name + "'" : "") + ">", this.textbox, "after");
11672 },
11673
11674 reset:function(){
11675 // Overrides `dijit.form.ValidationTextBox.reset` to
11676 // reset the hidden textbox value to ''
11677 this.valueNode.value = '';
11678 this.inherited(arguments);
11679 }
11680 }
11681);
11682
11683/*=====
11684 dijit.form.RangeBoundTextBox.__Constraints = function(){
11685 // min: Number
11686 // Minimum signed value. Default is -Infinity
11687 // max: Number
11688 // Maximum signed value. Default is +Infinity
11689 this.min = min;
11690 this.max = max;
11691 }
11692=====*/
11693
11694dojo.declare(
11695 "dijit.form.RangeBoundTextBox",
11696 dijit.form.MappedTextBox,
11697 {
11698 // summary:
11699 // Base class for textbox form widgets which defines a range of valid values.
11700
11701 // rangeMessage: String
11702 // The message to display if value is out-of-range
11703 rangeMessage: "",
11704
11705 /*=====
11706 // constraints: dijit.form.RangeBoundTextBox.__Constraints
11707 constraints: {},
11708 ======*/
11709
11710 rangeCheck: function(/*Number*/ primitive, /*dijit.form.RangeBoundTextBox.__Constraints*/ constraints){
11711 // summary:
11712 // Overridable function used to validate the range of the numeric input value.
11713 // tags:
11714 // protected
11715 return ("min" in constraints? (this.compare(primitive,constraints.min) >= 0) : true) &&
11716 ("max" in constraints? (this.compare(primitive,constraints.max) <= 0) : true); // Boolean
11717 },
11718
11719 isInRange: function(/*Boolean*/ isFocused){
11720 // summary:
11721 // Tests if the value is in the min/max range specified in constraints
11722 // tags:
11723 // protected
11724 return this.rangeCheck(this.get('value'), this.constraints);
11725 },
11726
11727 _isDefinitelyOutOfRange: function(){
11728 // summary:
11729 // Returns true if the value is out of range and will remain
11730 // out of range even if the user types more characters
11731 var val = this.get('value');
11732 var isTooLittle = false;
11733 var isTooMuch = false;
11734 if("min" in this.constraints){
11735 var min = this.constraints.min;
11736 min = this.compare(val, ((typeof min == "number") && min >= 0 && val !=0) ? 0 : min);
11737 isTooLittle = (typeof min == "number") && min < 0;
11738 }
11739 if("max" in this.constraints){
11740 var max = this.constraints.max;
11741 max = this.compare(val, ((typeof max != "number") || max > 0) ? max : 0);
11742 isTooMuch = (typeof max == "number") && max > 0;
11743 }
11744 return isTooLittle || isTooMuch;
11745 },
11746
11747 _isValidSubset: function(){
11748 // summary:
11749 // Overrides `dijit.form.ValidationTextBox._isValidSubset`.
11750 // Returns true if the input is syntactically valid, and either within
11751 // range or could be made in range by more typing.
11752 return this.inherited(arguments) && !this._isDefinitelyOutOfRange();
11753 },
11754
11755 isValid: function(/*Boolean*/ isFocused){
11756 // Overrides dijit.form.ValidationTextBox.isValid to check that the value is also in range.
11757 return this.inherited(arguments) &&
11758 ((this._isEmpty(this.textbox.value) && !this.required) || this.isInRange(isFocused)); // Boolean
11759 },
11760
11761 getErrorMessage: function(/*Boolean*/ isFocused){
11762 // Overrides dijit.form.ValidationTextBox.getErrorMessage to print "out of range" message if appropriate
11763 var v = this.get('value');
11764 if(v !== null && v !== '' && v !== undefined && (typeof v != "number" || !isNaN(v)) && !this.isInRange(isFocused)){ // don't check isInRange w/o a real value
11765 return this.rangeMessage; // String
11766 }
11767 return this.inherited(arguments);
11768 },
11769
11770 postMixInProperties: function(){
11771 this.inherited(arguments);
11772 if(!this.rangeMessage){
11773 this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
11774 this.rangeMessage = this.messages.rangeMessage;
11775 }
11776 },
11777
11778 _setConstraintsAttr: function(/* Object */ constraints){
11779 this.inherited(arguments);
11780 if(this.focusNode){ // not set when called from postMixInProperties
11781 if(this.constraints.min !== undefined){
11782 dijit.setWaiState(this.focusNode, "valuemin", this.constraints.min);
11783 }else{
11784 dijit.removeWaiState(this.focusNode, "valuemin");
11785 }
11786 if(this.constraints.max !== undefined){
11787 dijit.setWaiState(this.focusNode, "valuemax", this.constraints.max);
11788 }else{
11789 dijit.removeWaiState(this.focusNode, "valuemax");
11790 }
11791 }
11792 },
11793
11794 _setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange){
11795 // summary:
11796 // Hook so attr('value', ...) works.
11797
11798 dijit.setWaiState(this.focusNode, "valuenow", value);
11799 this.inherited(arguments);
11800 }
11801 }
11802);
11803
11804}
11805
11806if(!dojo._hasResource["dijit.form.ComboBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11807dojo._hasResource["dijit.form.ComboBox"] = true;
11808dojo.provide("dijit.form.ComboBox");
11809
11810
11811
11812
11813
11814
11815
11816
11817
11818
11819
11820
11821dojo.declare(
11822 "dijit.form.ComboBoxMixin",
11823 null,
11824 {
11825 // summary:
11826 // Implements the base functionality for `dijit.form.ComboBox`/`dijit.form.FilteringSelect`
11827 // description:
11828 // All widgets that mix in dijit.form.ComboBoxMixin must extend `dijit.form._FormValueWidget`.
11829 // tags:
11830 // protected
11831
11832 // item: Object
11833 // This is the item returned by the dojo.data.store implementation that
11834 // provides the data for this ComboBox, it's the currently selected item.
11835 item: null,
11836
11837 // pageSize: Integer
11838 // Argument to data provider.
11839 // Specifies number of search results per page (before hitting "next" button)
11840 pageSize: Infinity,
11841
11842 // store: Object
11843 // Reference to data provider object used by this ComboBox
11844 store: null,
11845
11846 // fetchProperties: Object
11847 // Mixin to the dojo.data store's fetch.
11848 // For example, to set the sort order of the ComboBox menu, pass:
11849 // | { sort: {attribute:"name",descending: true} }
11850 // To override the default queryOptions so that deep=false, do:
11851 // | { queryOptions: {ignoreCase: true, deep: false} }
11852 fetchProperties:{},
11853
11854 // query: Object
11855 // A query that can be passed to 'store' to initially filter the items,
11856 // before doing further filtering based on `searchAttr` and the key.
11857 // Any reference to the `searchAttr` is ignored.
11858 query: {},
11859
11860 // autoComplete: Boolean
11861 // If user types in a partial string, and then tab out of the `<input>` box,
11862 // automatically copy the first entry displayed in the drop down list to
11863 // the `<input>` field
11864 autoComplete: true,
11865
11866 // highlightMatch: String
11867 // One of: "first", "all" or "none".
11868 //
11869 // If the ComboBox/FilteringSelect opens with the search results and the searched
11870 // string can be found, it will be highlighted. If set to "all"
11871 // then will probably want to change `queryExpr` parameter to '*${0}*'
11872 //
11873 // Highlighting is only performed when `labelType` is "text", so as to not
11874 // interfere with any HTML markup an HTML label might contain.
11875 highlightMatch: "first",
11876
11877 // searchDelay: Integer
11878 // Delay in milliseconds between when user types something and we start
11879 // searching based on that value
11880 searchDelay: 100,
11881
11882 // searchAttr: String
11883 // Search for items in the data store where this attribute (in the item)
11884 // matches what the user typed
11885 searchAttr: "name",
11886
11887 // labelAttr: String?
11888 // The entries in the drop down list come from this attribute in the
11889 // dojo.data items.
11890 // If not specified, the searchAttr attribute is used instead.
11891 labelAttr: "",
11892
11893 // labelType: String
11894 // Specifies how to interpret the labelAttr in the data store items.
11895 // Can be "html" or "text".
11896 labelType: "text",
11897
11898 // queryExpr: String
11899 // This specifies what query ComboBox/FilteringSelect sends to the data store,
11900 // based on what the user has typed. Changing this expression will modify
11901 // whether the drop down shows only exact matches, a "starting with" match,
11902 // etc. Use it in conjunction with highlightMatch.
11903 // dojo.data query expression pattern.
11904 // `${0}` will be substituted for the user text.
11905 // `*` is used for wildcards.
11906 // `${0}*` means "starts with", `*${0}*` means "contains", `${0}` means "is"
11907 queryExpr: "${0}*",
11908
11909 // ignoreCase: Boolean
11910 // Set true if the ComboBox/FilteringSelect should ignore case when matching possible items
11911 ignoreCase: true,
11912
11913 // hasDownArrow: [const] Boolean
11914 // Set this textbox to have a down arrow button, to display the drop down list.
11915 // Defaults to true.
11916 hasDownArrow: true,
11917
11918 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=\"&#9660; \" 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=\"&Chi; \" 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"),
11919
11920 baseClass: "dijitTextBox dijitComboBox",
11921
11922 // Set classes like dijitDownArrowButtonHover depending on
11923 // mouse action over button node
11924 cssStateNodes: {
11925 "downArrowNode": "dijitDownArrowButton"
11926 },
11927
11928 _getCaretPos: function(/*DomNode*/ element){
11929 // khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22
11930 var pos = 0;
11931 if(typeof(element.selectionStart) == "number"){
11932 // FIXME: this is totally borked on Moz < 1.3. Any recourse?
11933 pos = element.selectionStart;
11934 }else if(dojo.isIE){
11935 // in the case of a mouse click in a popup being handled,
11936 // then the dojo.doc.selection is not the textarea, but the popup
11937 // var r = dojo.doc.selection.createRange();
11938 // hack to get IE 6 to play nice. What a POS browser.
11939 var tr = dojo.doc.selection.createRange().duplicate();
11940 var ntr = element.createTextRange();
11941 tr.move("character",0);
11942 ntr.move("character",0);
11943 try{
11944 // If control doesnt have focus, you get an exception.
11945 // Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes).
11946 // There appears to be no workaround for this - googled for quite a while.
11947 ntr.setEndPoint("EndToEnd", tr);
11948 pos = String(ntr.text).replace(/\r/g,"").length;
11949 }catch(e){
11950 // If focus has shifted, 0 is fine for caret pos.
11951 }
11952 }
11953 return pos;
11954 },
11955
11956 _setCaretPos: function(/*DomNode*/ element, /*Number*/ location){
11957 location = parseInt(location);
11958 dijit.selectInputText(element, location, location);
11959 },
11960
11961 _setDisabledAttr: function(/*Boolean*/ value){
11962 // Additional code to set disabled state of ComboBox node.
11963 // Overrides _FormValueWidget._setDisabledAttr() or ValidationTextBox._setDisabledAttr().
11964 this.inherited(arguments);
11965 dijit.setWaiState(this.comboNode, "disabled", value);
11966 },
11967
11968 _abortQuery: function(){
11969 // stop in-progress query
11970 if(this.searchTimer){
11971 clearTimeout(this.searchTimer);
11972 this.searchTimer = null;
11973 }
11974 if(this._fetchHandle){
11975 if(this._fetchHandle.abort){ this._fetchHandle.abort(); }
11976 this._fetchHandle = null;
11977 }
11978 },
11979
11980 _onInput: function(/*Event*/ evt){
11981 // summary:
11982 // Handles paste events
11983 if(!this.searchTimer && (evt.type == 'paste'/*IE|WebKit*/ || evt.type == 'input'/*Firefox*/) && this._lastInput != this.textbox.value){
11984 this.searchTimer = setTimeout(dojo.hitch(this, function(){
11985 this._onKeyPress({charOrCode: 229}); // fake IME key to cause a search
11986 }), 100); // long delay that will probably be preempted by keyboard input
11987 }
11988 this.inherited(arguments);
11989 },
11990
11991 _onKeyPress: function(/*Event*/ evt){
11992 // summary:
11993 // Handles keyboard events
11994 var key = evt.charOrCode;
11995 // except for cutting/pasting case - ctrl + x/v
11996 if(evt.altKey || ((evt.ctrlKey || evt.metaKey) && (key != 'x' && key != 'v')) || key == dojo.keys.SHIFT){
11997 return; // throw out weird key combinations and spurious events
11998 }
11999 var doSearch = false;
12000 var searchFunction = "_startSearchFromInput";
12001 var pw = this._popupWidget;
12002 var dk = dojo.keys;
12003 var highlighted = null;
12004 this._prev_key_backspace = false;
12005 this._abortQuery();
12006 if(this._isShowingNow){
12007 pw.handleKey(key);
12008 highlighted = pw.getHighlightedOption();
12009 }
12010 switch(key){
12011 case dk.PAGE_DOWN:
12012 case dk.DOWN_ARROW:
12013 case dk.PAGE_UP:
12014 case dk.UP_ARROW:
12015 if(!this._isShowingNow){
12016 doSearch = true;
12017 searchFunction = "_startSearchAll";
12018 }else{
12019 this._announceOption(highlighted);
12020 }
12021 dojo.stopEvent(evt);
12022 break;
12023
12024 case dk.ENTER:
12025 // prevent submitting form if user presses enter. Also
12026 // prevent accepting the value if either Next or Previous
12027 // are selected
12028 if(highlighted){
12029 // only stop event on prev/next
12030 if(highlighted == pw.nextButton){
12031 this._nextSearch(1);
12032 dojo.stopEvent(evt);
12033 break;
12034 }else if(highlighted == pw.previousButton){
12035 this._nextSearch(-1);
12036 dojo.stopEvent(evt);
12037 break;
12038 }
12039 }else{
12040 // Update 'value' (ex: KY) according to currently displayed text
12041 this._setBlurValue(); // set value if needed
12042 this._setCaretPos(this.focusNode, this.focusNode.value.length); // move cursor to end and cancel highlighting
12043 }
12044 // default case:
12045 // prevent submit, but allow event to bubble
12046 evt.preventDefault();
12047 // fall through
12048
12049 case dk.TAB:
12050 var newvalue = this.get('displayedValue');
12051 // if the user had More Choices selected fall into the
12052 // _onBlur handler
12053 if(pw && (
12054 newvalue == pw._messages["previousMessage"] ||
12055 newvalue == pw._messages["nextMessage"])
12056 ){
12057 break;
12058 }
12059 if(highlighted){
12060 this._selectOption();
12061 }
12062 if(this._isShowingNow){
12063 this._lastQuery = null; // in case results come back later
12064 this._hideResultList();
12065 }
12066 break;
12067
12068 case ' ':
12069 if(highlighted){
12070 dojo.stopEvent(evt);
12071 this._selectOption();
12072 this._hideResultList();
12073 }else{
12074 doSearch = true;
12075 }
12076 break;
12077
12078 case dk.ESCAPE:
12079 if(this._isShowingNow){
12080 dojo.stopEvent(evt);
12081 this._hideResultList();
12082 }
12083 break;
12084
12085 case dk.DELETE:
12086 case dk.BACKSPACE:
12087 this._prev_key_backspace = true;
12088 doSearch = true;
12089 break;
12090
12091 default:
12092 // Non char keys (F1-F12 etc..) shouldn't open list.
12093 // Ascii characters and IME input (Chinese, Japanese etc.) should.
12094 // On IE and safari, IME input produces keycode == 229, and we simulate
12095 // it on firefox by attaching to compositionend event (see compositionend method)
12096 doSearch = typeof key == 'string' || key == 229;
12097 }
12098 if(doSearch){
12099 // need to wait a tad before start search so that the event
12100 // bubbles through DOM and we have value visible
12101 this.item = undefined; // undefined means item needs to be set
12102 this.searchTimer = setTimeout(dojo.hitch(this, searchFunction),1);
12103 }
12104 },
12105
12106 _autoCompleteText: function(/*String*/ text){
12107 // summary:
12108 // Fill in the textbox with the first item from the drop down
12109 // list, and highlight the characters that were
12110 // auto-completed. For example, if user typed "CA" and the
12111 // drop down list appeared, the textbox would be changed to
12112 // "California" and "ifornia" would be highlighted.
12113
12114 var fn = this.focusNode;
12115
12116 // IE7: clear selection so next highlight works all the time
12117 dijit.selectInputText(fn, fn.value.length);
12118 // does text autoComplete the value in the textbox?
12119 var caseFilter = this.ignoreCase? 'toLowerCase' : 'substr';
12120 if(text[caseFilter](0).indexOf(this.focusNode.value[caseFilter](0)) == 0){
12121 var cpos = this._getCaretPos(fn);
12122 // only try to extend if we added the last character at the end of the input
12123 if((cpos+1) > fn.value.length){
12124 // only add to input node as we would overwrite Capitalisation of chars
12125 // actually, that is ok
12126 fn.value = text;//.substr(cpos);
12127 // visually highlight the autocompleted characters
12128 dijit.selectInputText(fn, cpos);
12129 }
12130 }else{
12131 // text does not autoComplete; replace the whole value and highlight
12132 fn.value = text;
12133 dijit.selectInputText(fn);
12134 }
12135 },
12136
12137 _openResultList: function(/*Object*/ results, /*Object*/ dataObject){
12138 this._fetchHandle = null;
12139 if( this.disabled ||
12140 this.readOnly ||
12141 (dataObject.query[this.searchAttr] != this._lastQuery)
12142 ){
12143 return;
12144 }
12145 this._popupWidget.clearResultList();
12146 if(!results.length && !this._maxOptions){ // this condition needs to match !this._isvalid set in FilteringSelect::_openResultList
12147 this._hideResultList();
12148 return;
12149 }
12150
12151
12152 // Fill in the textbox with the first item from the drop down list,
12153 // and highlight the characters that were auto-completed. For
12154 // example, if user typed "CA" and the drop down list appeared, the
12155 // textbox would be changed to "California" and "ifornia" would be
12156 // highlighted.
12157
12158 dataObject._maxOptions = this._maxOptions;
12159 var nodes = this._popupWidget.createOptions(
12160 results,
12161 dataObject,
12162 dojo.hitch(this, "_getMenuLabelFromItem")
12163 );
12164
12165 // show our list (only if we have content, else nothing)
12166 this._showResultList();
12167
12168 // #4091:
12169 // tell the screen reader that the paging callback finished by
12170 // shouting the next choice
12171 if(dataObject.direction){
12172 if(1 == dataObject.direction){
12173 this._popupWidget.highlightFirstOption();
12174 }else if(-1 == dataObject.direction){
12175 this._popupWidget.highlightLastOption();
12176 }
12177 this._announceOption(this._popupWidget.getHighlightedOption());
12178 }else if(this.autoComplete && !this._prev_key_backspace /*&& !dataObject.direction*/
12179 // when the user clicks the arrow button to show the full list,
12180 // startSearch looks for "*".
12181 // it does not make sense to autocomplete
12182 // if they are just previewing the options available.
12183 && !/^[*]+$/.test(dataObject.query[this.searchAttr])){
12184 this._announceOption(nodes[1]); // 1st real item
12185 }
12186 },
12187
12188 _showResultList: function(){
12189 this._hideResultList();
12190 // hide the tooltip
12191 this.displayMessage("");
12192
12193 // Position the list and if it's too big to fit on the screen then
12194 // size it to the maximum possible height
12195 // Our dear friend IE doesnt take max-height so we need to
12196 // calculate that on our own every time
12197
12198 // TODO: want to redo this, see
12199 // http://trac.dojotoolkit.org/ticket/3272
12200 // and
12201 // http://trac.dojotoolkit.org/ticket/4108
12202
12203
12204 // natural size of the list has changed, so erase old
12205 // width/height settings, which were hardcoded in a previous
12206 // call to this function (via dojo.marginBox() call)
12207 dojo.style(this._popupWidget.domNode, {width: "", height: ""});
12208
12209 var best = this.open();
12210 // #3212:
12211 // only set auto scroll bars if necessary prevents issues with
12212 // scroll bars appearing when they shouldn't when node is made
12213 // wider (fractional pixels cause this)
12214 var popupbox = dojo.marginBox(this._popupWidget.domNode);
12215 this._popupWidget.domNode.style.overflow =
12216 ((best.h == popupbox.h) && (best.w == popupbox.w)) ? "hidden" : "auto";
12217 // #4134:
12218 // borrow TextArea scrollbar test so content isn't covered by
12219 // scrollbar and horizontal scrollbar doesn't appear
12220 var newwidth = best.w;
12221 if(best.h < this._popupWidget.domNode.scrollHeight){
12222 newwidth += 16;
12223 }
12224 dojo.marginBox(this._popupWidget.domNode, {
12225 h: best.h,
12226 w: Math.max(newwidth, this.domNode.offsetWidth)
12227 });
12228
12229 // If we increased the width of drop down to match the width of ComboBox.domNode,
12230 // then need to reposition the drop down (wrapper) so (all of) the drop down still
12231 // appears underneath the ComboBox.domNode
12232 if(newwidth < this.domNode.offsetWidth){
12233 this._popupWidget.domNode.parentNode.style.left = dojo.position(this.domNode, true).x + "px";
12234 }
12235
12236 dijit.setWaiState(this.comboNode, "expanded", "true");
12237 },
12238
12239 _hideResultList: function(){
12240 this._abortQuery();
12241 if(this._isShowingNow){
12242 dijit.popup.close(this._popupWidget);
12243 this._isShowingNow=false;
12244 dijit.setWaiState(this.comboNode, "expanded", "false");
12245 dijit.removeWaiState(this.focusNode,"activedescendant");
12246 }
12247 },
12248
12249 _setBlurValue: function(){
12250 // if the user clicks away from the textbox OR tabs away, set the
12251 // value to the textbox value
12252 // #4617:
12253 // if value is now more choices or previous choices, revert
12254 // the value
12255 var newvalue = this.get('displayedValue');
12256 var pw = this._popupWidget;
12257 if(pw && (
12258 newvalue == pw._messages["previousMessage"] ||
12259 newvalue == pw._messages["nextMessage"]
12260 )
12261 ){
12262 this._setValueAttr(this._lastValueReported, true);
12263 }else if(typeof this.item == "undefined"){
12264 // Update 'value' (ex: KY) according to currently displayed text
12265 this.item = null;
12266 this.set('displayedValue', newvalue);
12267 }else{
12268 if(this.value != this._lastValueReported){
12269 dijit.form._FormValueWidget.prototype._setValueAttr.call(this, this.value, true);
12270 }
12271 this._refreshState();
12272 }
12273 },
12274
12275 _onBlur: function(){
12276 // summary:
12277 // Called magically when focus has shifted away from this widget and it's drop down
12278 this._hideResultList();
12279 this.inherited(arguments);
12280 },
12281
12282 _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
12283 // summary:
12284 // Set the displayed valued in the input box, and the hidden value
12285 // that gets submitted, based on a dojo.data store item.
12286 // description:
12287 // Users shouldn't call this function; they should be calling
12288 // attr('item', value)
12289 // tags:
12290 // private
12291 if(!displayedValue){ displayedValue = this.labelFunc(item, this.store); }
12292 this.value = this._getValueField() != this.searchAttr? this.store.getIdentity(item) : displayedValue;
12293 this.item = item;
12294 dijit.form.ComboBox.superclass._setValueAttr.call(this, this.value, priorityChange, displayedValue);
12295 },
12296
12297 _announceOption: function(/*Node*/ node){
12298 // summary:
12299 // a11y code that puts the highlighted option in the textbox.
12300 // This way screen readers will know what is happening in the
12301 // menu.
12302
12303 if(!node){
12304 return;
12305 }
12306 // pull the text value from the item attached to the DOM node
12307 var newValue;
12308 if(node == this._popupWidget.nextButton ||
12309 node == this._popupWidget.previousButton){
12310 newValue = node.innerHTML;
12311 this.item = undefined;
12312 this.value = '';
12313 }else{
12314 newValue = this.labelFunc(node.item, this.store);
12315 this.set('item', node.item, false, newValue);
12316 }
12317 // get the text that the user manually entered (cut off autocompleted text)
12318 this.focusNode.value = this.focusNode.value.substring(0, this._lastInput.length);
12319 // set up ARIA activedescendant
12320 dijit.setWaiState(this.focusNode, "activedescendant", dojo.attr(node, "id"));
12321 // autocomplete the rest of the option to announce change
12322 this._autoCompleteText(newValue);
12323 },
12324
12325 _selectOption: function(/*Event*/ evt){
12326 // summary:
12327 // Menu callback function, called when an item in the menu is selected.
12328 if(evt){
12329 this._announceOption(evt.target);
12330 }
12331 this._hideResultList();
12332 this._setCaretPos(this.focusNode, this.focusNode.value.length);
12333 dijit.form._FormValueWidget.prototype._setValueAttr.call(this, this.value, true); // set this.value and fire onChange
12334 },
12335
12336 _onArrowMouseDown: function(evt){
12337 // summary:
12338 // Callback when arrow is clicked
12339 if(this.disabled || this.readOnly){
12340 return;
12341 }
12342 dojo.stopEvent(evt);
12343 this.focus();
12344 if(this._isShowingNow){
12345 this._hideResultList();
12346 }else{
12347 // forces full population of results, if they click
12348 // on the arrow it means they want to see more options
12349 this._startSearchAll();
12350 }
12351 },
12352
12353 _startSearchAll: function(){
12354 this._startSearch('');
12355 },
12356
12357 _startSearchFromInput: function(){
12358 this._startSearch(this.focusNode.value.replace(/([\\\*\?])/g, "\\$1"));
12359 },
12360
12361 _getQueryString: function(/*String*/ text){
12362 return dojo.string.substitute(this.queryExpr, [text]);
12363 },
12364
12365 _startSearch: function(/*String*/ key){
12366 if(!this._popupWidget){
12367 var popupId = this.id + "_popup";
12368 this._popupWidget = new dijit.form._ComboBoxMenu({
12369 onChange: dojo.hitch(this, this._selectOption),
12370 id: popupId,
12371 dir: this.dir
12372 });
12373 dijit.removeWaiState(this.focusNode,"activedescendant");
12374 dijit.setWaiState(this.textbox,"owns",popupId); // associate popup with textbox
12375 }
12376 // create a new query to prevent accidentally querying for a hidden
12377 // value from FilteringSelect's keyField
12378 var query = dojo.clone(this.query); // #5970
12379 this._lastInput = key; // Store exactly what was entered by the user.
12380 this._lastQuery = query[this.searchAttr] = this._getQueryString(key);
12381 // #5970: set _lastQuery, *then* start the timeout
12382 // otherwise, if the user types and the last query returns before the timeout,
12383 // _lastQuery won't be set and their input gets rewritten
12384 this.searchTimer=setTimeout(dojo.hitch(this, function(query, _this){
12385 this.searchTimer = null;
12386 var fetch = {
12387 queryOptions: {
12388 ignoreCase: this.ignoreCase,
12389 deep: true
12390 },
12391 query: query,
12392 onBegin: dojo.hitch(this, "_setMaxOptions"),
12393 onComplete: dojo.hitch(this, "_openResultList"),
12394 onError: function(errText){
12395 _this._fetchHandle = null;
12396 console.error('dijit.form.ComboBox: ' + errText);
12397 dojo.hitch(_this, "_hideResultList")();
12398 },
12399 start: 0,
12400 count: this.pageSize
12401 };
12402 dojo.mixin(fetch, _this.fetchProperties);
12403 this._fetchHandle = _this.store.fetch(fetch);
12404
12405 var nextSearch = function(dataObject, direction){
12406 dataObject.start += dataObject.count*direction;
12407 // #4091:
12408 // tell callback the direction of the paging so the screen
12409 // reader knows which menu option to shout
12410 dataObject.direction = direction;
12411 this._fetchHandle = this.store.fetch(dataObject);
12412 };
12413 this._nextSearch = this._popupWidget.onPage = dojo.hitch(this, nextSearch, this._fetchHandle);
12414 }, query, this), this.searchDelay);
12415 },
12416
12417 _setMaxOptions: function(size, request){
12418 this._maxOptions = size;
12419 },
12420
12421 _getValueField: function(){
12422 // summmary:
12423 // Helper for postMixInProperties() to set this.value based on data inlined into the markup.
12424 // Returns the attribute name in the item (in dijit.form._ComboBoxDataStore) to use as the value.
12425 return this.searchAttr;
12426 },
12427
12428 /////////////// Event handlers /////////////////////
12429
12430 // FIXME: For 2.0, rename to "_compositionEnd"
12431 compositionend: function(/*Event*/ evt){
12432 // summary:
12433 // When inputting characters using an input method, such as
12434 // Asian languages, it will generate this event instead of
12435 // onKeyDown event.
12436 // Note: this event is only triggered in FF (not in IE/safari)
12437 // tags:
12438 // private
12439
12440 // 229 is the code produced by IE and safari while pressing keys during
12441 // IME input mode
12442 this._onKeyPress({charOrCode: 229});
12443 },
12444
12445 //////////// INITIALIZATION METHODS ///////////////////////////////////////
12446
12447 constructor: function(){
12448 this.query={};
12449 this.fetchProperties={};
12450 },
12451
12452 postMixInProperties: function(){
12453 if(!this.store){
12454 var srcNodeRef = this.srcNodeRef;
12455
12456 // if user didn't specify store, then assume there are option tags
12457 this.store = new dijit.form._ComboBoxDataStore(srcNodeRef);
12458
12459 // if there is no value set and there is an option list, set
12460 // the value to the first value to be consistent with native
12461 // Select
12462
12463 // Firefox and Safari set value
12464 // IE6 and Opera set selectedIndex, which is automatically set
12465 // by the selected attribute of an option tag
12466 // IE6 does not set value, Opera sets value = selectedIndex
12467 if(!("value" in this.params)){
12468 var item = this.store.fetchSelectedItem();
12469 if(item){
12470 var valueField = this._getValueField();
12471 this.value = valueField != this.searchAttr? this.store.getValue(item, valueField) : this.labelFunc(item, this.store);
12472 }
12473 }
12474 }
12475 this.inherited(arguments);
12476 },
12477
12478 postCreate: function(){
12479 // summary:
12480 // Subclasses must call this method from their postCreate() methods
12481 // tags:
12482 // protected
12483
12484 if(!this.hasDownArrow){
12485 this.downArrowNode.style.display = "none";
12486 }
12487
12488 // find any associated label element and add to ComboBox node.
12489 var label=dojo.query('label[for="'+this.id+'"]');
12490 if(label.length){
12491 label[0].id = (this.id+"_label");
12492 var cn=this.comboNode;
12493 dijit.setWaiState(cn, "labelledby", label[0].id);
12494
12495 }
12496 this.inherited(arguments);
12497 },
12498
12499 uninitialize: function(){
12500 if(this._popupWidget && !this._popupWidget._destroyed){
12501 this._hideResultList();
12502 this._popupWidget.destroy();
12503 }
12504 this.inherited(arguments);
12505 },
12506
12507 _getMenuLabelFromItem: function(/*Item*/ item){
12508 var label = this.labelAttr? this.store.getValue(item, this.labelAttr) : this.labelFunc(item, this.store);
12509 var labelType = this.labelType;
12510 // If labelType is not "text" we don't want to screw any markup ot whatever.
12511 if(this.highlightMatch != "none" && this.labelType == "text" && this._lastInput){
12512 label = this.doHighlight(label, this._escapeHtml(this._lastInput));
12513 labelType = "html";
12514 }
12515 return {html: labelType == "html", label: label};
12516 },
12517
12518 doHighlight: function(/*String*/label, /*String*/find){
12519 // summary:
12520 // Highlights the string entered by the user in the menu. By default this
12521 // highlights the first occurence found. Override this method
12522 // to implement your custom highlighing.
12523 // tags:
12524 // protected
12525
12526 // Add greedy when this.highlightMatch == "all"
12527 var modifiers = "i"+(this.highlightMatch == "all"?"g":"");
12528 var escapedLabel = this._escapeHtml(label);
12529 find = dojo.regexp.escapeString(find); // escape regexp special chars
12530 var ret = escapedLabel.replace(new RegExp("(^|\\s)("+ find +")", modifiers),
12531 '$1<span class="dijitComboBoxHighlightMatch">$2</span>');
12532 return ret;// returns String, (almost) valid HTML (entities encoded)
12533 },
12534
12535 _escapeHtml: function(/*string*/str){
12536 // TODO Should become dojo.html.entities(), when exists use instead
12537 // summary:
12538 // Adds escape sequences for special characters in XML: &<>"'
12539 str = String(str).replace(/&/gm, "&amp;").replace(/</gm, "&lt;")
12540 .replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
12541 return str; // string
12542 },
12543
12544 open: function(){
12545 // summary:
12546 // Opens the drop down menu. TODO: rename to _open.
12547 // tags:
12548 // private
12549 this._isShowingNow=true;
12550 return dijit.popup.open({
12551 popup: this._popupWidget,
12552 around: this.domNode,
12553 parent: this
12554 });
12555 },
12556
12557 reset: function(){
12558 // Overrides the _FormWidget.reset().
12559 // Additionally reset the .item (to clean up).
12560 this.item = null;
12561 this.inherited(arguments);
12562 },
12563
12564 labelFunc: function(/*item*/ item, /*dojo.data.store*/ store){
12565 // summary:
12566 // Computes the label to display based on the dojo.data store item.
12567 // returns:
12568 // The label that the ComboBox should display
12569 // tags:
12570 // private
12571
12572 // Use toString() because XMLStore returns an XMLItem whereas this
12573 // method is expected to return a String (#9354)
12574 return store.getValue(item, this.searchAttr).toString(); // String
12575 }
12576 }
12577);
12578
12579dojo.declare(
12580 "dijit.form._ComboBoxMenu",
12581 [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
12582 {
12583 // summary:
12584 // Focus-less menu for internal use in `dijit.form.ComboBox`
12585 // tags:
12586 // private
12587
12588 templateString: "<ul class='dijitReset dijitMenu' dojoAttachEvent='onmousedown:_onMouseDown,onmouseup:_onMouseUp,onmouseover:_onMouseOver,onmouseout:_onMouseOut' tabIndex='-1' style='overflow: \"auto\"; overflow-x: \"hidden\";'>"
12589 +"<li class='dijitMenuItem dijitMenuPreviousButton' dojoAttachPoint='previousButton' waiRole='option'></li>"
12590 +"<li class='dijitMenuItem dijitMenuNextButton' dojoAttachPoint='nextButton' waiRole='option'></li>"
12591 +"</ul>",
12592
12593 // _messages: Object
12594 // Holds "next" and "previous" text for paging buttons on drop down
12595 _messages: null,
12596
12597 baseClass: "dijitComboBoxMenu",
12598
12599 postMixInProperties: function(){
12600 this._messages = dojo.i18n.getLocalization("dijit.form", "ComboBox", this.lang);
12601 this.inherited(arguments);
12602 },
12603
12604 _setValueAttr: function(/*Object*/ value){
12605 this.value = value;
12606 this.onChange(value);
12607 },
12608
12609 // stubs
12610 onChange: function(/*Object*/ value){
12611 // summary:
12612 // Notifies ComboBox/FilteringSelect that user clicked an option in the drop down menu.
12613 // Probably should be called onSelect.
12614 // tags:
12615 // callback
12616 },
12617 onPage: function(/*Number*/ direction){
12618 // summary:
12619 // Notifies ComboBox/FilteringSelect that user clicked to advance to next/previous page.
12620 // tags:
12621 // callback
12622 },
12623
12624 postCreate: function(){
12625 // fill in template with i18n messages
12626 this.previousButton.innerHTML = this._messages["previousMessage"];
12627 this.nextButton.innerHTML = this._messages["nextMessage"];
12628 this.inherited(arguments);
12629 },
12630
12631 onClose: function(){
12632 // summary:
12633 // Callback from dijit.popup code to this widget, notifying it that it closed
12634 // tags:
12635 // private
12636 this._blurOptionNode();
12637 },
12638
12639 _createOption: function(/*Object*/ item, labelFunc){
12640 // summary:
12641 // Creates an option to appear on the popup menu subclassed by
12642 // `dijit.form.FilteringSelect`.
12643
12644 var labelObject = labelFunc(item);
12645 var menuitem = dojo.doc.createElement("li");
12646 dijit.setWaiRole(menuitem, "option");
12647 if(labelObject.html){
12648 menuitem.innerHTML = labelObject.label;
12649 }else{
12650 menuitem.appendChild(
12651 dojo.doc.createTextNode(labelObject.label)
12652 );
12653 }
12654 // #3250: in blank options, assign a normal height
12655 if(menuitem.innerHTML == ""){
12656 menuitem.innerHTML = "&nbsp;";
12657 }
12658 menuitem.item=item;
12659 return menuitem;
12660 },
12661
12662 createOptions: function(results, dataObject, labelFunc){
12663 // summary:
12664 // Fills in the items in the drop down list
12665 // results:
12666 // Array of dojo.data items
12667 // dataObject:
12668 // dojo.data store
12669 // labelFunc:
12670 // Function to produce a label in the drop down list from a dojo.data item
12671
12672 //this._dataObject=dataObject;
12673 //this._dataObject.onComplete=dojo.hitch(comboBox, comboBox._openResultList);
12674 // display "Previous . . ." button
12675 this.previousButton.style.display = (dataObject.start == 0) ? "none" : "";
12676 dojo.attr(this.previousButton, "id", this.id + "_prev");
12677 // create options using _createOption function defined by parent
12678 // ComboBox (or FilteringSelect) class
12679 // #2309:
12680 // iterate over cache nondestructively
12681 dojo.forEach(results, function(item, i){
12682 var menuitem = this._createOption(item, labelFunc);
12683 menuitem.className = "dijitReset dijitMenuItem" +
12684 (this.isLeftToRight() ? "" : " dijitMenuItemRtl");
12685 dojo.attr(menuitem, "id", this.id + i);
12686 this.domNode.insertBefore(menuitem, this.nextButton);
12687 }, this);
12688 // display "Next . . ." button
12689 var displayMore = false;
12690 //Try to determine if we should show 'more'...
12691 if(dataObject._maxOptions && dataObject._maxOptions != -1){
12692 if((dataObject.start + dataObject.count) < dataObject._maxOptions){
12693 displayMore = true;
12694 }else if((dataObject.start + dataObject.count) > dataObject._maxOptions && dataObject.count == results.length){
12695 //Weird return from a datastore, where a start + count > maxOptions
12696 // implies maxOptions isn't really valid and we have to go into faking it.
12697 //And more or less assume more if count == results.length
12698 displayMore = true;
12699 }
12700 }else if(dataObject.count == results.length){
12701 //Don't know the size, so we do the best we can based off count alone.
12702 //So, if we have an exact match to count, assume more.
12703 displayMore = true;
12704 }
12705
12706 this.nextButton.style.display = displayMore ? "" : "none";
12707 dojo.attr(this.nextButton,"id", this.id + "_next");
12708 return this.domNode.childNodes;
12709 },
12710
12711 clearResultList: function(){
12712 // summary:
12713 // Clears the entries in the drop down list, but of course keeps the previous and next buttons.
12714 while(this.domNode.childNodes.length>2){
12715 this.domNode.removeChild(this.domNode.childNodes[this.domNode.childNodes.length-2]);
12716 }
12717 },
12718
12719 _onMouseDown: function(/*Event*/ evt){
12720 dojo.stopEvent(evt);
12721 },
12722
12723 _onMouseUp: function(/*Event*/ evt){
12724 if(evt.target === this.domNode || !this._highlighted_option){
12725 return;
12726 }else if(evt.target == this.previousButton){
12727 this.onPage(-1);
12728 }else if(evt.target == this.nextButton){
12729 this.onPage(1);
12730 }else{
12731 var tgt = evt.target;
12732 // while the clicked node is inside the div
12733 while(!tgt.item){
12734 // recurse to the top
12735 tgt = tgt.parentNode;
12736 }
12737 this._setValueAttr({ target: tgt }, true);
12738 }
12739 },
12740
12741 _onMouseOver: function(/*Event*/ evt){
12742 if(evt.target === this.domNode){ return; }
12743 var tgt = evt.target;
12744 if(!(tgt == this.previousButton || tgt == this.nextButton)){
12745 // while the clicked node is inside the div
12746 while(!tgt.item){
12747 // recurse to the top
12748 tgt = tgt.parentNode;
12749 }
12750 }
12751 this._focusOptionNode(tgt);
12752 },
12753
12754 _onMouseOut: function(/*Event*/ evt){
12755 if(evt.target === this.domNode){ return; }
12756 this._blurOptionNode();
12757 },
12758
12759 _focusOptionNode: function(/*DomNode*/ node){
12760 // summary:
12761 // Does the actual highlight.
12762 if(this._highlighted_option != node){
12763 this._blurOptionNode();
12764 this._highlighted_option = node;
12765 dojo.addClass(this._highlighted_option, "dijitMenuItemSelected");
12766 }
12767 },
12768
12769 _blurOptionNode: function(){
12770 // summary:
12771 // Removes highlight on highlighted option.
12772 if(this._highlighted_option){
12773 dojo.removeClass(this._highlighted_option, "dijitMenuItemSelected");
12774 this._highlighted_option = null;
12775 }
12776 },
12777
12778 _highlightNextOption: function(){
12779 // summary:
12780 // Highlight the item just below the current selection.
12781 // If nothing selected, highlight first option.
12782
12783 // because each press of a button clears the menu,
12784 // the highlighted option sometimes becomes detached from the menu!
12785 // test to see if the option has a parent to see if this is the case.
12786 if(!this.getHighlightedOption()){
12787 var fc = this.domNode.firstChild;
12788 this._focusOptionNode(fc.style.display == "none" ? fc.nextSibling : fc);
12789 }else{
12790 var ns = this._highlighted_option.nextSibling;
12791 if(ns && ns.style.display != "none"){
12792 this._focusOptionNode(ns);
12793 }else{
12794 this.highlightFirstOption();
12795 }
12796 }
12797 // scrollIntoView is called outside of _focusOptionNode because in IE putting it inside causes the menu to scroll up on mouseover
12798 dojo.window.scrollIntoView(this._highlighted_option);
12799 },
12800
12801 highlightFirstOption: function(){
12802 // summary:
12803 // Highlight the first real item in the list (not Previous Choices).
12804 var first = this.domNode.firstChild;
12805 var second = first.nextSibling;
12806 this._focusOptionNode(second.style.display == "none" ? first : second); // remotely possible that Previous Choices is the only thing in the list
12807 dojo.window.scrollIntoView(this._highlighted_option);
12808 },
12809
12810 highlightLastOption: function(){
12811 // summary:
12812 // Highlight the last real item in the list (not More Choices).
12813 this._focusOptionNode(this.domNode.lastChild.previousSibling);
12814 dojo.window.scrollIntoView(this._highlighted_option);
12815 },
12816
12817 _highlightPrevOption: function(){
12818 // summary:
12819 // Highlight the item just above the current selection.
12820 // If nothing selected, highlight last option (if
12821 // you select Previous and try to keep scrolling up the list).
12822 if(!this.getHighlightedOption()){
12823 var lc = this.domNode.lastChild;
12824 this._focusOptionNode(lc.style.display == "none" ? lc.previousSibling : lc);
12825 }else{
12826 var ps = this._highlighted_option.previousSibling;
12827 if(ps && ps.style.display != "none"){
12828 this._focusOptionNode(ps);
12829 }else{
12830 this.highlightLastOption();
12831 }
12832 }
12833 dojo.window.scrollIntoView(this._highlighted_option);
12834 },
12835
12836 _page: function(/*Boolean*/ up){
12837 // summary:
12838 // Handles page-up and page-down keypresses
12839
12840 var scrollamount = 0;
12841 var oldscroll = this.domNode.scrollTop;
12842 var height = dojo.style(this.domNode, "height");
12843 // if no item is highlighted, highlight the first option
12844 if(!this.getHighlightedOption()){
12845 this._highlightNextOption();
12846 }
12847 while(scrollamount<height){
12848 if(up){
12849 // stop at option 1
12850 if(!this.getHighlightedOption().previousSibling ||
12851 this._highlighted_option.previousSibling.style.display == "none"){
12852 break;
12853 }
12854 this._highlightPrevOption();
12855 }else{
12856 // stop at last option
12857 if(!this.getHighlightedOption().nextSibling ||
12858 this._highlighted_option.nextSibling.style.display == "none"){
12859 break;
12860 }
12861 this._highlightNextOption();
12862 }
12863 // going backwards
12864 var newscroll=this.domNode.scrollTop;
12865 scrollamount+=(newscroll-oldscroll)*(up ? -1:1);
12866 oldscroll=newscroll;
12867 }
12868 },
12869
12870 pageUp: function(){
12871 // summary:
12872 // Handles pageup keypress.
12873 // TODO: just call _page directly from handleKey().
12874 // tags:
12875 // private
12876 this._page(true);
12877 },
12878
12879 pageDown: function(){
12880 // summary:
12881 // Handles pagedown keypress.
12882 // TODO: just call _page directly from handleKey().
12883 // tags:
12884 // private
12885 this._page(false);
12886 },
12887
12888 getHighlightedOption: function(){
12889 // summary:
12890 // Returns the highlighted option.
12891 var ho = this._highlighted_option;
12892 return (ho && ho.parentNode) ? ho : null;
12893 },
12894
12895 handleKey: function(key){
12896 switch(key){
12897 case dojo.keys.DOWN_ARROW:
12898 this._highlightNextOption();
12899 break;
12900 case dojo.keys.PAGE_DOWN:
12901 this.pageDown();
12902 break;
12903 case dojo.keys.UP_ARROW:
12904 this._highlightPrevOption();
12905 break;
12906 case dojo.keys.PAGE_UP:
12907 this.pageUp();
12908 break;
12909 }
12910 }
12911 }
12912);
12913
12914dojo.declare(
12915 "dijit.form.ComboBox",
12916 [dijit.form.ValidationTextBox, dijit.form.ComboBoxMixin],
12917 {
12918 // summary:
12919 // Auto-completing text box, and base class for dijit.form.FilteringSelect.
12920 //
12921 // description:
12922 // The drop down box's values are populated from an class called
12923 // a data provider, which returns a list of values based on the characters
12924 // that the user has typed into the input box.
12925 // If OPTION tags are used as the data provider via markup,
12926 // then the OPTION tag's child text node is used as the widget value
12927 // when selected. The OPTION tag's value attribute is ignored.
12928 // To set the default value when using OPTION tags, specify the selected
12929 // attribute on 1 of the child OPTION tags.
12930 //
12931 // Some of the options to the ComboBox are actually arguments to the data
12932 // provider.
12933
12934 _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
12935 // summary:
12936 // Hook so attr('value', value) works.
12937 // description:
12938 // Sets the value of the select.
12939 this.item = null; // value not looked up in store
12940 if(!value){ value = ''; } // null translates to blank
12941 dijit.form.ValidationTextBox.prototype._setValueAttr.call(this, value, priorityChange, displayedValue);
12942 }
12943 }
12944);
12945
12946dojo.declare("dijit.form._ComboBoxDataStore", null, {
12947 // summary:
12948 // Inefficient but small data store specialized for inlined `dijit.form.ComboBox` data
12949 //
12950 // description:
12951 // Provides a store for inlined data like:
12952 //
12953 // | <select>
12954 // | <option value="AL">Alabama</option>
12955 // | ...
12956 //
12957 // Actually. just implements the subset of dojo.data.Read/Notification
12958 // needed for ComboBox and FilteringSelect to work.
12959 //
12960 // Note that an item is just a pointer to the <option> DomNode.
12961
12962 constructor: function( /*DomNode*/ root){
12963 this.root = root;
12964 if(root.tagName != "SELECT" && root.firstChild){
12965 root = dojo.query("select", root);
12966 if(root.length > 0){ // SELECT is a child of srcNodeRef
12967 root = root[0];
12968 }else{ // no select, so create 1 to parent the option tags to define selectedIndex
12969 this.root.innerHTML = "<SELECT>"+this.root.innerHTML+"</SELECT>";
12970 root = this.root.firstChild;
12971 }
12972 this.root = root;
12973 }
12974 dojo.query("> option", root).forEach(function(node){
12975 // TODO: this was added in #3858 but unclear why/if it's needed; doesn't seem to be.
12976 // If it is needed then can we just hide the select itself instead?
12977 //node.style.display="none";
12978 node.innerHTML = dojo.trim(node.innerHTML);
12979 });
12980
12981 },
12982
12983 getValue: function( /* item */ item,
12984 /* attribute-name-string */ attribute,
12985 /* value? */ defaultValue){
12986 return (attribute == "value") ? item.value : (item.innerText || item.textContent || '');
12987 },
12988
12989 isItemLoaded: function(/* anything */ something){
12990 return true;
12991 },
12992
12993 getFeatures: function(){
12994 return {"dojo.data.api.Read": true, "dojo.data.api.Identity": true};
12995 },
12996
12997 _fetchItems: function( /* Object */ args,
12998 /* Function */ findCallback,
12999 /* Function */ errorCallback){
13000 // summary:
13001 // See dojo.data.util.simpleFetch.fetch()
13002 if(!args.query){ args.query = {}; }
13003 if(!args.query.name){ args.query.name = ""; }
13004 if(!args.queryOptions){ args.queryOptions = {}; }
13005 var matcher = dojo.data.util.filter.patternToRegExp(args.query.name, args.queryOptions.ignoreCase),
13006 items = dojo.query("> option", this.root).filter(function(option){
13007 return (option.innerText || option.textContent || '').match(matcher);
13008 } );
13009 if(args.sort){
13010 items.sort(dojo.data.util.sorter.createSortFunction(args.sort, this));
13011 }
13012 findCallback(items, args);
13013 },
13014
13015 close: function(/*dojo.data.api.Request || args || null */ request){
13016 return;
13017 },
13018
13019 getLabel: function(/* item */ item){
13020 return item.innerHTML;
13021 },
13022
13023 getIdentity: function(/* item */ item){
13024 return dojo.attr(item, "value");
13025 },
13026
13027 fetchItemByIdentity: function(/* Object */ args){
13028 // summary:
13029 // Given the identity of an item, this method returns the item that has
13030 // that identity through the onItem callback.
13031 // Refer to dojo.data.api.Identity.fetchItemByIdentity() for more details.
13032 //
13033 // description:
13034 // Given arguments like:
13035 //
13036 // | {identity: "CA", onItem: function(item){...}
13037 //
13038 // Call `onItem()` with the DOM node `<option value="CA">California</option>`
13039 var item = dojo.query("> option[value='" + args.identity + "']", this.root)[0];
13040 args.onItem(item);
13041 },
13042
13043 fetchSelectedItem: function(){
13044 // summary:
13045 // Get the option marked as selected, like `<option selected>`.
13046 // Not part of dojo.data API.
13047 var root = this.root,
13048 si = root.selectedIndex;
13049 return typeof si == "number"
13050 ? dojo.query("> option:nth-child(" + (si != -1 ? si+1 : 1) + ")", root)[0]
13051 : null; // dojo.data.Item
13052 }
13053});
13054//Mix in the simple fetch implementation to this class.
13055dojo.extend(dijit.form._ComboBoxDataStore,dojo.data.util.simpleFetch);
13056
13057}
13058
13059if(!dojo._hasResource["dijit.form.FilteringSelect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
13060dojo._hasResource["dijit.form.FilteringSelect"] = true;
13061dojo.provide("dijit.form.FilteringSelect");
13062
13063
13064
13065dojo.declare(
13066 "dijit.form.FilteringSelect",
13067 [dijit.form.MappedTextBox, dijit.form.ComboBoxMixin],
13068 {
13069 // summary:
13070 // An enhanced version of the HTML SELECT tag, populated dynamically
13071 //
13072 // description:
13073 // An enhanced version of the HTML SELECT tag, populated dynamically. It works
13074 // very nicely with very large data sets because it can load and page data as needed.
13075 // It also resembles ComboBox, but does not allow values outside of the provided ones.
13076 // If OPTION tags are used as the data provider via markup, then the
13077 // OPTION tag's child text node is used as the displayed value when selected
13078 // while the OPTION tag's value attribute is used as the widget value on form submit.
13079 // To set the default value when using OPTION tags, specify the selected
13080 // attribute on 1 of the child OPTION tags.
13081 //
13082 // Similar features:
13083 // - There is a drop down list of possible values.
13084 // - You can only enter a value from the drop down list. (You can't
13085 // enter an arbitrary value.)
13086 // - The value submitted with the form is the hidden value (ex: CA),
13087 // not the displayed value a.k.a. label (ex: California)
13088 //
13089 // Enhancements over plain HTML version:
13090 // - If you type in some text then it will filter down the list of
13091 // possible values in the drop down list.
13092 // - List can be specified either as a static list or via a javascript
13093 // function (that can get the list from a server)
13094
13095 _isvalid: true,
13096
13097 // required: Boolean
13098 // True (default) if user is required to enter a value into this field.
13099 required: true,
13100
13101 _lastDisplayedValue: "",
13102
13103 isValid: function(){
13104 // Overrides ValidationTextBox.isValid()
13105 return this._isvalid || (!this.required && this.get('displayedValue') == ""); // #5974
13106 },
13107
13108 _refreshState: function(){
13109 if(!this.searchTimer){ // state will be refreshed after results are returned
13110 this.inherited(arguments);
13111 }
13112 },
13113
13114 _callbackSetLabel: function( /*Array*/ result,
13115 /*Object*/ dataObject,
13116 /*Boolean?*/ priorityChange){
13117 // summary:
13118 // Callback function that dynamically sets the label of the
13119 // ComboBox
13120
13121 // setValue does a synchronous lookup,
13122 // so it calls _callbackSetLabel directly,
13123 // and so does not pass dataObject
13124 // still need to test against _lastQuery in case it came too late
13125 if((dataObject && dataObject.query[this.searchAttr] != this._lastQuery) || (!dataObject && result.length && this.store.getIdentity(result[0]) != this._lastQuery)){
13126 return;
13127 }
13128 if(!result.length){
13129 //#3268: do nothing on bad input
13130 //#3285: change CSS to indicate error
13131 this.valueNode.value = "";
13132 dijit.form.TextBox.superclass._setValueAttr.call(this, "", priorityChange || (priorityChange === undefined && !this._focused));
13133 this._isvalid = false;
13134 this.validate(this._focused);
13135 this.item = null;
13136 }else{
13137 this.set('item', result[0], priorityChange);
13138 }
13139 },
13140
13141 _openResultList: function(/*Object*/ results, /*Object*/ dataObject){
13142 // Overrides ComboBox._openResultList()
13143
13144 // #3285: tap into search callback to see if user's query resembles a match
13145 if(dataObject.query[this.searchAttr] != this._lastQuery){
13146 return;
13147 }
13148 if(this.item === undefined){ // item == undefined for keyboard search
13149 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
13150 this.validate(true);
13151 }
13152 dijit.form.ComboBoxMixin.prototype._openResultList.apply(this, arguments);
13153 },
13154
13155 _getValueAttr: function(){
13156 // summary:
13157 // Hook for attr('value') to work.
13158
13159 // don't get the textbox value but rather the previously set hidden value.
13160 // Use this.valueNode.value which isn't always set for other MappedTextBox widgets until blur
13161 return this.valueNode.value;
13162 },
13163
13164 _getValueField: function(){
13165 // Overrides ComboBox._getValueField()
13166 return "value";
13167 },
13168
13169 _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange){
13170 // summary:
13171 // Hook so attr('value', value) works.
13172 // description:
13173 // Sets the value of the select.
13174 // Also sets the label to the corresponding value by reverse lookup.
13175 if(!this._onChangeActive){ priorityChange = null; }
13176 this._lastQuery = value;
13177
13178 if(value === null || value === ''){
13179 this._setDisplayedValueAttr('', priorityChange);
13180 return;
13181 }
13182
13183 //#3347: fetchItemByIdentity if no keyAttr specified
13184 var self = this;
13185 this.store.fetchItemByIdentity({
13186 identity: value,
13187 onItem: function(item){
13188 self._callbackSetLabel(item? [item] : [], undefined, priorityChange);
13189 }
13190 });
13191 },
13192
13193 _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
13194 // summary:
13195 // Set the displayed valued in the input box, and the hidden value
13196 // that gets submitted, based on a dojo.data store item.
13197 // description:
13198 // Users shouldn't call this function; they should be calling
13199 // attr('item', value)
13200 // tags:
13201 // private
13202 this._isvalid = true;
13203 this.inherited(arguments);
13204 this.valueNode.value = this.value;
13205 this._lastDisplayedValue = this.textbox.value;
13206 },
13207
13208 _getDisplayQueryString: function(/*String*/ text){
13209 return text.replace(/([\\\*\?])/g, "\\$1");
13210 },
13211
13212 _setDisplayedValueAttr: function(/*String*/ label, /*Boolean?*/ priorityChange){
13213 // summary:
13214 // Hook so attr('displayedValue', label) works.
13215 // description:
13216 // Sets textbox to display label. Also performs reverse lookup
13217 // to set the hidden value.
13218
13219 // When this is called during initialization it'll ping the datastore
13220 // for reverse lookup, and when that completes (after an XHR request)
13221 // will call setValueAttr()... but that shouldn't trigger an onChange()
13222 // event, even when it happens after creation has finished
13223 if(!this._created){
13224 priorityChange = false;
13225 }
13226
13227 if(this.store){
13228 this._hideResultList();
13229 var query = dojo.clone(this.query); // #6196: populate query with user-specifics
13230 // escape meta characters of dojo.data.util.filter.patternToRegExp().
13231 this._lastQuery = query[this.searchAttr] = this._getDisplayQueryString(label);
13232 // if the label is not valid, the callback will never set it,
13233 // so the last valid value will get the warning textbox set the
13234 // textbox value now so that the impending warning will make
13235 // sense to the user
13236 this.textbox.value = label;
13237 this._lastDisplayedValue = label;
13238 var _this = this;
13239 var fetch = {
13240 query: query,
13241 queryOptions: {
13242 ignoreCase: this.ignoreCase,
13243 deep: true
13244 },
13245 onComplete: function(result, dataObject){
13246 _this._fetchHandle = null;
13247 dojo.hitch(_this, "_callbackSetLabel")(result, dataObject, priorityChange);
13248 },
13249 onError: function(errText){
13250 _this._fetchHandle = null;
13251 console.error('dijit.form.FilteringSelect: ' + errText);
13252 dojo.hitch(_this, "_callbackSetLabel")([], undefined, false);
13253 }
13254 };
13255 dojo.mixin(fetch, this.fetchProperties);
13256 this._fetchHandle = this.store.fetch(fetch);
13257 }
13258 },
13259
13260 postMixInProperties: function(){
13261 this.inherited(arguments);
13262 this._isvalid = !this.required;
13263 },
13264
13265 undo: function(){
13266 this.set('displayedValue', this._lastDisplayedValue);
13267 }
13268 }
13269);
13270
13271}
13272
13273if(!dojo._hasResource["dojo.data.ItemFileReadStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
13274dojo._hasResource["dojo.data.ItemFileReadStore"] = true;
13275dojo.provide("dojo.data.ItemFileReadStore");
13276
13277
13278
13279
13280
13281dojo.declare("dojo.data.ItemFileReadStore", null,{
13282 // summary:
13283 // The ItemFileReadStore implements the dojo.data.api.Read API and reads
13284 // data from JSON files that have contents in this format --
13285 // { items: [
13286 // { name:'Kermit', color:'green', age:12, friends:['Gonzo', {_reference:{name:'Fozzie Bear'}}]},
13287 // { name:'Fozzie Bear', wears:['hat', 'tie']},
13288 // { name:'Miss Piggy', pets:'Foo-Foo'}
13289 // ]}
13290 // Note that it can also contain an 'identifer' property that specified which attribute on the items
13291 // in the array of items that acts as the unique identifier for that item.
13292 //
13293 constructor: function(/* Object */ keywordParameters){
13294 // summary: constructor
13295 // keywordParameters: {url: String}
13296 // keywordParameters: {data: jsonObject}
13297 // keywordParameters: {typeMap: object)
13298 // The structure of the typeMap object is as follows:
13299 // {
13300 // type0: function || object,
13301 // type1: function || object,
13302 // ...
13303 // typeN: function || object
13304 // }
13305 // Where if it is a function, it is assumed to be an object constructor that takes the
13306 // value of _value as the initialization parameters. If it is an object, then it is assumed
13307 // to be an object of general form:
13308 // {
13309 // type: function, //constructor.
13310 // deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately.
13311 // }
13312
13313 this._arrayOfAllItems = [];
13314 this._arrayOfTopLevelItems = [];
13315 this._loadFinished = false;
13316 this._jsonFileUrl = keywordParameters.url;
13317 this._ccUrl = keywordParameters.url;
13318 this.url = keywordParameters.url;
13319 this._jsonData = keywordParameters.data;
13320 this.data = null;
13321 this._datatypeMap = keywordParameters.typeMap || {};
13322 if(!this._datatypeMap['Date']){
13323 //If no default mapping for dates, then set this as default.
13324 //We use the dojo.date.stamp here because the ISO format is the 'dojo way'
13325 //of generically representing dates.
13326 this._datatypeMap['Date'] = {
13327 type: Date,
13328 deserialize: function(value){
13329 return dojo.date.stamp.fromISOString(value);
13330 }
13331 };
13332 }
13333 this._features = {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true};
13334 this._itemsByIdentity = null;
13335 this._storeRefPropName = "_S"; // Default name for the store reference to attach to every item.
13336 this._itemNumPropName = "_0"; // Default Item Id for isItem to attach to every item.
13337 this._rootItemPropName = "_RI"; // Default Item Id for isItem to attach to every item.
13338 this._reverseRefMap = "_RRM"; // Default attribute for constructing a reverse reference map for use with reference integrity
13339 this._loadInProgress = false; //Got to track the initial load to prevent duelling loads of the dataset.
13340 this._queuedFetches = [];
13341 if(keywordParameters.urlPreventCache !== undefined){
13342 this.urlPreventCache = keywordParameters.urlPreventCache?true:false;
13343 }
13344 if(keywordParameters.hierarchical !== undefined){
13345 this.hierarchical = keywordParameters.hierarchical?true:false;
13346 }
13347 if(keywordParameters.clearOnClose){
13348 this.clearOnClose = true;
13349 }
13350 if("failOk" in keywordParameters){
13351 this.failOk = keywordParameters.failOk?true:false;
13352 }
13353 },
13354
13355 url: "", // use "" rather than undefined for the benefit of the parser (#3539)
13356
13357 //Internal var, crossCheckUrl. Used so that setting either url or _jsonFileUrl, can still trigger a reload
13358 //when clearOnClose and close is used.
13359 _ccUrl: "",
13360
13361 data: null, // define this so that the parser can populate it
13362
13363 typeMap: null, //Define so parser can populate.
13364
13365 //Parameter to allow users to specify if a close call should force a reload or not.
13366 //By default, it retains the old behavior of not clearing if close is called. But
13367 //if set true, the store will be reset to default state. Note that by doing this,
13368 //all item handles will become invalid and a new fetch must be issued.
13369 clearOnClose: false,
13370
13371 //Parameter to allow specifying if preventCache should be passed to the xhrGet call or not when loading data from a url.
13372 //Note this does not mean the store calls the server on each fetch, only that the data load has preventCache set as an option.
13373 //Added for tracker: #6072
13374 urlPreventCache: false,
13375
13376 //Parameter for specifying that it is OK for the xhrGet call to fail silently.
13377 failOk: false,
13378
13379 //Parameter to indicate to process data from the url as hierarchical
13380 //(data items can contain other data items in js form). Default is true
13381 //for backwards compatibility. False means only root items are processed
13382 //as items, all child objects outside of type-mapped objects and those in
13383 //specific reference format, are left straight JS data objects.
13384 hierarchical: true,
13385
13386 _assertIsItem: function(/* item */ item){
13387 // summary:
13388 // This function tests whether the item passed in is indeed an item in the store.
13389 // item:
13390 // The item to test for being contained by the store.
13391 if(!this.isItem(item)){
13392 throw new Error("dojo.data.ItemFileReadStore: Invalid item argument.");
13393 }
13394 },
13395
13396 _assertIsAttribute: function(/* attribute-name-string */ attribute){
13397 // summary:
13398 // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
13399 // attribute:
13400 // The attribute to test for being contained by the store.
13401 if(typeof attribute !== "string"){
13402 throw new Error("dojo.data.ItemFileReadStore: Invalid attribute argument.");
13403 }
13404 },
13405
13406 getValue: function( /* item */ item,
13407 /* attribute-name-string */ attribute,
13408 /* value? */ defaultValue){
13409 // summary:
13410 // See dojo.data.api.Read.getValue()
13411 var values = this.getValues(item, attribute);
13412 return (values.length > 0)?values[0]:defaultValue; // mixed
13413 },
13414
13415 getValues: function(/* item */ item,
13416 /* attribute-name-string */ attribute){
13417 // summary:
13418 // See dojo.data.api.Read.getValues()
13419
13420 this._assertIsItem(item);
13421 this._assertIsAttribute(attribute);
13422 // Clone it before returning. refs: #10474
13423 return (item[attribute] || []).slice(0); // Array
13424 },
13425
13426 getAttributes: function(/* item */ item){
13427 // summary:
13428 // See dojo.data.api.Read.getAttributes()
13429 this._assertIsItem(item);
13430 var attributes = [];
13431 for(var key in item){
13432 // Save off only the real item attributes, not the special id marks for O(1) isItem.
13433 if((key !== this._storeRefPropName) && (key !== this._itemNumPropName) && (key !== this._rootItemPropName) && (key !== this._reverseRefMap)){
13434 attributes.push(key);
13435 }
13436 }
13437 return attributes; // Array
13438 },
13439
13440 hasAttribute: function( /* item */ item,
13441 /* attribute-name-string */ attribute){
13442 // summary:
13443 // See dojo.data.api.Read.hasAttribute()
13444 this._assertIsItem(item);
13445 this._assertIsAttribute(attribute);
13446 return (attribute in item);
13447 },
13448
13449 containsValue: function(/* item */ item,
13450 /* attribute-name-string */ attribute,
13451 /* anything */ value){
13452 // summary:
13453 // See dojo.data.api.Read.containsValue()
13454 var regexp = undefined;
13455 if(typeof value === "string"){
13456 regexp = dojo.data.util.filter.patternToRegExp(value, false);
13457 }
13458 return this._containsValue(item, attribute, value, regexp); //boolean.
13459 },
13460
13461 _containsValue: function( /* item */ item,
13462 /* attribute-name-string */ attribute,
13463 /* anything */ value,
13464 /* RegExp?*/ regexp){
13465 // summary:
13466 // Internal function for looking at the values contained by the item.
13467 // description:
13468 // Internal function for looking at the values contained by the item. This
13469 // function allows for denoting if the comparison should be case sensitive for
13470 // strings or not (for handling filtering cases where string case should not matter)
13471 //
13472 // item:
13473 // The data item to examine for attribute values.
13474 // attribute:
13475 // The attribute to inspect.
13476 // value:
13477 // The value to match.
13478 // regexp:
13479 // Optional regular expression generated off value if value was of string type to handle wildcarding.
13480 // If present and attribute values are string, then it can be used for comparison instead of 'value'
13481 return dojo.some(this.getValues(item, attribute), function(possibleValue){
13482 if(possibleValue !== null && !dojo.isObject(possibleValue) && regexp){
13483 if(possibleValue.toString().match(regexp)){
13484 return true; // Boolean
13485 }
13486 }else if(value === possibleValue){
13487 return true; // Boolean
13488 }
13489 });
13490 },
13491
13492 isItem: function(/* anything */ something){
13493 // summary:
13494 // See dojo.data.api.Read.isItem()
13495 if(something && something[this._storeRefPropName] === this){
13496 if(this._arrayOfAllItems[something[this._itemNumPropName]] === something){
13497 return true;
13498 }
13499 }
13500 return false; // Boolean
13501 },
13502
13503 isItemLoaded: function(/* anything */ something){
13504 // summary:
13505 // See dojo.data.api.Read.isItemLoaded()
13506 return this.isItem(something); //boolean
13507 },
13508
13509 loadItem: function(/* object */ keywordArgs){
13510 // summary:
13511 // See dojo.data.api.Read.loadItem()
13512 this._assertIsItem(keywordArgs.item);
13513 },
13514
13515 getFeatures: function(){
13516 // summary:
13517 // See dojo.data.api.Read.getFeatures()
13518 return this._features; //Object
13519 },
13520
13521 getLabel: function(/* item */ item){
13522 // summary:
13523 // See dojo.data.api.Read.getLabel()
13524 if(this._labelAttr && this.isItem(item)){
13525 return this.getValue(item,this._labelAttr); //String
13526 }
13527 return undefined; //undefined
13528 },
13529
13530 getLabelAttributes: function(/* item */ item){
13531 // summary:
13532 // See dojo.data.api.Read.getLabelAttributes()
13533 if(this._labelAttr){
13534 return [this._labelAttr]; //array
13535 }
13536 return null; //null
13537 },
13538
13539 _fetchItems: function( /* Object */ keywordArgs,
13540 /* Function */ findCallback,
13541 /* Function */ errorCallback){
13542 // summary:
13543 // See dojo.data.util.simpleFetch.fetch()
13544 var self = this,
13545 filter = function(requestArgs, arrayOfItems){
13546 var items = [],
13547 i, key;
13548 if(requestArgs.query){
13549 var value,
13550 ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
13551
13552 //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
13553 //same value for each item examined. Much more efficient.
13554 var regexpList = {};
13555 for(key in requestArgs.query){
13556 value = requestArgs.query[key];
13557 if(typeof value === "string"){
13558 regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
13559 }else if(value instanceof RegExp){
13560 regexpList[key] = value;
13561 }
13562 }
13563 for(i = 0; i < arrayOfItems.length; ++i){
13564 var match = true;
13565 var candidateItem = arrayOfItems[i];
13566 if(candidateItem === null){
13567 match = false;
13568 }else{
13569 for(key in requestArgs.query){
13570 value = requestArgs.query[key];
13571 if(!self._containsValue(candidateItem, key, value, regexpList[key])){
13572 match = false;
13573 }
13574 }
13575 }
13576 if(match){
13577 items.push(candidateItem);
13578 }
13579 }
13580 findCallback(items, requestArgs);
13581 }else{
13582 // We want a copy to pass back in case the parent wishes to sort the array.
13583 // We shouldn't allow resort of the internal list, so that multiple callers
13584 // can get lists and sort without affecting each other. We also need to
13585 // filter out any null values that have been left as a result of deleteItem()
13586 // calls in ItemFileWriteStore.
13587 for(i = 0; i < arrayOfItems.length; ++i){
13588 var item = arrayOfItems[i];
13589 if(item !== null){
13590 items.push(item);
13591 }
13592 }
13593 findCallback(items, requestArgs);
13594 }
13595 };
13596
13597 if(this._loadFinished){
13598 filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
13599 }else{
13600 //Do a check on the JsonFileUrl and crosscheck it.
13601 //If it doesn't match the cross-check, it needs to be updated
13602 //This allows for either url or _jsonFileUrl to he changed to
13603 //reset the store load location. Done this way for backwards
13604 //compatibility. People use _jsonFileUrl (even though officially
13605 //private.
13606 if(this._jsonFileUrl !== this._ccUrl){
13607 dojo.deprecated("dojo.data.ItemFileReadStore: ",
13608 "To change the url, set the url property of the store," +
13609 " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
13610 this._ccUrl = this._jsonFileUrl;
13611 this.url = this._jsonFileUrl;
13612 }else if(this.url !== this._ccUrl){
13613 this._jsonFileUrl = this.url;
13614 this._ccUrl = this.url;
13615 }
13616
13617 //See if there was any forced reset of data.
13618 if(this.data != null && this._jsonData == null){
13619 this._jsonData = this.data;
13620 this.data = null;
13621 }
13622
13623 if(this._jsonFileUrl){
13624 //If fetches come in before the loading has finished, but while
13625 //a load is in progress, we have to defer the fetching to be
13626 //invoked in the callback.
13627 if(this._loadInProgress){
13628 this._queuedFetches.push({args: keywordArgs, filter: filter});
13629 }else{
13630 this._loadInProgress = true;
13631 var getArgs = {
13632 url: self._jsonFileUrl,
13633 handleAs: "json-comment-optional",
13634 preventCache: this.urlPreventCache,
13635 failOk: this.failOk
13636 };
13637 var getHandler = dojo.xhrGet(getArgs);
13638 getHandler.addCallback(function(data){
13639 try{
13640 self._getItemsFromLoadedData(data);
13641 self._loadFinished = true;
13642 self._loadInProgress = false;
13643
13644 filter(keywordArgs, self._getItemsArray(keywordArgs.queryOptions));
13645 self._handleQueuedFetches();
13646 }catch(e){
13647 self._loadFinished = true;
13648 self._loadInProgress = false;
13649 errorCallback(e, keywordArgs);
13650 }
13651 });
13652 getHandler.addErrback(function(error){
13653 self._loadInProgress = false;
13654 errorCallback(error, keywordArgs);
13655 });
13656
13657 //Wire up the cancel to abort of the request
13658 //This call cancel on the deferred if it hasn't been called
13659 //yet and then will chain to the simple abort of the
13660 //simpleFetch keywordArgs
13661 var oldAbort = null;
13662 if(keywordArgs.abort){
13663 oldAbort = keywordArgs.abort;
13664 }
13665 keywordArgs.abort = function(){
13666 var df = getHandler;
13667 if(df && df.fired === -1){
13668 df.cancel();
13669 df = null;
13670 }
13671 if(oldAbort){
13672 oldAbort.call(keywordArgs);
13673 }
13674 };
13675 }
13676 }else if(this._jsonData){
13677 try{
13678 this._loadFinished = true;
13679 this._getItemsFromLoadedData(this._jsonData);
13680 this._jsonData = null;
13681 filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
13682 }catch(e){
13683 errorCallback(e, keywordArgs);
13684 }
13685 }else{
13686 errorCallback(new Error("dojo.data.ItemFileReadStore: No JSON source data was provided as either URL or a nested Javascript object."), keywordArgs);
13687 }
13688 }
13689 },
13690
13691 _handleQueuedFetches: function(){
13692 // summary:
13693 // Internal function to execute delayed request in the store.
13694 //Execute any deferred fetches now.
13695 if(this._queuedFetches.length > 0){
13696 for(var i = 0; i < this._queuedFetches.length; i++){
13697 var fData = this._queuedFetches[i],
13698 delayedQuery = fData.args,
13699 delayedFilter = fData.filter;
13700 if(delayedFilter){
13701 delayedFilter(delayedQuery, this._getItemsArray(delayedQuery.queryOptions));
13702 }else{
13703 this.fetchItemByIdentity(delayedQuery);
13704 }
13705 }
13706 this._queuedFetches = [];
13707 }
13708 },
13709
13710 _getItemsArray: function(/*object?*/queryOptions){
13711 // summary:
13712 // Internal function to determine which list of items to search over.
13713 // queryOptions: The query options parameter, if any.
13714 if(queryOptions && queryOptions.deep){
13715 return this._arrayOfAllItems;
13716 }
13717 return this._arrayOfTopLevelItems;
13718 },
13719
13720 close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
13721 // summary:
13722 // See dojo.data.api.Read.close()
13723 if(this.clearOnClose &&
13724 this._loadFinished &&
13725 !this._loadInProgress){
13726 //Reset all internalsback to default state. This will force a reload
13727 //on next fetch. This also checks that the data or url param was set
13728 //so that the store knows it can get data. Without one of those being set,
13729 //the next fetch will trigger an error.
13730
13731 if(((this._jsonFileUrl == "" || this._jsonFileUrl == null) &&
13732 (this.url == "" || this.url == null)
13733 ) && this.data == null){
13734 console.debug("dojo.data.ItemFileReadStore: WARNING! Data reload " +
13735 " information has not been provided." +
13736 " Please set 'url' or 'data' to the appropriate value before" +
13737 " the next fetch");
13738 }
13739 this._arrayOfAllItems = [];
13740 this._arrayOfTopLevelItems = [];
13741 this._loadFinished = false;
13742 this._itemsByIdentity = null;
13743 this._loadInProgress = false;
13744 this._queuedFetches = [];
13745 }
13746 },
13747
13748 _getItemsFromLoadedData: function(/* Object */ dataObject){
13749 // summary:
13750 // Function to parse the loaded data into item format and build the internal items array.
13751 // description:
13752 // Function to parse the loaded data into item format and build the internal items array.
13753 //
13754 // dataObject:
13755 // The JS data object containing the raw data to convery into item format.
13756 //
13757 // returns: array
13758 // Array of items in store item format.
13759
13760 // First, we define a couple little utility functions...
13761 var addingArrays = false,
13762 self = this;
13763
13764 function valueIsAnItem(/* anything */ aValue){
13765 // summary:
13766 // Given any sort of value that could be in the raw json data,
13767 // return true if we should interpret the value as being an
13768 // item itself, rather than a literal value or a reference.
13769 // example:
13770 // | false == valueIsAnItem("Kermit");
13771 // | false == valueIsAnItem(42);
13772 // | false == valueIsAnItem(new Date());
13773 // | false == valueIsAnItem({_type:'Date', _value:'May 14, 1802'});
13774 // | false == valueIsAnItem({_reference:'Kermit'});
13775 // | true == valueIsAnItem({name:'Kermit', color:'green'});
13776 // | true == valueIsAnItem({iggy:'pop'});
13777 // | true == valueIsAnItem({foo:42});
13778 var isItem = (
13779 (aValue !== null) &&
13780 (typeof aValue === "object") &&
13781 (!dojo.isArray(aValue) || addingArrays) &&
13782 (!dojo.isFunction(aValue)) &&
13783 (aValue.constructor == Object || dojo.isArray(aValue)) &&
13784 (typeof aValue._reference === "undefined") &&
13785 (typeof aValue._type === "undefined") &&
13786 (typeof aValue._value === "undefined") &&
13787 self.hierarchical
13788 );
13789 return isItem;
13790 }
13791
13792 function addItemAndSubItemsToArrayOfAllItems(/* Item */ anItem){
13793 self._arrayOfAllItems.push(anItem);
13794 for(var attribute in anItem){
13795 var valueForAttribute = anItem[attribute];
13796 if(valueForAttribute){
13797 if(dojo.isArray(valueForAttribute)){
13798 var valueArray = valueForAttribute;
13799 for(var k = 0; k < valueArray.length; ++k){
13800 var singleValue = valueArray[k];
13801 if(valueIsAnItem(singleValue)){
13802 addItemAndSubItemsToArrayOfAllItems(singleValue);
13803 }
13804 }
13805 }else{
13806 if(valueIsAnItem(valueForAttribute)){
13807 addItemAndSubItemsToArrayOfAllItems(valueForAttribute);
13808 }
13809 }
13810 }
13811 }
13812 }
13813
13814 this._labelAttr = dataObject.label;
13815
13816 // We need to do some transformations to convert the data structure
13817 // that we read from the file into a format that will be convenient
13818 // to work with in memory.
13819
13820 // Step 1: Walk through the object hierarchy and build a list of all items
13821 var i,
13822 item;
13823 this._arrayOfAllItems = [];
13824 this._arrayOfTopLevelItems = dataObject.items;
13825
13826 for(i = 0; i < this._arrayOfTopLevelItems.length; ++i){
13827 item = this._arrayOfTopLevelItems[i];
13828 if(dojo.isArray(item)){
13829 addingArrays = true;
13830 }
13831 addItemAndSubItemsToArrayOfAllItems(item);
13832 item[this._rootItemPropName]=true;
13833 }
13834
13835 // Step 2: Walk through all the attribute values of all the items,
13836 // and replace single values with arrays. For example, we change this:
13837 // { name:'Miss Piggy', pets:'Foo-Foo'}
13838 // into this:
13839 // { name:['Miss Piggy'], pets:['Foo-Foo']}
13840 //
13841 // We also store the attribute names so we can validate our store
13842 // reference and item id special properties for the O(1) isItem
13843 var allAttributeNames = {},
13844 key;
13845
13846 for(i = 0; i < this._arrayOfAllItems.length; ++i){
13847 item = this._arrayOfAllItems[i];
13848 for(key in item){
13849 if(key !== this._rootItemPropName){
13850 var value = item[key];
13851 if(value !== null){
13852 if(!dojo.isArray(value)){
13853 item[key] = [value];
13854 }
13855 }else{
13856 item[key] = [null];
13857 }
13858 }
13859 allAttributeNames[key]=key;
13860 }
13861 }
13862
13863 // Step 3: Build unique property names to use for the _storeRefPropName and _itemNumPropName
13864 // This should go really fast, it will generally never even run the loop.
13865 while(allAttributeNames[this._storeRefPropName]){
13866 this._storeRefPropName += "_";
13867 }
13868 while(allAttributeNames[this._itemNumPropName]){
13869 this._itemNumPropName += "_";
13870 }
13871 while(allAttributeNames[this._reverseRefMap]){
13872 this._reverseRefMap += "_";
13873 }
13874
13875 // Step 4: Some data files specify an optional 'identifier', which is
13876 // the name of an attribute that holds the identity of each item.
13877 // If this data file specified an identifier attribute, then build a
13878 // hash table of items keyed by the identity of the items.
13879 var arrayOfValues;
13880
13881 var identifier = dataObject.identifier;
13882 if(identifier){
13883 this._itemsByIdentity = {};
13884 this._features['dojo.data.api.Identity'] = identifier;
13885 for(i = 0; i < this._arrayOfAllItems.length; ++i){
13886 item = this._arrayOfAllItems[i];
13887 arrayOfValues = item[identifier];
13888 var identity = arrayOfValues[0];
13889 if(!this._itemsByIdentity[identity]){
13890 this._itemsByIdentity[identity] = item;
13891 }else{
13892 if(this._jsonFileUrl){
13893 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 + "]");
13894 }else if(this._jsonData){
13895 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 + "]");
13896 }
13897 }
13898 }
13899 }else{
13900 this._features['dojo.data.api.Identity'] = Number;
13901 }
13902
13903 // Step 5: Walk through all the items, and set each item's properties
13904 // for _storeRefPropName and _itemNumPropName, so that store.isItem() will return true.
13905 for(i = 0; i < this._arrayOfAllItems.length; ++i){
13906 item = this._arrayOfAllItems[i];
13907 item[this._storeRefPropName] = this;
13908 item[this._itemNumPropName] = i;
13909 }
13910
13911 // Step 6: We walk through all the attribute values of all the items,
13912 // looking for type/value literals and item-references.
13913 //
13914 // We replace item-references with pointers to items. For example, we change:
13915 // { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
13916 // into this:
13917 // { name:['Kermit'], friends:[miss_piggy] }
13918 // (where miss_piggy is the object representing the 'Miss Piggy' item).
13919 //
13920 // We replace type/value pairs with typed-literals. For example, we change:
13921 // { name:['Nelson Mandela'], born:[{_type:'Date', _value:'July 18, 1918'}] }
13922 // into this:
13923 // { name:['Kermit'], born:(new Date('July 18, 1918')) }
13924 //
13925 // We also generate the associate map for all items for the O(1) isItem function.
13926 for(i = 0; i < this._arrayOfAllItems.length; ++i){
13927 item = this._arrayOfAllItems[i]; // example: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
13928 for(key in item){
13929 arrayOfValues = item[key]; // example: [{_reference:{name:'Miss Piggy'}}]
13930 for(var j = 0; j < arrayOfValues.length; ++j){
13931 value = arrayOfValues[j]; // example: {_reference:{name:'Miss Piggy'}}
13932 if(value !== null && typeof value == "object"){
13933 if(("_type" in value) && ("_value" in value)){
13934 var type = value._type; // examples: 'Date', 'Color', or 'ComplexNumber'
13935 var mappingObj = this._datatypeMap[type]; // examples: Date, dojo.Color, foo.math.ComplexNumber, {type: dojo.Color, deserialize(value){ return new dojo.Color(value)}}
13936 if(!mappingObj){
13937 throw new Error("dojo.data.ItemFileReadStore: in the typeMap constructor arg, no object class was specified for the datatype '" + type + "'");
13938 }else if(dojo.isFunction(mappingObj)){
13939 arrayOfValues[j] = new mappingObj(value._value);
13940 }else if(dojo.isFunction(mappingObj.deserialize)){
13941 arrayOfValues[j] = mappingObj.deserialize(value._value);
13942 }else{
13943 throw new Error("dojo.data.ItemFileReadStore: Value provided in typeMap was neither a constructor, nor a an object with a deserialize function");
13944 }
13945 }
13946 if(value._reference){
13947 var referenceDescription = value._reference; // example: {name:'Miss Piggy'}
13948 if(!dojo.isObject(referenceDescription)){
13949 // example: 'Miss Piggy'
13950 // from an item like: { name:['Kermit'], friends:[{_reference:'Miss Piggy'}]}
13951 arrayOfValues[j] = this._getItemByIdentity(referenceDescription);
13952 }else{
13953 // example: {name:'Miss Piggy'}
13954 // from an item like: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
13955 for(var k = 0; k < this._arrayOfAllItems.length; ++k){
13956 var candidateItem = this._arrayOfAllItems[k],
13957 found = true;
13958 for(var refKey in referenceDescription){
13959 if(candidateItem[refKey] != referenceDescription[refKey]){
13960 found = false;
13961 }
13962 }
13963 if(found){
13964 arrayOfValues[j] = candidateItem;
13965 }
13966 }
13967 }
13968 if(this.referenceIntegrity){
13969 var refItem = arrayOfValues[j];
13970 if(this.isItem(refItem)){
13971 this._addReferenceToMap(refItem, item, key);
13972 }
13973 }
13974 }else if(this.isItem(value)){
13975 //It's a child item (not one referenced through _reference).
13976 //We need to treat this as a referenced item, so it can be cleaned up
13977 //in a write store easily.
13978 if(this.referenceIntegrity){
13979 this._addReferenceToMap(value, item, key);
13980 }
13981 }
13982 }
13983 }
13984 }
13985 }
13986 },
13987
13988 _addReferenceToMap: function(/*item*/ refItem, /*item*/ parentItem, /*string*/ attribute){
13989 // summary:
13990 // Method to add an reference map entry for an item and attribute.
13991 // description:
13992 // Method to add an reference map entry for an item and attribute. //
13993 // refItem:
13994 // The item that is referenced.
13995 // parentItem:
13996 // The item that holds the new reference to refItem.
13997 // attribute:
13998 // The attribute on parentItem that contains the new reference.
13999
14000 //Stub function, does nothing. Real processing is in ItemFileWriteStore.
14001 },
14002
14003 getIdentity: function(/* item */ item){
14004 // summary:
14005 // See dojo.data.api.Identity.getIdentity()
14006 var identifier = this._features['dojo.data.api.Identity'];
14007 if(identifier === Number){
14008 return item[this._itemNumPropName]; // Number
14009 }else{
14010 var arrayOfValues = item[identifier];
14011 if(arrayOfValues){
14012 return arrayOfValues[0]; // Object || String
14013 }
14014 }
14015 return null; // null
14016 },
14017
14018 fetchItemByIdentity: function(/* Object */ keywordArgs){
14019 // summary:
14020 // See dojo.data.api.Identity.fetchItemByIdentity()
14021
14022 // Hasn't loaded yet, we have to trigger the load.
14023 var item,
14024 scope;
14025 if(!this._loadFinished){
14026 var self = this;
14027 //Do a check on the JsonFileUrl and crosscheck it.
14028 //If it doesn't match the cross-check, it needs to be updated
14029 //This allows for either url or _jsonFileUrl to he changed to
14030 //reset the store load location. Done this way for backwards
14031 //compatibility. People use _jsonFileUrl (even though officially
14032 //private.
14033 if(this._jsonFileUrl !== this._ccUrl){
14034 dojo.deprecated("dojo.data.ItemFileReadStore: ",
14035 "To change the url, set the url property of the store," +
14036 " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
14037 this._ccUrl = this._jsonFileUrl;
14038 this.url = this._jsonFileUrl;
14039 }else if(this.url !== this._ccUrl){
14040 this._jsonFileUrl = this.url;
14041 this._ccUrl = this.url;
14042 }
14043
14044 //See if there was any forced reset of data.
14045 if(this.data != null && this._jsonData == null){
14046 this._jsonData = this.data;
14047 this.data = null;
14048 }
14049
14050 if(this._jsonFileUrl){
14051
14052 if(this._loadInProgress){
14053 this._queuedFetches.push({args: keywordArgs});
14054 }else{
14055 this._loadInProgress = true;
14056 var getArgs = {
14057 url: self._jsonFileUrl,
14058 handleAs: "json-comment-optional",
14059 preventCache: this.urlPreventCache,
14060 failOk: this.failOk
14061 };
14062 var getHandler = dojo.xhrGet(getArgs);
14063 getHandler.addCallback(function(data){
14064 var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
14065 try{
14066 self._getItemsFromLoadedData(data);
14067 self._loadFinished = true;
14068 self._loadInProgress = false;
14069 item = self._getItemByIdentity(keywordArgs.identity);
14070 if(keywordArgs.onItem){
14071 keywordArgs.onItem.call(scope, item);
14072 }
14073 self._handleQueuedFetches();
14074 }catch(error){
14075 self._loadInProgress = false;
14076 if(keywordArgs.onError){
14077 keywordArgs.onError.call(scope, error);
14078 }
14079 }
14080 });
14081 getHandler.addErrback(function(error){
14082 self._loadInProgress = false;
14083 if(keywordArgs.onError){
14084 var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
14085 keywordArgs.onError.call(scope, error);
14086 }
14087 });
14088 }
14089
14090 }else if(this._jsonData){
14091 // Passed in data, no need to xhr.
14092 self._getItemsFromLoadedData(self._jsonData);
14093 self._jsonData = null;
14094 self._loadFinished = true;
14095 item = self._getItemByIdentity(keywordArgs.identity);
14096 if(keywordArgs.onItem){
14097 scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
14098 keywordArgs.onItem.call(scope, item);
14099 }
14100 }
14101 }else{
14102 // Already loaded. We can just look it up and call back.
14103 item = this._getItemByIdentity(keywordArgs.identity);
14104 if(keywordArgs.onItem){
14105 scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
14106 keywordArgs.onItem.call(scope, item);
14107 }
14108 }
14109 },
14110
14111 _getItemByIdentity: function(/* Object */ identity){
14112 // summary:
14113 // Internal function to look an item up by its identity map.
14114 var item = null;
14115 if(this._itemsByIdentity){
14116 item = this._itemsByIdentity[identity];
14117 }else{
14118 item = this._arrayOfAllItems[identity];
14119 }
14120 if(item === undefined){
14121 item = null;
14122 }
14123 return item; // Object
14124 },
14125
14126 getIdentityAttributes: function(/* item */ item){
14127 // summary:
14128 // See dojo.data.api.Identity.getIdentifierAttributes()
14129
14130 var identifier = this._features['dojo.data.api.Identity'];
14131 if(identifier === Number){
14132 // If (identifier === Number) it means getIdentity() just returns
14133 // an integer item-number for each item. The dojo.data.api.Identity
14134 // spec says we need to return null if the identity is not composed
14135 // of attributes
14136 return null; // null
14137 }else{
14138 return [identifier]; // Array
14139 }
14140 },
14141
14142 _forceLoad: function(){
14143 // summary:
14144 // Internal function to force a load of the store if it hasn't occurred yet. This is required
14145 // for specific functions to work properly.
14146 var self = this;
14147 //Do a check on the JsonFileUrl and crosscheck it.
14148 //If it doesn't match the cross-check, it needs to be updated
14149 //This allows for either url or _jsonFileUrl to he changed to
14150 //reset the store load location. Done this way for backwards
14151 //compatibility. People use _jsonFileUrl (even though officially
14152 //private.
14153 if(this._jsonFileUrl !== this._ccUrl){
14154 dojo.deprecated("dojo.data.ItemFileReadStore: ",
14155 "To change the url, set the url property of the store," +
14156 " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
14157 this._ccUrl = this._jsonFileUrl;
14158 this.url = this._jsonFileUrl;
14159 }else if(this.url !== this._ccUrl){
14160 this._jsonFileUrl = this.url;
14161 this._ccUrl = this.url;
14162 }
14163
14164 //See if there was any forced reset of data.
14165 if(this.data != null && this._jsonData == null){
14166 this._jsonData = this.data;
14167 this.data = null;
14168 }
14169
14170 if(this._jsonFileUrl){
14171 var getArgs = {
14172 url: this._jsonFileUrl,
14173 handleAs: "json-comment-optional",
14174 preventCache: this.urlPreventCache,
14175 failOk: this.failOk,
14176 sync: true
14177 };
14178 var getHandler = dojo.xhrGet(getArgs);
14179 getHandler.addCallback(function(data){
14180 try{
14181 //Check to be sure there wasn't another load going on concurrently
14182 //So we don't clobber data that comes in on it. If there is a load going on
14183 //then do not save this data. It will potentially clobber current data.
14184 //We mainly wanted to sync/wait here.
14185 //TODO: Revisit the loading scheme of this store to improve multi-initial
14186 //request handling.
14187 if(self._loadInProgress !== true && !self._loadFinished){
14188 self._getItemsFromLoadedData(data);
14189 self._loadFinished = true;
14190 }else if(self._loadInProgress){
14191 //Okay, we hit an error state we can't recover from. A forced load occurred
14192 //while an async load was occurring. Since we cannot block at this point, the best
14193 //that can be managed is to throw an error.
14194 throw new Error("dojo.data.ItemFileReadStore: Unable to perform a synchronous load, an async load is in progress.");
14195 }
14196 }catch(e){
14197 console.log(e);
14198 throw e;
14199 }
14200 });
14201 getHandler.addErrback(function(error){
14202 throw error;
14203 });
14204 }else if(this._jsonData){
14205 self._getItemsFromLoadedData(self._jsonData);
14206 self._jsonData = null;
14207 self._loadFinished = true;
14208 }
14209 }
14210});
14211//Mix in the simple fetch implementation to this class.
14212dojo.extend(dojo.data.ItemFileReadStore,dojo.data.util.simpleFetch);
14213
14214}
14215
14216if(!dojo._hasResource["dijit._editor.plugins.FontChoice"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
14217dojo._hasResource["dijit._editor.plugins.FontChoice"] = true;
14218dojo.provide("dijit._editor.plugins.FontChoice");
14219
14220
14221
14222
14223
14224
14225
14226
14227
14228
14229dojo.declare("dijit._editor.plugins._FontDropDown",
14230 [dijit._Widget, dijit._Templated],{
14231 // summary:
14232 // Base class for widgets that contains a label (like "Font:")
14233 // and a FilteringSelect drop down to pick a value.
14234 // Used as Toolbar entry.
14235
14236 // label: [public] String
14237 // The label to apply to this particular FontDropDown.
14238 label: "",
14239
14240 // widgetsInTemplate: [public] boolean
14241 // Over-ride denoting the template has widgets to parse.
14242 widgetsInTemplate: true,
14243
14244 // plainText: [public] boolean
14245 // Flag to indicate that the returned label should be plain text
14246 // instead of an example.
14247 plainText: false,
14248
14249 // templateString: [public] String
14250 // The template used to construct the labeled dropdown.
14251 templateString:
14252 "<span style='white-space: nowrap' class='dijit dijitReset dijitInline'>" +
14253 "<label class='dijitLeft dijitInline' for='${selectId}'>${label}</label>" +
14254 "<input dojoType='dijit.form.FilteringSelect' required=false labelType=html labelAttr=label searchAttr=name " +
14255 "tabIndex='-1' id='${selectId}' dojoAttachPoint='select' value=''/>" +
14256 "</span>",
14257
14258 postMixInProperties: function(){
14259 // summary:
14260 // Over-ride to misin specific properties.
14261 this.inherited(arguments);
14262
14263 this.strings = dojo.i18n.getLocalization("dijit._editor", "FontChoice");
14264
14265 // Set some substitution variables used in the template
14266 this.label = this.strings[this.command];
14267 this.id = dijit.getUniqueId(this.declaredClass.replace(/\./g,"_"));
14268 this.selectId = this.id + "_select";
14269
14270 this.inherited(arguments);
14271 },
14272
14273 postCreate: function(){
14274 // summary:
14275 // Over-ride for the default postCreate action
14276 // This establishes the filtering selects and the like.
14277
14278 // Initialize the list of items in the drop down by creating data store with items like:
14279 // {value: 1, name: "xx-small", label: "<font size=1>xx-small</font-size>" }
14280 var items = dojo.map(this.values, function(value){
14281 var name = this.strings[value] || value;
14282 return {
14283 label: this.getLabel(value, name),
14284 name: name,
14285 value: value
14286 };
14287 }, this);
14288
14289 this.select.store = new dojo.data.ItemFileReadStore({
14290 data: {
14291 identifier: "value",
14292 items: items
14293 }
14294 });
14295
14296 this.select.set("value", "", false);
14297 this.disabled = this.select.get("disabled");
14298 },
14299
14300 _setValueAttr: function(value, priorityChange){
14301 // summary:
14302 // Over-ride for the default action of setting the
14303 // widget value, maps the input to known values
14304 // value: Object|String
14305 // The value to set in the select.
14306 // priorityChange:
14307 // Optional parameter used to tell the select whether or not to fire
14308 // onChange event.
14309
14310 //if the value is not a permitted value, just set empty string to prevent showing the warning icon
14311 priorityChange = priorityChange !== false?true:false;
14312 this.select.set('value', dojo.indexOf(this.values,value) < 0 ? "" : value, priorityChange);
14313 if(!priorityChange){
14314 // Clear the last state in case of updateState calls. Ref: #10466
14315 this.select._lastValueReported=null;
14316 }
14317 },
14318
14319 _getValueAttr: function(){
14320 // summary:
14321 // Allow retreiving the value from the composite select on
14322 // call to button.get("value");
14323 return this.select.get('value');
14324 },
14325
14326 focus: function(){
14327 // summary:
14328 // Over-ride for focus control of this widget. Delegates focus down to the
14329 // filtering select.
14330 this.select.focus();
14331 },
14332
14333 _setDisabledAttr: function(value){
14334 // summary:
14335 // Over-ride for the button's 'disabled' attribute so that it can be
14336 // disabled programmatically.
14337
14338 // Save off ths disabled state so the get retrieves it correctly
14339 //without needing to have a function proxy it.
14340 this.disabled = value;
14341 this.select.set("disabled", value);
14342 }
14343});
14344
14345
14346dojo.declare("dijit._editor.plugins._FontNameDropDown", dijit._editor.plugins._FontDropDown, {
14347 // summary:
14348 // Dropdown to select a font; goes in editor toolbar.
14349
14350 // generic: Boolean
14351 // Use generic (web standard) font names
14352 generic: false,
14353
14354 // command: [public] String
14355 // The editor 'command' implemented by this plugin.
14356 command: "fontName",
14357
14358 postMixInProperties: function(){
14359 // summary:
14360 // Over-ride for the default posr mixin control
14361 if(!this.values){
14362 this.values = this.generic ?
14363 ["serif", "sans-serif", "monospace", "cursive", "fantasy"] : // CSS font-family generics
14364 ["Arial", "Times New Roman", "Comic Sans MS", "Courier New"];
14365 }
14366 this.inherited(arguments);
14367 },
14368
14369 getLabel: function(value, name){
14370 // summary:
14371 // Function used to generate the labels of the format dropdown
14372 // will return a formatted, or plain label based on the value
14373 // of the plainText option.
14374 // value: String
14375 // The 'insert value' associated with a name
14376 // name: String
14377 // The text name of the value
14378 if(this.plainText){
14379 return name;
14380 }else{
14381 return "<div style='font-family: "+value+"'>" + name + "</div>";
14382 }
14383 },
14384
14385 _setValueAttr: function(value, priorityChange){
14386 // summary:
14387 // Over-ride for the default action of setting the
14388 // widget value, maps the input to known values
14389
14390 priorityChange = priorityChange !== false?true:false;
14391 if(this.generic){
14392 var map = {
14393 "Arial": "sans-serif",
14394 "Helvetica": "sans-serif",
14395 "Myriad": "sans-serif",
14396 "Times": "serif",
14397 "Times New Roman": "serif",
14398 "Comic Sans MS": "cursive",
14399 "Apple Chancery": "cursive",
14400 "Courier": "monospace",
14401 "Courier New": "monospace",
14402 "Papyrus": "fantasy"
14403// ,"????": "fantasy" TODO: IE doesn't map fantasy font-family?
14404 };
14405 value = map[value] || value;
14406 }
14407 this.inherited(arguments, [value, priorityChange]);
14408 }
14409});
14410
14411dojo.declare("dijit._editor.plugins._FontSizeDropDown", dijit._editor.plugins._FontDropDown, {
14412 // summary:
14413 // Dropdown to select a font size; goes in editor toolbar.
14414
14415 // command: [public] String
14416 // The editor 'command' implemented by this plugin.
14417 command: "fontSize",
14418
14419 // values: [public] Number[]
14420 // The HTML font size values supported by this plugin
14421 values: [1,2,3,4,5,6,7], // sizes according to the old HTML FONT SIZE
14422
14423 getLabel: function(value, name){
14424 // summary:
14425 // Function used to generate the labels of the format dropdown
14426 // will return a formatted, or plain label based on the value
14427 // of the plainText option.
14428 // We're stuck using the deprecated FONT tag to correspond
14429 // with the size measurements used by the editor
14430 // value: String
14431 // The 'insert value' associated with a name
14432 // name: String
14433 // The text name of the value
14434 if(this.plainText){
14435 return name;
14436 }else{
14437 return "<font size=" + value + "'>" + name + "</font>";
14438 }
14439 },
14440
14441 _setValueAttr: function(value, priorityChange){
14442 // summary:
14443 // Over-ride for the default action of setting the
14444 // widget value, maps the input to known values
14445 priorityChange = priorityChange !== false?true:false;
14446 if(value.indexOf && value.indexOf("px") != -1){
14447 var pixels = parseInt(value, 10);
14448 value = {10:1, 13:2, 16:3, 18:4, 24:5, 32:6, 48:7}[pixels] || value;
14449 }
14450
14451 this.inherited(arguments, [value, priorityChange]);
14452 }
14453});
14454
14455
14456dojo.declare("dijit._editor.plugins._FormatBlockDropDown", dijit._editor.plugins._FontDropDown, {
14457 // summary:
14458 // Dropdown to select a format (like paragraph or heading); goes in editor toolbar.
14459
14460 // command: [public] String
14461 // The editor 'command' implemented by this plugin.
14462 command: "formatBlock",
14463
14464 // values: [public] Array
14465 // The HTML format tags supported by this plugin
14466 values: ["noFormat", "p", "h1", "h2", "h3", "pre"],
14467
14468 postCreate: function(){
14469 // Init and set the default value to no formatting. Update state will adjust it
14470 // as needed.
14471 this.inherited(arguments);
14472 this.set("value", "noFormat", false);
14473 },
14474
14475 getLabel: function(value, name){
14476 // summary:
14477 // Function used to generate the labels of the format dropdown
14478 // will return a formatted, or plain label based on the value
14479 // of the plainText option.
14480 // value: String
14481 // The 'insert value' associated with a name
14482 // name: String
14483 // The text name of the value
14484 if(this.plainText){
14485 return name;
14486 }else{
14487 return "<" + value + ">" + name + "</" + value + ">";
14488 }
14489 },
14490
14491 _execCommand: function(editor, command, choice){
14492 // summary:
14493 // Over-ride for default exec-command label.
14494 // Allows us to treat 'none' as special.
14495 if(choice === "noFormat"){
14496 var start;
14497 var end;
14498 var sel = dijit.range.getSelection(editor.window);
14499 if(sel && sel.rangeCount > 0){
14500 var range = sel.getRangeAt(0);
14501 var node, tag;
14502 if(range){
14503 start = range.startContainer;
14504 end = range.endContainer;
14505
14506 // find containing nodes of start/end.
14507 while(start && start !== editor.editNode &&
14508 start !== editor.document.body &&
14509 start.nodeType !== 1){
14510 start = start.parentNode;
14511 }
14512
14513 while(end && end !== editor.editNode &&
14514 end !== editor.document.body &&
14515 end.nodeType !== 1){
14516 end = end.parentNode;
14517 }
14518
14519 var processChildren = dojo.hitch(this, function(node, array){
14520 if(node.childNodes && node.childNodes.length){
14521 var i;
14522 for(i = 0; i < node.childNodes.length; i++){
14523 var c = node.childNodes[i];
14524 if(c.nodeType == 1){
14525 if(dojo.withGlobal(editor.window, "inSelection", dijit._editor.selection, [c])){
14526 var tag = c.tagName? c.tagName.toLowerCase(): "";
14527 if(dojo.indexOf(this.values, tag) !== -1){
14528 array.push(c);
14529 }
14530 processChildren(c,array);
14531 }
14532 }
14533 }
14534 }
14535 });
14536
14537 var unformatNodes = dojo.hitch(this, function(nodes){
14538 // summary:
14539 // Internal function to clear format nodes.
14540 // nodes:
14541 // The array of nodes to strip formatting from.
14542 if(nodes && nodes.length){
14543 editor.beginEditing();
14544 while(nodes.length){
14545 this._removeFormat(editor, nodes.pop());
14546 }
14547 editor.endEditing();
14548 }
14549 });
14550
14551 var clearNodes = [];
14552 if(start == end){
14553 //Contained within the same block, may be collapsed, but who cares, see if we
14554 // have a block element to remove.
14555 var block;
14556 node = start;
14557 while(node && node !== editor.editNode && node !== editor.document.body){
14558 if(node.nodeType == 1){
14559 tag = node.tagName? node.tagName.toLowerCase(): "";
14560 if(dojo.indexOf(this.values, tag) !== -1){
14561 block = node;
14562 break;
14563 }
14564 }
14565 node = node.parentNode;
14566 }
14567
14568 //Also look for all child nodes in the selection that may need to be
14569 //cleared of formatting
14570 processChildren(start, clearNodes);
14571 if(block) { clearNodes = [block].concat(clearNodes); }
14572 unformatNodes(clearNodes);
14573 }else{
14574 // Probably a multi select, so we have to process it. Whee.
14575 node = start;
14576 while(dojo.withGlobal(editor.window, "inSelection", dijit._editor.selection, [node])){
14577 if(node.nodeType == 1){
14578 tag = node.tagName? node.tagName.toLowerCase(): "";
14579 if(dojo.indexOf(this.values, tag) !== -1){
14580 clearNodes.push(node);
14581 }
14582 processChildren(node,clearNodes);
14583 }
14584 node = node.nextSibling;
14585 }
14586 unformatNodes(clearNodes);
14587 }
14588 editor.onDisplayChanged();
14589 }
14590 }
14591 }else{
14592 editor.execCommand(command, choice);
14593 }
14594 },
14595
14596 _removeFormat: function(editor, node){
14597 // summary:
14598 // function to remove the block format node.
14599 // node:
14600 // The block format node to remove (and leave the contents behind)
14601 if(editor.customUndo){
14602 // So of course IE doesn't work right with paste-overs.
14603 // We have to do this manually, which is okay since IE already uses
14604 // customUndo and we turned it on for WebKit. WebKit pasted funny,
14605 // so couldn't use the execCommand approach
14606 while(node.firstChild){
14607 dojo.place(node.firstChild, node, "before");
14608 }
14609 node.parentNode.removeChild(node);
14610 }else{
14611 // Everyone else works fine this way, a paste-over and is native
14612 // undo friendly.
14613 dojo.withGlobal(editor.window,
14614 "selectElementChildren", dijit._editor.selection, [node]);
14615 var html = dojo.withGlobal(editor.window,
14616 "getSelectedHtml", dijit._editor.selection, [null]);
14617 dojo.withGlobal(editor.window,
14618 "selectElement", dijit._editor.selection, [node]);
14619 editor.execCommand("inserthtml", html||"");
14620 }
14621 }
14622});
14623
14624// TODO: for 2.0, split into FontChoice plugin into three separate classes,
14625// one for each command (and change registry below)
14626dojo.declare("dijit._editor.plugins.FontChoice", dijit._editor._Plugin,{
14627 // summary:
14628 // This plugin provides three drop downs for setting style in the editor
14629 // (font, font size, and format block), as controlled by command.
14630 //
14631 // description:
14632 // The commands provided by this plugin are:
14633 //
14634 // * fontName
14635 // | Provides a drop down to select from a list of font names
14636 // * fontSize
14637 // | Provides a drop down to select from a list of font sizes
14638 // * formatBlock
14639 // | Provides a drop down to select from a list of block styles
14640 // |
14641 //
14642 // which can easily be added to an editor by including one or more of the above commands
14643 // in the `plugins` attribute as follows:
14644 //
14645 // | plugins="['fontName','fontSize',...]"
14646 //
14647 // It is possible to override the default dropdown list by providing an Array for the `custom` property when
14648 // instantiating this plugin, e.g.
14649 //
14650 // | plugins="[{name:'dijit._editor.plugins.FontChoice', command:'fontName', custom:['Verdana','Myriad','Garamond']},...]"
14651 //
14652 // Alternatively, for `fontName` only, `generic:true` may be specified to provide a dropdown with
14653 // [CSS generic font families](http://www.w3.org/TR/REC-CSS2/fonts.html#generic-font-families)
14654 //
14655 // Note that the editor is often unable to properly handle font styling information defined outside
14656 // the context of the current editor instance, such as pre-populated HTML.
14657
14658 // useDefaultCommand: [protected] booleam
14659 // Override _Plugin.useDefaultCommand...
14660 // processing is handled by this plugin, not by dijit.Editor.
14661 useDefaultCommand: false,
14662
14663 _initButton: function(){
14664 // summary:
14665 // Overrides _Plugin._initButton(), to initialize the FilteringSelect+label in toolbar,
14666 // rather than a simple button.
14667 // tags:
14668 // protected
14669
14670 // Create the widget to go into the toolbar (the so-called "button")
14671 var clazz = {
14672 fontName: dijit._editor.plugins._FontNameDropDown,
14673 fontSize: dijit._editor.plugins._FontSizeDropDown,
14674 formatBlock: dijit._editor.plugins._FormatBlockDropDown
14675 }[this.command],
14676 params = this.params;
14677
14678 // For back-compat reasons support setting custom values via "custom" parameter
14679 // rather than "values" parameter
14680 if(this.params.custom){
14681 params.values = this.params.custom;
14682 }
14683
14684 var editor = this.editor;
14685 this.button = new clazz(dojo.delegate({dir: editor.dir, lang: editor.lang}, params));
14686
14687 // Reflect changes to the drop down in the editor
14688 this.connect(this.button.select, "onChange", function(choice){
14689 // User invoked change, since all internal updates set priorityChange to false and will
14690 // not trigger an onChange event.
14691 this.editor.focus();
14692
14693 if(this.command == "fontName" && choice.indexOf(" ") != -1){ choice = "'" + choice + "'"; }
14694
14695 // Invoke, the editor already normalizes commands called through its
14696 // execCommand.
14697 if(this.button._execCommand){
14698 this.button._execCommand(this.editor, this.command, choice);
14699 }else{
14700 this.editor.execCommand(this.command, choice);
14701 }
14702
14703 // Enable custom undo for webkit, needed for noFormat to work properly
14704 // and still undo.
14705 this.editor.customUndo = this.editor.customUndo || dojo.isWebKit;
14706 });
14707 },
14708
14709 updateState: function(){
14710 // summary:
14711 // Overrides _Plugin.updateState(). This controls updating the menu
14712 // options to the right values on state changes in the document (that trigger a
14713 // test of the actions.)
14714 // It set value of drop down in toolbar to reflect font/font size/format block
14715 // of text at current caret position.
14716 // tags:
14717 // protected
14718 var _e = this.editor;
14719 var _c = this.command;
14720 if(!_e || !_e.isLoaded || !_c.length){ return; }
14721 if(this.button){
14722 var value;
14723 try{
14724 value = _e.queryCommandValue(_c) || "";
14725 }catch(e){
14726 //Firefox may throw error above if the editor is just loaded, ignore it
14727 value = "";
14728 }
14729
14730 // strip off single quotes, if any
14731 var quoted = dojo.isString(value) && value.match(/'([^']*)'/);
14732 if(quoted){ value = quoted[1]; }
14733
14734 if(_c === "formatBlock"){
14735 if(!value || value == "p"){
14736 // Some browsers (WebKit) doesn't actually get the tag info right.
14737 // and IE returns paragraph when in a DIV!, so incorrect a lot,
14738 // so we have double-check it.
14739 value = null;
14740 var elem;
14741 // Try to find the current element where the caret is.
14742 var sel = dijit.range.getSelection(this.editor.window);
14743 if(sel && sel.rangeCount > 0){
14744 var range = sel.getRangeAt(0);
14745 if(range){
14746 elem = range.endContainer;
14747 }
14748 }
14749
14750 // Okay, now see if we can find one of the formatting types we're in.
14751 while(elem && elem !== _e.editNode && elem !== _e.document){
14752 var tg = elem.tagName?elem.tagName.toLowerCase():"";
14753 if(tg && dojo.indexOf(this.button.values, tg) > -1){
14754 value = tg;
14755 break;
14756 }
14757 elem = elem.parentNode;
14758 }
14759 if(!value){
14760 // Still no value, so lets select 'none'.
14761 value = "noFormat";
14762 }
14763 }else{
14764 // Check that the block format is one allowed, if not,
14765 // null it so that it gets set to empty.
14766 if(dojo.indexOf(this.button.values, value) < 0){
14767 value = "noFormat";
14768 }
14769 }
14770 }
14771 if(value !== this.button.get("value")){
14772 // Set the value, but denote it is not a priority change, so no
14773 // onchange fires.
14774 this.button.set('value', value, false);
14775 }
14776 }
14777 }
14778});
14779
14780// Register this plugin.
14781dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){
14782 if(o.plugin){ return; }
14783 switch(o.args.name){
14784 case "fontName": case "fontSize": case "formatBlock":
14785 o.plugin = new dijit._editor.plugins.FontChoice({
14786 command: o.args.name,
14787 plainText: o.args.plainText?o.args.plainText:false
14788 });
14789 }
14790});
14791
14792}
14793
14794if(!dojo._hasResource["dijit.form._FormSelectWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
14795dojo._hasResource["dijit.form._FormSelectWidget"] = true;
14796dojo.provide("dijit.form._FormSelectWidget");
14797
14798
14799
14800
14801/*=====
14802dijit.form.__SelectOption = function(){
14803 // value: String
14804 // The value of the option. Setting to empty (or missing) will
14805 // place a separator at that location
14806 // label: String
14807 // The label for our option. It can contain html tags.
14808 // selected: Boolean
14809 // Whether or not we are a selected option
14810 // disabled: Boolean
14811 // Whether or not this specific option is disabled
14812 this.value = value;
14813 this.label = label;
14814 this.selected = selected;
14815 this.disabled = disabled;
14816}
14817=====*/
14818
14819dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
14820 // summary:
14821 // Extends _FormValueWidget in order to provide "select-specific"
14822 // values - i.e., those values that are unique to <select> elements.
14823 // This also provides the mechanism for reading the elements from
14824 // a store, if desired.
14825
14826 // multiple: Boolean
14827 // Whether or not we are multi-valued
14828 multiple: false,
14829
14830 // options: dijit.form.__SelectOption[]
14831 // The set of options for our select item. Roughly corresponds to
14832 // the html <option> tag.
14833 options: null,
14834
14835 // store: dojo.data.api.Identity
14836 // A store which, at the very least impelements dojo.data.api.Identity
14837 // to use for getting our list of options - rather than reading them
14838 // from the <option> html tags.
14839 store: null,
14840
14841 // query: object
14842 // A query to use when fetching items from our store
14843 query: null,
14844
14845 // queryOptions: object
14846 // Query options to use when fetching from the store
14847 queryOptions: null,
14848
14849 // onFetch: Function
14850 // A callback to do with an onFetch - but before any items are actually
14851 // iterated over (i.e. to filter even futher what you want to add)
14852 onFetch: null,
14853
14854 // sortByLabel: boolean
14855 // Flag to sort the options returned from a store by the label of
14856 // the store.
14857 sortByLabel: true,
14858
14859
14860 // loadChildrenOnOpen: boolean
14861 // By default loadChildren is called when the items are fetched from the
14862 // store. This property allows delaying loadChildren (and the creation
14863 // of the options/menuitems) until the user opens the click the button.
14864 // dropdown
14865 loadChildrenOnOpen: false,
14866
14867 getOptions: function(/* anything */ valueOrIdx){
14868 // summary:
14869 // Returns a given option (or options).
14870 // valueOrIdx:
14871 // If passed in as a string, that string is used to look up the option
14872 // in the array of options - based on the value property.
14873 // (See dijit.form.__SelectOption).
14874 //
14875 // If passed in a number, then the option with the given index (0-based)
14876 // within this select will be returned.
14877 //
14878 // If passed in a dijit.form.__SelectOption, the same option will be
14879 // returned if and only if it exists within this select.
14880 //
14881 // If passed an array, then an array will be returned with each element
14882 // in the array being looked up.
14883 //
14884 // If not passed a value, then all options will be returned
14885 //
14886 // returns:
14887 // The option corresponding with the given value or index. null
14888 // is returned if any of the following are true:
14889 // - A string value is passed in which doesn't exist
14890 // - An index is passed in which is outside the bounds of the array of options
14891 // - A dijit.form.__SelectOption is passed in which is not a part of the select
14892
14893 // NOTE: the compare for passing in a dijit.form.__SelectOption checks
14894 // if the value property matches - NOT if the exact option exists
14895 // NOTE: if passing in an array, null elements will be placed in the returned
14896 // array when a value is not found.
14897 var lookupValue = valueOrIdx, opts = this.options || [], l = opts.length;
14898
14899 if(lookupValue === undefined){
14900 return opts; // dijit.form.__SelectOption[]
14901 }
14902 if(dojo.isArray(lookupValue)){
14903 return dojo.map(lookupValue, "return this.getOptions(item);", this); // dijit.form.__SelectOption[]
14904 }
14905 if(dojo.isObject(valueOrIdx)){
14906 // We were passed an option - so see if it's in our array (directly),
14907 // and if it's not, try and find it by value.
14908 if(!dojo.some(this.options, function(o, idx){
14909 if(o === lookupValue ||
14910 (o.value && o.value === lookupValue.value)){
14911 lookupValue = idx;
14912 return true;
14913 }
14914 return false;
14915 })){
14916 lookupValue = -1;
14917 }
14918 }
14919 if(typeof lookupValue == "string"){
14920 for(var i=0; i<l; i++){
14921 if(opts[i].value === lookupValue){
14922 lookupValue = i;
14923 break;
14924 }
14925 }
14926 }
14927 if(typeof lookupValue == "number" && lookupValue >= 0 && lookupValue < l){
14928 return this.options[lookupValue] // dijit.form.__SelectOption
14929 }
14930 return null; // null
14931 },
14932
14933 addOption: function(/* dijit.form.__SelectOption, dijit.form.__SelectOption[] */ option){
14934 // summary:
14935 // Adds an option or options to the end of the select. If value
14936 // of the option is empty or missing, a separator is created instead.
14937 // Passing in an array of options will yield slightly better performance
14938 // since the children are only loaded once.
14939 if(!dojo.isArray(option)){ option = [option]; }
14940 dojo.forEach(option, function(i){
14941 if(i && dojo.isObject(i)){
14942 this.options.push(i);
14943 }
14944 }, this);
14945 this._loadChildren();
14946 },
14947
14948 removeOption: function(/* string, dijit.form.__SelectOption, number, or array */ valueOrIdx){
14949 // summary:
14950 // Removes the given option or options. You can remove by string
14951 // (in which case the value is removed), number (in which case the
14952 // index in the options array is removed), or select option (in
14953 // which case, the select option with a matching value is removed).
14954 // You can also pass in an array of those values for a slightly
14955 // better performance since the children are only loaded once.
14956 if(!dojo.isArray(valueOrIdx)){ valueOrIdx = [valueOrIdx]; }
14957 var oldOpts = this.getOptions(valueOrIdx);
14958 dojo.forEach(oldOpts, function(i){
14959 // We can get null back in our array - if our option was not found. In
14960 // that case, we don't want to blow up...
14961 if(i){
14962 this.options = dojo.filter(this.options, function(node, idx){
14963 return (node.value !== i.value);
14964 });
14965 this._removeOptionItem(i);
14966 }
14967 }, this);
14968 this._loadChildren();
14969 },
14970
14971 updateOption: function(/* dijit.form.__SelectOption, dijit.form.__SelectOption[] */ newOption){
14972 // summary:
14973 // Updates the values of the given option. The option to update
14974 // is matched based on the value of the entered option. Passing
14975 // in an array of new options will yeild better performance since
14976 // the children will only be loaded once.
14977 if(!dojo.isArray(newOption)){ newOption = [newOption]; }
14978 dojo.forEach(newOption, function(i){
14979 var oldOpt = this.getOptions(i), k;
14980 if(oldOpt){
14981 for(k in i){ oldOpt[k] = i[k]; }
14982 }
14983 }, this);
14984 this._loadChildren();
14985 },
14986
14987 setStore: function(/* dojo.data.api.Identity */ store,
14988 /* anything? */ selectedValue,
14989 /* Object? */ fetchArgs){
14990 // summary:
14991 // Sets the store you would like to use with this select widget.
14992 // The selected value is the value of the new store to set. This
14993 // function returns the original store, in case you want to reuse
14994 // it or something.
14995 // store: dojo.data.api.Identity
14996 // The store you would like to use - it MUST implement Identity,
14997 // and MAY implement Notification.
14998 // selectedValue: anything?
14999 // The value that this widget should set itself to *after* the store
15000 // has been loaded
15001 // fetchArgs: Object?
15002 // The arguments that will be passed to the store's fetch() function
15003 var oStore = this.store;
15004 fetchArgs = fetchArgs || {};
15005 if(oStore !== store){
15006 // Our store has changed, so update our notifications
15007 dojo.forEach(this._notifyConnections || [], dojo.disconnect);
15008 delete this._notifyConnections;
15009 if(store && store.getFeatures()["dojo.data.api.Notification"]){
15010 this._notifyConnections = [
15011 dojo.connect(store, "onNew", this, "_onNewItem"),
15012 dojo.connect(store, "onDelete", this, "_onDeleteItem"),
15013 dojo.connect(store, "onSet", this, "_onSetItem")
15014 ];
15015 }
15016 this.store = store;
15017 }
15018
15019 // Turn off change notifications while we make all these changes
15020 this._onChangeActive = false;
15021
15022 // Remove existing options (if there are any)
15023 if(this.options && this.options.length){
15024 this.removeOption(this.options);
15025 }
15026
15027 // Add our new options
15028 if(store){
15029 var cb = function(items){
15030 if(this.sortByLabel && !fetchArgs.sort && items.length){
15031 items.sort(dojo.data.util.sorter.createSortFunction([{
15032 attribute: store.getLabelAttributes(items[0])[0]
15033 }], store));
15034 }
15035
15036 if(fetchArgs.onFetch){
15037 items = fetchArgs.onFetch(items);
15038 }
15039 // TODO: Add these guys as a batch, instead of separately
15040 dojo.forEach(items, function(i){
15041 this._addOptionForItem(i);
15042 }, this);
15043
15044 // Set our value (which might be undefined), and then tweak
15045 // it to send a change event with the real value
15046 this._loadingStore = false;
15047 this.set("value", (("_pendingValue" in this) ? this._pendingValue : selectedValue));
15048 delete this._pendingValue;
15049
15050 if(!this.loadChildrenOnOpen){
15051 this._loadChildren();
15052 }else{
15053 this._pseudoLoadChildren(items);
15054 }
15055 this._fetchedWith = opts;
15056 this._lastValueReported = this.multiple ? [] : null;
15057 this._onChangeActive = true;
15058 this.onSetStore();
15059 this._handleOnChange(this.value);
15060 };
15061 var opts = dojo.mixin({onComplete:cb, scope: this}, fetchArgs);
15062 this._loadingStore = true;
15063 store.fetch(opts);
15064 }else{
15065 delete this._fetchedWith;
15066 }
15067 return oStore; // dojo.data.api.Identity
15068 },
15069
15070 _setValueAttr: function(/*anything*/ newValue, /*Boolean, optional*/ priorityChange){
15071 // summary:
15072 // set the value of the widget.
15073 // If a string is passed, then we set our value from looking it up.
15074 if(this._loadingStore){
15075 // Our store is loading - so save our value, and we'll set it when
15076 // we're done
15077 this._pendingValue = newValue;
15078 return;
15079 }
15080 var opts = this.getOptions() || [];
15081 if(!dojo.isArray(newValue)){
15082 newValue = [newValue];
15083 }
15084 dojo.forEach(newValue, function(i, idx){
15085 if(!dojo.isObject(i)){
15086 i = i + "";
15087 }
15088 if(typeof i === "string"){
15089 newValue[idx] = dojo.filter(opts, function(node){
15090 return node.value === i;
15091 })[0] || {value: "", label: ""};
15092 }
15093 }, this);
15094
15095 // Make sure some sane default is set
15096 newValue = dojo.filter(newValue, function(i){ return i && i.value; });
15097 if(!this.multiple && (!newValue[0] || !newValue[0].value) && opts.length){
15098 newValue[0] = opts[0];
15099 }
15100 dojo.forEach(opts, function(i){
15101 i.selected = dojo.some(newValue, function(v){ return v.value === i.value; });
15102 });
15103 var val = dojo.map(newValue, function(i){ return i.value; }),
15104 disp = dojo.map(newValue, function(i){ return i.label; });
15105
15106 this.value = this.multiple ? val : val[0];
15107 this._setDisplay(this.multiple ? disp : disp[0]);
15108 this._updateSelection();
15109 this._handleOnChange(this.value, priorityChange);
15110 },
15111
15112 _getDisplayedValueAttr: function(){
15113 // summary:
15114 // returns the displayed value of the widget
15115 var val = this.get("value");
15116 if(!dojo.isArray(val)){
15117 val = [val];
15118 }
15119 var ret = dojo.map(this.getOptions(val), function(v){
15120 if(v && "label" in v){
15121 return v.label;
15122 }else if(v){
15123 return v.value;
15124 }
15125 return null;
15126 }, this);
15127 return this.multiple ? ret : ret[0];
15128 },
15129
15130 _getValueDeprecated: false, // remove when _FormWidget:getValue is removed
15131 getValue: function(){
15132 // summary:
15133 // get the value of the widget.
15134 return this._lastValue;
15135 },
15136
15137 undo: function(){
15138 // summary:
15139 // restore the value to the last value passed to onChange
15140 this._setValueAttr(this._lastValueReported, false);
15141 },
15142
15143 _loadChildren: function(){
15144 // summary:
15145 // Loads the children represented by this widget's options.
15146 // reset the menu to make it "populatable on the next click
15147 if(this._loadingStore){ return; }
15148 dojo.forEach(this._getChildren(), function(child){
15149 child.destroyRecursive();
15150 });
15151 // Add each menu item
15152 dojo.forEach(this.options, this._addOptionItem, this);
15153
15154 // Update states
15155 this._updateSelection();
15156 },
15157
15158 _updateSelection: function(){
15159 // summary:
15160 // Sets the "selected" class on the item for styling purposes
15161 this.value = this._getValueFromOpts();
15162 var val = this.value;
15163 if(!dojo.isArray(val)){
15164 val = [val];
15165 }
15166 if(val && val[0]){
15167 dojo.forEach(this._getChildren(), function(child){
15168 var isSelected = dojo.some(val, function(v){
15169 return child.option && (v === child.option.value);
15170 });
15171 dojo.toggleClass(child.domNode, this.baseClass + "SelectedOption", isSelected);
15172 dijit.setWaiState(child.domNode, "selected", isSelected);
15173 }, this);
15174 }
15175 this._handleOnChange(this.value);
15176 },
15177
15178 _getValueFromOpts: function(){
15179 // summary:
15180 // Returns the value of the widget by reading the options for
15181 // the selected flag
15182 var opts = this.getOptions() || [];
15183 if(!this.multiple && opts.length){
15184 // Mirror what a select does - choose the first one
15185 var opt = dojo.filter(opts, function(i){
15186 return i.selected;
15187 })[0];
15188 if(opt && opt.value){
15189 return opt.value
15190 }else{
15191 opts[0].selected = true;
15192 return opts[0].value;
15193 }
15194 }else if(this.multiple){
15195 // Set value to be the sum of all selected
15196 return dojo.map(dojo.filter(opts, function(i){
15197 return i.selected;
15198 }), function(i){
15199 return i.value;
15200 }) || [];
15201 }
15202 return "";
15203 },
15204
15205 // Internal functions to call when we have store notifications come in
15206 _onNewItem: function(/* item */ item, /* Object? */ parentInfo){
15207 if(!parentInfo || !parentInfo.parent){
15208 // Only add it if we are top-level
15209 this._addOptionForItem(item);
15210 }
15211 },
15212 _onDeleteItem: function(/* item */ item){
15213 var store = this.store;
15214 this.removeOption(store.getIdentity(item));
15215 },
15216 _onSetItem: function(/* item */ item){
15217 this.updateOption(this._getOptionObjForItem(item));
15218 },
15219
15220 _getOptionObjForItem: function(item){
15221 // summary:
15222 // Returns an option object based off the given item. The "value"
15223 // of the option item will be the identity of the item, the "label"
15224 // of the option will be the label of the item. If the item contains
15225 // children, the children value of the item will be set
15226 var store = this.store, label = store.getLabel(item),
15227 value = (label ? store.getIdentity(item) : null);
15228 return {value: value, label: label, item:item}; // dijit.form.__SelectOption
15229 },
15230
15231 _addOptionForItem: function(/* item */ item){
15232 // summary:
15233 // Creates (and adds) the option for the given item
15234 var store = this.store;
15235 if(!store.isItemLoaded(item)){
15236 // We are not loaded - so let's load it and add later
15237 store.loadItem({item: item, onComplete: function(i){
15238 this._addOptionForItem(item);
15239 },
15240 scope: this});
15241 return;
15242 }
15243 var newOpt = this._getOptionObjForItem(item);
15244 this.addOption(newOpt);
15245 },
15246
15247 constructor: function(/* Object */ keywordArgs){
15248 // summary:
15249 // Saves off our value, if we have an initial one set so we
15250 // can use it if we have a store as well (see startup())
15251 this._oValue = (keywordArgs || {}).value || null;
15252 },
15253
15254 _fillContent: function(){
15255 // summary:
15256 // Loads our options and sets up our dropdown correctly. We
15257 // don't want any content, so we don't call any inherit chain
15258 // function.
15259 var opts = this.options;
15260 if(!opts){
15261 opts = this.options = this.srcNodeRef ? dojo.query(">",
15262 this.srcNodeRef).map(function(node){
15263 if(node.getAttribute("type") === "separator"){
15264 return { value: "", label: "", selected: false, disabled: false };
15265 }
15266 return { value: node.getAttribute("value"),
15267 label: String(node.innerHTML),
15268 selected: node.getAttribute("selected") || false,
15269 disabled: node.getAttribute("disabled") || false };
15270 }, this) : [];
15271 }
15272 if(!this.value){
15273 this.value = this._getValueFromOpts();
15274 }else if(this.multiple && typeof this.value == "string"){
15275 this.value = this.value.split(",");
15276 }
15277 },
15278
15279 postCreate: function(){
15280 // summary:
15281 // sets up our event handling that we need for functioning
15282 // as a select
15283 dojo.setSelectable(this.focusNode, false);
15284 this.inherited(arguments);
15285
15286 // Make our event connections for updating state
15287 this.connect(this, "onChange", "_updateSelection");
15288 this.connect(this, "startup", "_loadChildren");
15289
15290 this._setValueAttr(this.value, null);
15291 },
15292
15293 startup: function(){
15294 // summary:
15295 // Connects in our store, if we have one defined
15296 this.inherited(arguments);
15297 var store = this.store, fetchArgs = {};
15298 dojo.forEach(["query", "queryOptions", "onFetch"], function(i){
15299 if(this[i]){
15300 fetchArgs[i] = this[i];
15301 }
15302 delete this[i];
15303 }, this);
15304 if(store && store.getFeatures()["dojo.data.api.Identity"]){
15305 // Temporarily set our store to null so that it will get set
15306 // and connected appropriately
15307 this.store = null;
15308 this.setStore(store, this._oValue, fetchArgs);
15309 }
15310 },
15311
15312 destroy: function(){
15313 // summary:
15314 // Clean up our connections
15315 dojo.forEach(this._notifyConnections || [], dojo.disconnect);
15316 this.inherited(arguments);
15317 },
15318
15319 _addOptionItem: function(/* dijit.form.__SelectOption */ option){
15320 // summary:
15321 // User-overridable function which, for the given option, adds an
15322 // item to the select. If the option doesn't have a value, then a
15323 // separator is added in that place. Make sure to store the option
15324 // in the created option widget.
15325 },
15326
15327 _removeOptionItem: function(/* dijit.form.__SelectOption */ option){
15328 // summary:
15329 // User-overridable function which, for the given option, removes
15330 // its item from the select.
15331 },
15332
15333 _setDisplay: function(/*String or String[]*/ newDisplay){
15334 // summary:
15335 // Overridable function which will set the display for the
15336 // widget. newDisplay is either a string (in the case of
15337 // single selects) or array of strings (in the case of multi-selects)
15338 },
15339
15340 _getChildren: function(){
15341 // summary:
15342 // Overridable function to return the children that this widget contains.
15343 return [];
15344 },
15345
15346 _getSelectedOptionsAttr: function(){
15347 // summary:
15348 // hooks into this.attr to provide a mechanism for getting the
15349 // option items for the current value of the widget.
15350 return this.getOptions(this.get("value"));
15351 },
15352
15353 _pseudoLoadChildren: function(/* item[] */ items){
15354 // summary:
15355 // a function that will "fake" loading children, if needed, and
15356 // if we have set to not load children until the widget opens.
15357 // items:
15358 // An array of items that will be loaded, when needed
15359 },
15360
15361 onSetStore: function(){
15362 // summary:
15363 // a function that can be connected to in order to receive a
15364 // notification that the store has finished loading and all options
15365 // from that store are available
15366 }
15367});
15368
15369}
15370
15371if(!dojo._hasResource["dijit.MenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15372dojo._hasResource["dijit.MenuItem"] = true;
15373dojo.provide("dijit.MenuItem");
15374
15375
15376
15377
15378
15379
15380dojo.declare("dijit.MenuItem",
15381 [dijit._Widget, dijit._Templated, dijit._Contained, dijit._CssStateMixin],
15382 {
15383 // summary:
15384 // A line item in a Menu Widget
15385
15386 // Make 3 columns
15387 // icon, label, and expand arrow (BiDi-dependent) indicating sub-menu
15388 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"),
15389
15390 attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
15391 label: { node: "containerNode", type: "innerHTML" },
15392 iconClass: { node: "iconNode", type: "class" }
15393 }),
15394
15395 baseClass: "dijitMenuItem",
15396
15397 // label: String
15398 // Menu text
15399 label: '',
15400
15401 // iconClass: String
15402 // Class to apply to DOMNode to make it display an icon.
15403 iconClass: "",
15404
15405 // accelKey: String
15406 // Text for the accelerator (shortcut) key combination.
15407 // Note that although Menu can display accelerator keys there
15408 // is no infrastructure to actually catch and execute these
15409 // accelerators.
15410 accelKey: "",
15411
15412 // disabled: Boolean
15413 // If true, the menu item is disabled.
15414 // If false, the menu item is enabled.
15415 disabled: false,
15416
15417 _fillContent: function(/*DomNode*/ source){
15418 // If button label is specified as srcNodeRef.innerHTML rather than
15419 // this.params.label, handle it here.
15420 if(source && !("label" in this.params)){
15421 this.set('label', source.innerHTML);
15422 }
15423 },
15424
15425 postCreate: function(){
15426 this.inherited(arguments);
15427 dojo.setSelectable(this.domNode, false);
15428 var label = this.id+"_text";
15429 dojo.attr(this.containerNode, "id", label);
15430 if(this.accelKeyNode){
15431 dojo.attr(this.accelKeyNode, "id", this.id + "_accel");
15432 label += " " + this.id + "_accel";
15433 }
15434 dijit.setWaiState(this.domNode, "labelledby", label);
15435 },
15436
15437 _onHover: function(){
15438 // summary:
15439 // Handler when mouse is moved onto menu item
15440 // tags:
15441 // protected
15442 this.getParent().onItemHover(this);
15443 },
15444
15445 _onUnhover: function(){
15446 // summary:
15447 // Handler when mouse is moved off of menu item,
15448 // possibly to a child menu, or maybe to a sibling
15449 // menuitem or somewhere else entirely.
15450 // tags:
15451 // protected
15452
15453 // if we are unhovering the currently selected item
15454 // then unselect it
15455 this.getParent().onItemUnhover(this);
15456
15457 // _onUnhover() is called when the menu is hidden (collapsed), due to clicking
15458 // a MenuItem and having it execut. When that happens, FF and IE don't generate
15459 // an onmouseout event for the MenuItem, so give _CssStateMixin some help
15460 this._hovering = false;
15461 this._setStateClass();
15462 },
15463
15464 _onClick: function(evt){
15465 // summary:
15466 // Internal handler for click events on MenuItem.
15467 // tags:
15468 // private
15469 this.getParent().onItemClick(this, evt);
15470 dojo.stopEvent(evt);
15471 },
15472
15473 onClick: function(/*Event*/ evt){
15474 // summary:
15475 // User defined function to handle clicks
15476 // tags:
15477 // callback
15478 },
15479
15480 focus: function(){
15481 // summary:
15482 // Focus on this MenuItem
15483 try{
15484 if(dojo.isIE == 8){
15485 // needed for IE8 which won't scroll TR tags into view on focus yet calling scrollIntoView creates flicker (#10275)
15486 this.containerNode.focus();
15487 }
15488 dijit.focus(this.focusNode);
15489 }catch(e){
15490 // this throws on IE (at least) in some scenarios
15491 }
15492 },
15493
15494 _onFocus: function(){
15495 // summary:
15496 // This is called by the focus manager when focus
15497 // goes to this MenuItem or a child menu.
15498 // tags:
15499 // protected
15500 this._setSelected(true);
15501 this.getParent()._onItemFocus(this);
15502
15503 this.inherited(arguments);
15504 },
15505
15506 _setSelected: function(selected){
15507 // summary:
15508 // Indicate that this node is the currently selected one
15509 // tags:
15510 // private
15511
15512 /***
15513 * TODO: remove this method and calls to it, when _onBlur() is working for MenuItem.
15514 * Currently _onBlur() gets called when focus is moved from the MenuItem to a child menu.
15515 * That's not supposed to happen, but the problem is:
15516 * In order to allow dijit.popup's getTopPopup() to work,a sub menu's popupParent
15517 * points to the parent Menu, bypassing the parent MenuItem... thus the
15518 * MenuItem is not in the chain of active widgets and gets a premature call to
15519 * _onBlur()
15520 */
15521
15522 dojo.toggleClass(this.domNode, "dijitMenuItemSelected", selected);
15523 },
15524
15525 setLabel: function(/*String*/ content){
15526 // summary:
15527 // Deprecated. Use set('label', ...) instead.
15528 // tags:
15529 // deprecated
15530 dojo.deprecated("dijit.MenuItem.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
15531 this.set("label", content);
15532 },
15533
15534 setDisabled: function(/*Boolean*/ disabled){
15535 // summary:
15536 // Deprecated. Use set('disabled', bool) instead.
15537 // tags:
15538 // deprecated
15539 dojo.deprecated("dijit.Menu.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
15540 this.set('disabled', disabled);
15541 },
15542 _setDisabledAttr: function(/*Boolean*/ value){
15543 // summary:
15544 // Hook for attr('disabled', ...) to work.
15545 // Enable or disable this menu item.
15546 this.disabled = value;
15547 dijit.setWaiState(this.focusNode, 'disabled', value ? 'true' : 'false');
15548 },
15549 _setAccelKeyAttr: function(/*String*/ value){
15550 // summary:
15551 // Hook for attr('accelKey', ...) to work.
15552 // Set accelKey on this menu item.
15553 this.accelKey=value;
15554
15555 this.accelKeyNode.style.display=value?"":"none";
15556 this.accelKeyNode.innerHTML=value;
15557 //have to use colSpan to make it work in IE
15558 dojo.attr(this.containerNode,'colSpan',value?"1":"2");
15559 }
15560 });
15561
15562}
15563
15564if(!dojo._hasResource["dijit.PopupMenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15565dojo._hasResource["dijit.PopupMenuItem"] = true;
15566dojo.provide("dijit.PopupMenuItem");
15567
15568
15569
15570dojo.declare("dijit.PopupMenuItem",
15571 dijit.MenuItem,
15572 {
15573 _fillContent: function(){
15574 // summary:
15575 // When Menu is declared in markup, this code gets the menu label and
15576 // the popup widget from the srcNodeRef.
15577 // description:
15578 // srcNodeRefinnerHTML contains both the menu item text and a popup widget
15579 // The first part holds the menu item text and the second part is the popup
15580 // example:
15581 // | <div dojoType="dijit.PopupMenuItem">
15582 // | <span>pick me</span>
15583 // | <popup> ... </popup>
15584 // | </div>
15585 // tags:
15586 // protected
15587
15588 if(this.srcNodeRef){
15589 var nodes = dojo.query("*", this.srcNodeRef);
15590 dijit.PopupMenuItem.superclass._fillContent.call(this, nodes[0]);
15591
15592 // save pointer to srcNode so we can grab the drop down widget after it's instantiated
15593 this.dropDownContainer = this.srcNodeRef;
15594 }
15595 },
15596
15597 startup: function(){
15598 if(this._started){ return; }
15599 this.inherited(arguments);
15600
15601 // we didn't copy the dropdown widget from the this.srcNodeRef, so it's in no-man's
15602 // land now. move it to dojo.doc.body.
15603 if(!this.popup){
15604 var node = dojo.query("[widgetId]", this.dropDownContainer)[0];
15605 this.popup = dijit.byNode(node);
15606 }
15607 dojo.body().appendChild(this.popup.domNode);
15608 this.popup.startup();
15609
15610 this.popup.domNode.style.display="none";
15611 if(this.arrowWrapper){
15612 dojo.style(this.arrowWrapper, "visibility", "");
15613 }
15614 dijit.setWaiState(this.focusNode, "haspopup", "true");
15615 },
15616
15617 destroyDescendants: function(){
15618 if(this.popup){
15619 // Destroy the popup, unless it's already been destroyed. This can happen because
15620 // the popup is a direct child of <body> even though it's logically my child.
15621 if(!this.popup._destroyed){
15622 this.popup.destroyRecursive();
15623 }
15624 delete this.popup;
15625 }
15626 this.inherited(arguments);
15627 }
15628 });
15629
15630
15631}
15632
15633if(!dojo._hasResource["dijit.CheckedMenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15634dojo._hasResource["dijit.CheckedMenuItem"] = true;
15635dojo.provide("dijit.CheckedMenuItem");
15636
15637
15638
15639dojo.declare("dijit.CheckedMenuItem",
15640 dijit.MenuItem,
15641 {
15642 // summary:
15643 // A checkbox-like menu item for toggling on and off
15644
15645 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\">&#10003;</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\">&nbsp;</td>\n</tr>\n"),
15646
15647 // checked: Boolean
15648 // Our checked state
15649 checked: false,
15650 _setCheckedAttr: function(/*Boolean*/ checked){
15651 // summary:
15652 // Hook so attr('checked', bool) works.
15653 // Sets the class and state for the check box.
15654 dojo.toggleClass(this.domNode, "dijitCheckedMenuItemChecked", checked);
15655 dijit.setWaiState(this.domNode, "checked", checked);
15656 this.checked = checked;
15657 },
15658
15659 onChange: function(/*Boolean*/ checked){
15660 // summary:
15661 // User defined function to handle check/uncheck events
15662 // tags:
15663 // callback
15664 },
15665
15666 _onClick: function(/*Event*/ e){
15667 // summary:
15668 // Clicking this item just toggles its state
15669 // tags:
15670 // private
15671 if(!this.disabled){
15672 this.set("checked", !this.checked);
15673 this.onChange(this.checked);
15674 }
15675 this.inherited(arguments);
15676 }
15677 });
15678
15679}
15680
15681if(!dojo._hasResource["dijit.MenuSeparator"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15682dojo._hasResource["dijit.MenuSeparator"] = true;
15683dojo.provide("dijit.MenuSeparator");
15684
15685
15686
15687
15688
15689dojo.declare("dijit.MenuSeparator",
15690 [dijit._Widget, dijit._Templated, dijit._Contained],
15691 {
15692 // summary:
15693 // A line between two menu items
15694
15695 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"),
15696
15697 postCreate: function(){
15698 dojo.setSelectable(this.domNode, false);
15699 },
15700
15701 isFocusable: function(){
15702 // summary:
15703 // Override to always return false
15704 // tags:
15705 // protected
15706
15707 return false; // Boolean
15708 }
15709 });
15710
15711
15712}
15713
15714if(!dojo._hasResource["dijit.Menu"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15715dojo._hasResource["dijit.Menu"] = true;
15716dojo.provide("dijit.Menu");
15717
15718
15719
15720
15721
15722
15723
15724dojo.declare("dijit._MenuBase",
15725 [dijit._Widget, dijit._Templated, dijit._KeyNavContainer],
15726{
15727 // summary:
15728 // Base class for Menu and MenuBar
15729
15730 // parentMenu: [readonly] Widget
15731 // pointer to menu that displayed me
15732 parentMenu: null,
15733
15734 // popupDelay: Integer
15735 // number of milliseconds before hovering (without clicking) causes the popup to automatically open.
15736 popupDelay: 500,
15737
15738 startup: function(){
15739 if(this._started){ return; }
15740
15741 dojo.forEach(this.getChildren(), function(child){ child.startup(); });
15742 this.startupKeyNavChildren();
15743
15744 this.inherited(arguments);
15745 },
15746
15747 onExecute: function(){
15748 // summary:
15749 // Attach point for notification about when a menu item has been executed.
15750 // This is an internal mechanism used for Menus to signal to their parent to
15751 // close them, because they are about to execute the onClick handler. In
15752 // general developers should not attach to or override this method.
15753 // tags:
15754 // protected
15755 },
15756
15757 onCancel: function(/*Boolean*/ closeAll){
15758 // summary:
15759 // Attach point for notification about when the user cancels the current menu
15760 // This is an internal mechanism used for Menus to signal to their parent to
15761 // close them. In general developers should not attach to or override this method.
15762 // tags:
15763 // protected
15764 },
15765
15766 _moveToPopup: function(/*Event*/ evt){
15767 // summary:
15768 // This handles the right arrow key (left arrow key on RTL systems),
15769 // which will either open a submenu, or move to the next item in the
15770 // ancestor MenuBar
15771 // tags:
15772 // private
15773
15774 if(this.focusedChild && this.focusedChild.popup && !this.focusedChild.disabled){
15775 this.focusedChild._onClick(evt);
15776 }else{
15777 var topMenu = this._getTopMenu();
15778 if(topMenu && topMenu._isMenuBar){
15779 topMenu.focusNext();
15780 }
15781 }
15782 },
15783
15784 _onPopupHover: function(/*Event*/ evt){
15785 // summary:
15786 // This handler is called when the mouse moves over the popup.
15787 // tags:
15788 // private
15789
15790 // if the mouse hovers over a menu popup that is in pending-close state,
15791 // then stop the close operation.
15792 // This can't be done in onItemHover since some popup targets don't have MenuItems (e.g. ColorPicker)
15793 if(this.currentPopup && this.currentPopup._pendingClose_timer){
15794 var parentMenu = this.currentPopup.parentMenu;
15795 // highlight the parent menu item pointing to this popup
15796 if(parentMenu.focusedChild){
15797 parentMenu.focusedChild._setSelected(false);
15798 }
15799 parentMenu.focusedChild = this.currentPopup.from_item;
15800 parentMenu.focusedChild._setSelected(true);
15801 // cancel the pending close
15802 this._stopPendingCloseTimer(this.currentPopup);
15803 }
15804 },
15805
15806 onItemHover: function(/*MenuItem*/ item){
15807 // summary:
15808 // Called when cursor is over a MenuItem.
15809 // tags:
15810 // protected
15811
15812 // Don't do anything unless user has "activated" the menu by:
15813 // 1) clicking it
15814 // 2) opening it from a parent menu (which automatically focuses it)
15815 if(this.isActive){
15816 this.focusChild(item);
15817 if(this.focusedChild.popup && !this.focusedChild.disabled && !this.hover_timer){
15818 this.hover_timer = setTimeout(dojo.hitch(this, "_openPopup"), this.popupDelay);
15819 }
15820 }
15821 // if the user is mixing mouse and keyboard navigation,
15822 // then the menu may not be active but a menu item has focus,
15823 // but it's not the item that the mouse just hovered over.
15824 // To avoid both keyboard and mouse selections, use the latest.
15825 if(this.focusedChild){
15826 this.focusChild(item);
15827 }
15828 this._hoveredChild = item;
15829 },
15830
15831 _onChildBlur: function(item){
15832 // summary:
15833 // Called when a child MenuItem becomes inactive because focus
15834 // has been removed from the MenuItem *and* it's descendant menus.
15835 // tags:
15836 // private
15837 this._stopPopupTimer();
15838 item._setSelected(false);
15839 // Close all popups that are open and descendants of this menu
15840 var itemPopup = item.popup;
15841 if(itemPopup){
15842 this._stopPendingCloseTimer(itemPopup);
15843 itemPopup._pendingClose_timer = setTimeout(function(){
15844 itemPopup._pendingClose_timer = null;
15845 if(itemPopup.parentMenu){
15846 itemPopup.parentMenu.currentPopup = null;
15847 }
15848 dijit.popup.close(itemPopup); // this calls onClose
15849 }, this.popupDelay);
15850 }
15851 },
15852
15853 onItemUnhover: function(/*MenuItem*/ item){
15854 // summary:
15855 // Callback fires when mouse exits a MenuItem
15856 // tags:
15857 // protected
15858
15859 if(this.isActive){
15860 this._stopPopupTimer();
15861 }
15862 if(this._hoveredChild == item){ this._hoveredChild = null; }
15863 },
15864
15865 _stopPopupTimer: function(){
15866 // summary:
15867 // Cancels the popup timer because the user has stop hovering
15868 // on the MenuItem, etc.
15869 // tags:
15870 // private
15871 if(this.hover_timer){
15872 clearTimeout(this.hover_timer);
15873 this.hover_timer = null;
15874 }
15875 },
15876
15877 _stopPendingCloseTimer: function(/*dijit._Widget*/ popup){
15878 // summary:
15879 // Cancels the pending-close timer because the close has been preempted
15880 // tags:
15881 // private
15882 if(popup._pendingClose_timer){
15883 clearTimeout(popup._pendingClose_timer);
15884 popup._pendingClose_timer = null;
15885 }
15886 },
15887
15888 _stopFocusTimer: function(){
15889 // summary:
15890 // Cancels the pending-focus timer because the menu was closed before focus occured
15891 // tags:
15892 // private
15893 if(this._focus_timer){
15894 clearTimeout(this._focus_timer);
15895 this._focus_timer = null;
15896 }
15897 },
15898
15899 _getTopMenu: function(){
15900 // summary:
15901 // Returns the top menu in this chain of Menus
15902 // tags:
15903 // private
15904 for(var top=this; top.parentMenu; top=top.parentMenu);
15905 return top;
15906 },
15907
15908 onItemClick: function(/*dijit._Widget*/ item, /*Event*/ evt){
15909 // summary:
15910 // Handle clicks on an item.
15911 // tags:
15912 // private
15913
15914 // this can't be done in _onFocus since the _onFocus events occurs asynchronously
15915 if(typeof this.isShowingNow == 'undefined'){ // non-popup menu
15916 this._markActive();
15917 }
15918
15919 this.focusChild(item);
15920
15921 if(item.disabled){ return false; }
15922
15923 if(item.popup){
15924 this._openPopup();
15925 }else{
15926 // before calling user defined handler, close hierarchy of menus
15927 // and restore focus to place it was when menu was opened
15928 this.onExecute();
15929
15930 // user defined handler for click
15931 item.onClick(evt);
15932 }
15933 },
15934
15935 _openPopup: function(){
15936 // summary:
15937 // Open the popup to the side of/underneath the current menu item
15938 // tags:
15939 // protected
15940
15941 this._stopPopupTimer();
15942 var from_item = this.focusedChild;
15943 if(!from_item){ return; } // the focused child lost focus since the timer was started
15944 var popup = from_item.popup;
15945 if(popup.isShowingNow){ return; }
15946 if(this.currentPopup){
15947 this._stopPendingCloseTimer(this.currentPopup);
15948 dijit.popup.close(this.currentPopup);
15949 }
15950 popup.parentMenu = this;
15951 popup.from_item = from_item; // helps finding the parent item that should be focused for this popup
15952 var self = this;
15953 dijit.popup.open({
15954 parent: this,
15955 popup: popup,
15956 around: from_item.domNode,
15957 orient: this._orient || (this.isLeftToRight() ?
15958 {'TR': 'TL', 'TL': 'TR', 'BR': 'BL', 'BL': 'BR'} :
15959 {'TL': 'TR', 'TR': 'TL', 'BL': 'BR', 'BR': 'BL'}),
15960 onCancel: function(){ // called when the child menu is canceled
15961 // set isActive=false (_closeChild vs _cleanUp) so that subsequent hovering will NOT open child menus
15962 // which seems aligned with the UX of most applications (e.g. notepad, wordpad, paint shop pro)
15963 self.focusChild(from_item); // put focus back on my node
15964 self._cleanUp(); // close the submenu (be sure this is done _after_ focus is moved)
15965 from_item._setSelected(true); // oops, _cleanUp() deselected the item
15966 self.focusedChild = from_item; // and unset focusedChild
15967 },
15968 onExecute: dojo.hitch(this, "_cleanUp")
15969 });
15970
15971 this.currentPopup = popup;
15972 // detect mouseovers to handle lazy mouse movements that temporarily focus other menu items
15973 popup.connect(popup.domNode, "onmouseenter", dojo.hitch(self, "_onPopupHover")); // cleaned up when the popped-up widget is destroyed on close
15974
15975 if(popup.focus){
15976 // If user is opening the popup via keyboard (right arrow, or down arrow for MenuBar),
15977 // if the cursor happens to collide with the popup, it will generate an onmouseover event
15978 // even though the mouse wasn't moved. Use a setTimeout() to call popup.focus so that
15979 // our focus() call overrides the onmouseover event, rather than vice-versa. (#8742)
15980 popup._focus_timer = setTimeout(dojo.hitch(popup, function(){
15981 this._focus_timer = null;
15982 this.focus();
15983 }), 0);
15984 }
15985 },
15986
15987 _markActive: function(){
15988 // summary:
15989 // Mark this menu's state as active.
15990 // Called when this Menu gets focus from:
15991 // 1) clicking it (mouse or via space/arrow key)
15992 // 2) being opened by a parent menu.
15993 // This is not called just from mouse hover.
15994 // Focusing a menu via TAB does NOT automatically set isActive
15995 // since TAB is a navigation operation and not a selection one.
15996 // For Windows apps, pressing the ALT key focuses the menubar
15997 // menus (similar to TAB navigation) but the menu is not active
15998 // (ie no dropdown) until an item is clicked.
15999 this.isActive = true;
16000 dojo.addClass(this.domNode, "dijitMenuActive");
16001 dojo.removeClass(this.domNode, "dijitMenuPassive");
16002 },
16003
16004 onOpen: function(/*Event*/ e){
16005 // summary:
16006 // Callback when this menu is opened.
16007 // This is called by the popup manager as notification that the menu
16008 // was opened.
16009 // tags:
16010 // private
16011
16012 this.isShowingNow = true;
16013 this._markActive();
16014 },
16015
16016 _markInactive: function(){
16017 // summary:
16018 // Mark this menu's state as inactive.
16019 this.isActive = false; // don't do this in _onBlur since the state is pending-close until we get here
16020 dojo.removeClass(this.domNode, "dijitMenuActive");
16021 dojo.addClass(this.domNode, "dijitMenuPassive");
16022 },
16023
16024 onClose: function(){
16025 // summary:
16026 // Callback when this menu is closed.
16027 // This is called by the popup manager as notification that the menu
16028 // was closed.
16029 // tags:
16030 // private
16031
16032 this._stopFocusTimer();
16033 this._markInactive();
16034 this.isShowingNow = false;
16035 this.parentMenu = null;
16036 },
16037
16038 _closeChild: function(){
16039 // summary:
16040 // Called when submenu is clicked or focus is lost. Close hierarchy of menus.
16041 // tags:
16042 // private
16043 this._stopPopupTimer();
16044 if(this.focusedChild){ // unhighlight the focused item
16045 this.focusedChild._setSelected(false);
16046 this.focusedChild._onUnhover();
16047 this.focusedChild = null;
16048 }
16049 if(this.currentPopup){
16050 // Close all popups that are open and descendants of this menu
16051 dijit.popup.close(this.currentPopup);
16052 this.currentPopup = null;
16053 }
16054 },
16055
16056 _onItemFocus: function(/*MenuItem*/ item){
16057 // summary:
16058 // Called when child of this Menu gets focus from:
16059 // 1) clicking it
16060 // 2) tabbing into it
16061 // 3) being opened by a parent menu.
16062 // This is not called just from mouse hover.
16063 if(this._hoveredChild && this._hoveredChild != item){
16064 this._hoveredChild._onUnhover(); // any previous mouse movement is trumped by focus selection
16065 }
16066 },
16067
16068 _onBlur: function(){
16069 // summary:
16070 // Called when focus is moved away from this Menu and it's submenus.
16071 // tags:
16072 // protected
16073 this._cleanUp();
16074 this.inherited(arguments);
16075 },
16076
16077 _cleanUp: function(){
16078 // summary:
16079 // Called when the user is done with this menu. Closes hierarchy of menus.
16080 // tags:
16081 // private
16082
16083 this._closeChild(); // don't call this.onClose since that's incorrect for MenuBar's that never close
16084 if(typeof this.isShowingNow == 'undefined'){ // non-popup menu doesn't call onClose
16085 this._markInactive();
16086 }
16087 }
16088});
16089
16090dojo.declare("dijit.Menu",
16091 dijit._MenuBase,
16092 {
16093 // summary
16094 // A context menu you can assign to multiple elements
16095
16096 // TODO: most of the code in here is just for context menu (right-click menu)
16097 // support. In retrospect that should have been a separate class (dijit.ContextMenu).
16098 // Split them for 2.0
16099
16100 constructor: function(){
16101 this._bindings = [];
16102 },
16103
16104 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"),
16105
16106 baseClass: "dijitMenu",
16107
16108 // targetNodeIds: [const] String[]
16109 // Array of dom node ids of nodes to attach to.
16110 // Fill this with nodeIds upon widget creation and it becomes context menu for those nodes.
16111 targetNodeIds: [],
16112
16113 // contextMenuForWindow: [const] Boolean
16114 // If true, right clicking anywhere on the window will cause this context menu to open.
16115 // If false, must specify targetNodeIds.
16116 contextMenuForWindow: false,
16117
16118 // leftClickToOpen: [const] Boolean
16119 // If true, menu will open on left click instead of right click, similiar to a file menu.
16120 leftClickToOpen: false,
16121
16122 // refocus: Boolean
16123 // When this menu closes, re-focus the element which had focus before it was opened.
16124 refocus: true,
16125
16126 postCreate: function(){
16127 if(this.contextMenuForWindow){
16128 this.bindDomNode(dojo.body());
16129 }else{
16130 // TODO: should have _setTargetNodeIds() method to handle initialization and a possible
16131 // later attr('targetNodeIds', ...) call. There's also a problem that targetNodeIds[]
16132 // gets stale after calls to bindDomNode()/unBindDomNode() as it still is just the original list (see #9610)
16133 dojo.forEach(this.targetNodeIds, this.bindDomNode, this);
16134 }
16135 var k = dojo.keys, l = this.isLeftToRight();
16136 this._openSubMenuKey = l ? k.RIGHT_ARROW : k.LEFT_ARROW;
16137 this._closeSubMenuKey = l ? k.LEFT_ARROW : k.RIGHT_ARROW;
16138 this.connectKeyNavHandlers([k.UP_ARROW], [k.DOWN_ARROW]);
16139 },
16140
16141 _onKeyPress: function(/*Event*/ evt){
16142 // summary:
16143 // Handle keyboard based menu navigation.
16144 // tags:
16145 // protected
16146
16147 if(evt.ctrlKey || evt.altKey){ return; }
16148
16149 switch(evt.charOrCode){
16150 case this._openSubMenuKey:
16151 this._moveToPopup(evt);
16152 dojo.stopEvent(evt);
16153 break;
16154 case this._closeSubMenuKey:
16155 if(this.parentMenu){
16156 if(this.parentMenu._isMenuBar){
16157 this.parentMenu.focusPrev();
16158 }else{
16159 this.onCancel(false);
16160 }
16161 }else{
16162 dojo.stopEvent(evt);
16163 }
16164 break;
16165 }
16166 },
16167
16168 // thanks burstlib!
16169 _iframeContentWindow: function(/* HTMLIFrameElement */iframe_el){
16170 // summary:
16171 // Returns the window reference of the passed iframe
16172 // tags:
16173 // private
16174 var win = dojo.window.get(this._iframeContentDocument(iframe_el)) ||
16175 // Moz. TODO: is this available when defaultView isn't?
16176 this._iframeContentDocument(iframe_el)['__parent__'] ||
16177 (iframe_el.name && dojo.doc.frames[iframe_el.name]) || null;
16178 return win; // Window
16179 },
16180
16181 _iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){
16182 // summary:
16183 // Returns a reference to the document object inside iframe_el
16184 // tags:
16185 // protected
16186 var doc = iframe_el.contentDocument // W3
16187 || (iframe_el.contentWindow && iframe_el.contentWindow.document) // IE
16188 || (iframe_el.name && dojo.doc.frames[iframe_el.name] && dojo.doc.frames[iframe_el.name].document)
16189 || null;
16190 return doc; // HTMLDocument
16191 },
16192
16193 bindDomNode: function(/*String|DomNode*/ node){
16194 // summary:
16195 // Attach menu to given node
16196 node = dojo.byId(node);
16197
16198 var cn; // Connect node
16199
16200 // Support context menus on iframes. Rather than binding to the iframe itself we need
16201 // to bind to the <body> node inside the iframe.
16202 if(node.tagName.toLowerCase() == "iframe"){
16203 var iframe = node,
16204 win = this._iframeContentWindow(iframe);
16205 cn = dojo.withGlobal(win, dojo.body);
16206 }else{
16207
16208 // To capture these events at the top level, attach to <html>, not <body>.
16209 // Otherwise right-click context menu just doesn't work.
16210 cn = (node == dojo.body() ? dojo.doc.documentElement : node);
16211 }
16212
16213
16214 // "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode())
16215 var binding = {
16216 node: node,
16217 iframe: iframe
16218 };
16219
16220 // Save info about binding in _bindings[], and make node itself record index(+1) into
16221 // _bindings[] array. Prefix w/_dijitMenu to avoid setting an attribute that may
16222 // start with a number, which fails on FF/safari.
16223 dojo.attr(node, "_dijitMenu" + this.id, this._bindings.push(binding));
16224
16225 // Setup the connections to monitor click etc., unless we are connecting to an iframe which hasn't finished
16226 // loading yet, in which case we need to wait for the onload event first, and then connect
16227 // On linux Shift-F10 produces the oncontextmenu event, but on Windows it doesn't, so
16228 // we need to monitor keyboard events in addition to the oncontextmenu event.
16229 var doConnects = dojo.hitch(this, function(cn){
16230 return [
16231 // TODO: when leftClickToOpen is true then shouldn't space/enter key trigger the menu,
16232 // rather than shift-F10?
16233 dojo.connect(cn, this.leftClickToOpen ? "onclick" : "oncontextmenu", this, function(evt){
16234 // Schedule context menu to be opened unless it's already been scheduled from onkeydown handler
16235 dojo.stopEvent(evt);
16236 this._scheduleOpen(evt.target, iframe, {x: evt.pageX, y: evt.pageY});
16237 }),
16238 dojo.connect(cn, "onkeydown", this, function(evt){
16239 if(evt.shiftKey && evt.keyCode == dojo.keys.F10){
16240 dojo.stopEvent(evt);
16241 this._scheduleOpen(evt.target, iframe); // no coords - open near target node
16242 }
16243 })
16244 ];
16245 });
16246 binding.connects = cn ? doConnects(cn) : [];
16247
16248 if(iframe){
16249 // Setup handler to [re]bind to the iframe when the contents are initially loaded,
16250 // and every time the contents change.
16251 // Need to do this b/c we are actually binding to the iframe's <body> node.
16252 // Note: can't use dojo.connect(), see #9609.
16253
16254 binding.onloadHandler = dojo.hitch(this, function(){
16255 // want to remove old connections, but IE throws exceptions when trying to
16256 // access the <body> node because it's already gone, or at least in a state of limbo
16257
16258 var win = this._iframeContentWindow(iframe);
16259 cn = dojo.withGlobal(win, dojo.body);
16260 binding.connects = doConnects(cn);
16261 });
16262 if(iframe.addEventListener){
16263 iframe.addEventListener("load", binding.onloadHandler, false);
16264 }else{
16265 iframe.attachEvent("onload", binding.onloadHandler);
16266 }
16267 }
16268 },
16269
16270 unBindDomNode: function(/*String|DomNode*/ nodeName){
16271 // summary:
16272 // Detach menu from given node
16273
16274 var node;
16275 try{
16276 node = dojo.byId(nodeName);
16277 }catch(e){
16278 // On IE the dojo.byId() call will get an exception if the attach point was
16279 // the <body> node of an <iframe> that has since been reloaded (and thus the
16280 // <body> node is in a limbo state of destruction.
16281 return;
16282 }
16283
16284 // node["_dijitMenu" + this.id] contains index(+1) into my _bindings[] array
16285 var attrName = "_dijitMenu" + this.id;
16286 if(node && dojo.hasAttr(node, attrName)){
16287 var bid = dojo.attr(node, attrName)-1, b = this._bindings[bid];
16288 dojo.forEach(b.connects, dojo.disconnect);
16289
16290 // Remove listener for iframe onload events
16291 var iframe = b.iframe;
16292 if(iframe){
16293 if(iframe.removeEventListener){
16294 iframe.removeEventListener("load", b.onloadHandler, false);
16295 }else{
16296 iframe.detachEvent("onload", b.onloadHandler);
16297 }
16298 }
16299
16300 dojo.removeAttr(node, attrName);
16301 delete this._bindings[bid];
16302 }
16303 },
16304
16305 _scheduleOpen: function(/*DomNode?*/ target, /*DomNode?*/ iframe, /*Object?*/ coords){
16306 // summary:
16307 // Set timer to display myself. Using a timer rather than displaying immediately solves
16308 // two problems:
16309 //
16310 // 1. IE: without the delay, focus work in "open" causes the system
16311 // context menu to appear in spite of stopEvent.
16312 //
16313 // 2. Avoid double-shows on linux, where shift-F10 generates an oncontextmenu event
16314 // even after a dojo.stopEvent(e). (Shift-F10 on windows doesn't generate the
16315 // oncontextmenu event.)
16316
16317 if(!this._openTimer){
16318 this._openTimer = setTimeout(dojo.hitch(this, function(){
16319 delete this._openTimer;
16320 this._openMyself({
16321 target: target,
16322 iframe: iframe,
16323 coords: coords
16324 });
16325 }), 1);
16326 }
16327 },
16328
16329 _openMyself: function(args){
16330 // summary:
16331 // Internal function for opening myself when the user does a right-click or something similar.
16332 // args:
16333 // This is an Object containing:
16334 // * target:
16335 // The node that is being clicked
16336 // * iframe:
16337 // If an <iframe> is being clicked, iframe points to that iframe
16338 // * coords:
16339 // Put menu at specified x/y position in viewport, or if iframe is
16340 // specified, then relative to iframe.
16341 //
16342 // _openMyself() formerly took the event object, and since various code references
16343 // evt.target (after connecting to _openMyself()), using an Object for parameters
16344 // (so that old code still works).
16345
16346 var target = args.target,
16347 iframe = args.iframe,
16348 coords = args.coords;
16349
16350 // Get coordinates to open menu, either at specified (mouse) position or (if triggered via keyboard)
16351 // then near the node the menu is assigned to.
16352 if(coords){
16353 if(iframe){
16354 // Specified coordinates are on <body> node of an <iframe>, convert to match main document
16355 var od = target.ownerDocument,
16356 ifc = dojo.position(iframe, true),
16357 win = this._iframeContentWindow(iframe),
16358 scroll = dojo.withGlobal(win, "_docScroll", dojo);
16359
16360 var cs = dojo.getComputedStyle(iframe),
16361 tp = dojo._toPixelValue,
16362 left = (dojo.isIE && dojo.isQuirks ? 0 : tp(iframe, cs.paddingLeft)) + (dojo.isIE && dojo.isQuirks ? tp(iframe, cs.borderLeftWidth) : 0),
16363 top = (dojo.isIE && dojo.isQuirks ? 0 : tp(iframe, cs.paddingTop)) + (dojo.isIE && dojo.isQuirks ? tp(iframe, cs.borderTopWidth) : 0);
16364
16365 coords.x += ifc.x + left - scroll.x;
16366 coords.y += ifc.y + top - scroll.y;
16367 }
16368 }else{
16369 coords = dojo.position(target, true);
16370 coords.x += 10;
16371 coords.y += 10;
16372 }
16373
16374 var self=this;
16375 var savedFocus = dijit.getFocus(this);
16376 function closeAndRestoreFocus(){
16377 // user has clicked on a menu or popup
16378 if(self.refocus){
16379 dijit.focus(savedFocus);
16380 }
16381 dijit.popup.close(self);
16382 }
16383 dijit.popup.open({
16384 popup: this,
16385 x: coords.x,
16386 y: coords.y,
16387 onExecute: closeAndRestoreFocus,
16388 onCancel: closeAndRestoreFocus,
16389 orient: this.isLeftToRight() ? 'L' : 'R'
16390 });
16391 this.focus();
16392
16393 this._onBlur = function(){
16394 this.inherited('_onBlur', arguments);
16395 // Usually the parent closes the child widget but if this is a context
16396 // menu then there is no parent
16397 dijit.popup.close(this);
16398 // don't try to restore focus; user has clicked another part of the screen
16399 // and set focus there
16400 };
16401 },
16402
16403 uninitialize: function(){
16404 dojo.forEach(this._bindings, function(b){ if(b){ this.unBindDomNode(b.node); } }, this);
16405 this.inherited(arguments);
16406 }
16407}
16408);
16409
16410// Back-compat (TODO: remove in 2.0)
16411
16412
16413
16414
16415
16416
16417}
16418
16419if(!dojo._hasResource["dijit.form.Select"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
16420dojo._hasResource["dijit.form.Select"] = true;
16421dojo.provide("dijit.form.Select");
16422
16423
16424
16425
16426
16427
16428
16429
16430dojo.declare("dijit.form._SelectMenu", dijit.Menu, {
16431 // summary:
16432 // An internally-used menu for dropdown that allows us a vertical scrollbar
16433 buildRendering: function(){
16434 // summary:
16435 // Stub in our own changes, so that our domNode is not a table
16436 // otherwise, we won't respond correctly to heights/overflows
16437 this.inherited(arguments);
16438 var o = (this.menuTableNode = this.domNode);
16439 var n = (this.domNode = dojo.create("div", {style: {overflowX: "hidden", overflowY: "scroll"}}));
16440 if(o.parentNode){
16441 o.parentNode.replaceChild(n, o);
16442 }
16443 dojo.removeClass(o, "dijitMenuTable");
16444 n.className = o.className + " dijitSelectMenu";
16445 o.className = "dijitReset dijitMenuTable";
16446 dijit.setWaiRole(o,"listbox");
16447 dijit.setWaiRole(n,"presentation");
16448 n.appendChild(o);
16449 },
16450 resize: function(/*Object*/ mb){
16451 // summary:
16452 // Overridden so that we are able to handle resizing our
16453 // internal widget. Note that this is not a "full" resize
16454 // implementation - it only works correctly if you pass it a
16455 // marginBox.
16456 //
16457 // mb: Object
16458 // The margin box to set this dropdown to.
16459 if(mb){
16460 dojo.marginBox(this.domNode, mb);
16461 if("w" in mb){
16462 // We've explicitly set the wrapper <div>'s width, so set <table> width to match.
16463 // 100% is safer than a pixel value because there may be a scroll bar with
16464 // browser/OS specific width.
16465 this.menuTableNode.style.width = "100%";
16466 }
16467 }
16468 }
16469});
16470
16471dojo.declare("dijit.form.Select", [dijit.form._FormSelectWidget, dijit._HasDropDown], {
16472 // summary:
16473 // This is a "styleable" select box - it is basically a DropDownButton which
16474 // can take a <select> as its input.
16475
16476 baseClass: "dijitSelect",
16477
16478 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\">&#9660;</div\n\t\t></td\n\t></tr></tbody\n></table>\n"),
16479
16480 // attributeMap: Object
16481 // Add in our style to be applied to the focus node
16482 attributeMap: dojo.mixin(dojo.clone(dijit.form._FormSelectWidget.prototype.attributeMap),{style:"tableNode"}),
16483
16484 // required: Boolean
16485 // Can be true or false, default is false.
16486 required: false,
16487
16488 // state: String
16489 // Shows current state (ie, validation result) of input (Normal, Warning, or Error)
16490 state: "",
16491
16492 // tooltipPosition: String[]
16493 // See description of dijit.Tooltip.defaultPosition for details on this parameter.
16494 tooltipPosition: [],
16495
16496 // emptyLabel: string
16497 // What to display in an "empty" dropdown
16498 emptyLabel: "",
16499
16500 // _isLoaded: Boolean
16501 // Whether or not we have been loaded
16502 _isLoaded: false,
16503
16504 // _childrenLoaded: Boolean
16505 // Whether or not our children have been loaded
16506 _childrenLoaded: false,
16507
16508 _fillContent: function(){
16509 // summary:
16510 // Set the value to be the first, or the selected index
16511 this.inherited(arguments);
16512 if(this.options.length && !this.value && this.srcNodeRef){
16513 var si = this.srcNodeRef.selectedIndex;
16514 this.value = this.options[si != -1 ? si : 0].value;
16515 }
16516
16517 // Create the dropDown widget
16518 this.dropDown = new dijit.form._SelectMenu({id: this.id + "_menu"});
16519 dojo.addClass(this.dropDown.domNode, this.baseClass + "Menu");
16520 },
16521
16522 _getMenuItemForOption: function(/*dijit.form.__SelectOption*/ option){
16523 // summary:
16524 // For the given option, return the menu item that should be
16525 // used to display it. This can be overridden as needed
16526 if(!option.value){
16527 // We are a separator (no label set for it)
16528 return new dijit.MenuSeparator();
16529 }else{
16530 // Just a regular menu option
16531 var click = dojo.hitch(this, "_setValueAttr", option);
16532 var item = new dijit.MenuItem({
16533 option: option,
16534 label: option.label,
16535 onClick: click,
16536 disabled: option.disabled || false
16537 });
16538 dijit.setWaiRole(item.focusNode, "listitem");
16539 return item;
16540 }
16541 },
16542
16543 _addOptionItem: function(/*dijit.form.__SelectOption*/ option){
16544 // summary:
16545 // For the given option, add an option to our dropdown.
16546 // If the option doesn't have a value, then a separator is added
16547 // in that place.
16548 if(this.dropDown){
16549 this.dropDown.addChild(this._getMenuItemForOption(option));
16550 }
16551 },
16552
16553 _getChildren: function(){
16554 if(!this.dropDown){
16555 return [];
16556 }
16557 return this.dropDown.getChildren();
16558 },
16559
16560 _loadChildren: function(/*Boolean*/ loadMenuItems){
16561 // summary:
16562 // Resets the menu and the length attribute of the button - and
16563 // ensures that the label is appropriately set.
16564 // loadMenuItems: Boolean
16565 // actually loads the child menu items - we only do this when we are
16566 // populating for showing the dropdown.
16567
16568 if(loadMenuItems === true){
16569 // this.inherited destroys this.dropDown's child widgets (MenuItems).
16570 // Avoid this.dropDown (Menu widget) having a pointer to a destroyed widget (which will cause
16571 // issues later in _setSelected). (see #10296)
16572 if(this.dropDown){
16573 delete this.dropDown.focusedChild;
16574 }
16575 if(this.options.length){
16576 this.inherited(arguments);
16577 }else{
16578 // Drop down menu is blank but add one blank entry just so something appears on the screen
16579 // to let users know that they are no choices (mimicing native select behavior)
16580 dojo.forEach(this._getChildren(), function(child){ child.destroyRecursive(); });
16581 var item = new dijit.MenuItem({label: "&nbsp;"});
16582 this.dropDown.addChild(item);
16583 }
16584 }else{
16585 this._updateSelection();
16586 }
16587
16588 var len = this.options.length;
16589 this._isLoaded = false;
16590 this._childrenLoaded = true;
16591
16592 if(!this._loadingStore){
16593 // Don't call this if we are loading - since we will handle it later
16594 this._setValueAttr(this.value);
16595 }
16596 },
16597
16598 _setValueAttr: function(value){
16599 this.inherited(arguments);
16600 dojo.attr(this.valueNode, "value", this.get("value"));
16601 },
16602
16603 _setDisplay: function(/*String*/ newDisplay){
16604 // summary:
16605 // sets the display for the given value (or values)
16606 this.containerNode.innerHTML = '<span class="dijitReset dijitInline ' + this.baseClass + 'Label">' +
16607 (newDisplay || this.emptyLabel || "&nbsp;") +
16608 '</span>';
16609 dijit.setWaiState(this.focusNode, "valuetext", (newDisplay || this.emptyLabel || "&nbsp;") );
16610 },
16611
16612 validate: function(/*Boolean*/ isFocused){
16613 // summary:
16614 // Called by oninit, onblur, and onkeypress.
16615 // description:
16616 // Show missing or invalid messages if appropriate, and highlight textbox field.
16617 // Used when a select is initially set to no value and the user is required to
16618 // set the value.
16619
16620 var isValid = this.isValid(isFocused);
16621 this.state = isValid ? "" : "Error";
16622 this._setStateClass();
16623 dijit.setWaiState(this.focusNode, "invalid", isValid ? "false" : "true");
16624 var message = isValid ? "" : this._missingMsg;
16625 if(this._message !== message){
16626 this._message = message;
16627 dijit.hideTooltip(this.domNode);
16628 if(message){
16629 dijit.showTooltip(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
16630 }
16631 }
16632 return isValid;
16633 },
16634
16635 isValid: function(/*Boolean*/ isFocused){
16636 // summary:
16637 // Whether or not this is a valid value. The only way a Select
16638 // can be invalid is when it's required but nothing is selected.
16639 return (!this.required || !(/^\s*$/.test(this.value)));
16640 },
16641
16642 reset: function(){
16643 // summary:
16644 // Overridden so that the state will be cleared.
16645 this.inherited(arguments);
16646 dijit.hideTooltip(this.domNode);
16647 this.state = "";
16648 this._setStateClass();
16649 delete this._message;
16650 },
16651
16652 postMixInProperties: function(){
16653 // summary:
16654 // set the missing message
16655 this.inherited(arguments);
16656 this._missingMsg = dojo.i18n.getLocalization("dijit.form", "validate",
16657 this.lang).missingMessage;
16658 },
16659
16660 postCreate: function(){
16661 this.inherited(arguments);
16662 if(this.tableNode.style.width){
16663 dojo.addClass(this.domNode, this.baseClass + "FixedWidth");
16664 }
16665 },
16666
16667 isLoaded: function(){
16668 return this._isLoaded;
16669 },
16670
16671 loadDropDown: function(/*Function*/ loadCallback){
16672 // summary:
16673 // populates the menu
16674 this._loadChildren(true);
16675 this._isLoaded = true;
16676 loadCallback();
16677 },
16678
16679 closeDropDown: function(){
16680 // overriding _HasDropDown.closeDropDown()
16681 this.inherited(arguments);
16682
16683 if(this.dropDown && this.dropDown.menuTableNode){
16684 // Erase possible width: 100% setting from _SelectMenu.resize().
16685 // Leaving it would interfere with the next openDropDown() call, which
16686 // queries the natural size of the drop down.
16687 this.dropDown.menuTableNode.style.width = "";
16688 }
16689 },
16690
16691 uninitialize: function(preserveDom){
16692 if(this.dropDown && !this.dropDown._destroyed){
16693 this.dropDown.destroyRecursive(preserveDom);
16694 delete this.dropDown;
16695 }
16696 this.inherited(arguments);
16697 }
16698});
16699
16700}
16701
16702if(!dojo._hasResource["dijit._editor.plugins.LinkDialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
16703dojo._hasResource["dijit._editor.plugins.LinkDialog"] = true;
16704dojo.provide("dijit._editor.plugins.LinkDialog");
16705
16706
16707
16708
16709
16710
16711
16712
16713
16714
16715
16716
16717
16718
16719dojo.declare("dijit._editor.plugins.LinkDialog", dijit._editor._Plugin, {
16720 // summary:
16721 // This plugin provides the basis for an 'anchor' (link) dialog and an extension of it
16722 // provides the image link dialog.
16723 //
16724 // description:
16725 // The command provided by this plugin is:
16726 // * createLink
16727
16728 // Override _Plugin.buttonClass. This plugin is controlled by a DropDownButton
16729 // (which triggers a TooltipDialog).
16730 buttonClass: dijit.form.DropDownButton,
16731
16732 // Override _Plugin.useDefaultCommand... processing is handled by this plugin, not by dijit.Editor.
16733 useDefaultCommand: false,
16734
16735 // urlRegExp: [protected] String
16736 // Used for validating input as correct URL. While file:// urls are not terribly
16737 // useful, they are technically valid.
16738 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/]*)?(?:#.*)?)?)?",
16739
16740 // emailRegExp: [protected] String
16741 // Used for validating input as correct email address. Taken from dojox.validate
16742 emailRegExp: "<?(mailto\\:)([!#-'*+\\-\\/-9=?A-Z^-~]+[.])*[!#-'*+\\-\\/-9=?A-Z^-~]+" /*username*/ + "@" +
16743 "((?:(?:[\\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.
16744
16745 // htmlTemplate: [protected] String
16746 // String used for templating the HTML to insert at the desired point.
16747 htmlTemplate: "<a href=\"${urlInput}\" _djrealurl=\"${urlInput}\"" +
16748 " target=\"${targetSelect}\"" +
16749 ">${textInput}</a>",
16750
16751 // tag: [protected] String
16752 // Tag used for the link type.
16753 tag: "a",
16754
16755 // _hostRxp [private] RegExp
16756 // Regular expression used to validate url fragments (ip address, hostname, etc)
16757 _hostRxp: new RegExp("^((([^\\[:]+):)?([^@]+)@)?(\\[([^\\]]+)\\]|([^\\[:]*))(:([0-9]+))?$"),
16758
16759 // _userAtRxp [private] RegExp
16760 // Regular expression used to validate e-mail address fragment.
16761 _userAtRxp: new RegExp("^([!#-'*+\\-\\/-9=?A-Z^-~]+[.])*[!#-'*+\\-\\/-9=?A-Z^-~]+@", "i"),
16762
16763 // linkDialogTemplate: [protected] String
16764 // Template for contents of TooltipDialog to pick URL
16765 linkDialogTemplate: [
16766 "<table><tr><td>",
16767 "<label for='${id}_urlInput'>${url}</label>",
16768 "</td><td>",
16769 "<input dojoType='dijit.form.ValidationTextBox' required='true' " +
16770 "id='${id}_urlInput' name='urlInput' intermediateChanges='true'>",
16771 "</td></tr><tr><td>",
16772 "<label for='${id}_textInput'>${text}</label>",
16773 "</td><td>",
16774 "<input dojoType='dijit.form.ValidationTextBox' required='true' id='${id}_textInput' " +
16775 "name='textInput' intermediateChanges='true'>",
16776 "</td></tr><tr><td>",
16777 "<label for='${id}_targetSelect'>${target}</label>",
16778 "</td><td>",
16779 "<select id='${id}_targetSelect' name='targetSelect' dojoType='dijit.form.Select'>",
16780 "<option selected='selected' value='_self'>${currentWindow}</option>",
16781 "<option value='_blank'>${newWindow}</option>",
16782 "<option value='_top'>${topWindow}</option>",
16783 "<option value='_parent'>${parentWindow}</option>",
16784 "</select>",
16785 "</td></tr><tr><td colspan='2'>",
16786 "<button dojoType='dijit.form.Button' type='submit' id='${id}_setButton'>${set}</button>",
16787 "<button dojoType='dijit.form.Button' type='button' id='${id}_cancelButton'>${buttonCancel}</button>",
16788 "</td></tr></table>"
16789 ].join(""),
16790
16791 _initButton: function(){
16792 // Override _Plugin._initButton() to initialize DropDownButton and TooltipDialog.
16793 var _this = this;
16794 this.tag = this.command == 'insertImage' ? 'img' : 'a';
16795 var messages = dojo.mixin(dojo.i18n.getLocalization("dijit", "common", this.lang),
16796 dojo.i18n.getLocalization("dijit._editor", "LinkDialog", this.lang));
16797 var dropDown = (this.dropDown = new dijit.TooltipDialog({
16798 title: messages[this.command + "Title"],
16799 execute: dojo.hitch(this, "setValue"),
16800 onOpen: function(){
16801 _this._onOpenDialog();
16802 dijit.TooltipDialog.prototype.onOpen.apply(this, arguments);
16803 },
16804 onCancel: function(){
16805 setTimeout(dojo.hitch(_this, "_onCloseDialog"),0);
16806 }
16807 }));
16808 messages.urlRegExp = this.urlRegExp;
16809 messages.id = dijit.getUniqueId(this.editor.id);
16810 this._uniqueId = messages.id;
16811 this._setContent(dropDown.title +
16812 "<div style='border-bottom: 1px black solid;padding-bottom:2pt;margin-bottom:4pt'></div>" +
16813 dojo.string.substitute(this.linkDialogTemplate, messages));
16814 dropDown.startup();
16815 this._urlInput = dijit.byId(this._uniqueId + "_urlInput");
16816 this._textInput = dijit.byId(this._uniqueId + "_textInput");
16817 this._setButton = dijit.byId(this._uniqueId + "_setButton");
16818 this.connect(dijit.byId(this._uniqueId + "_cancelButton"), "onClick", function(){
16819 this.dropDown.onCancel();
16820 });
16821 if(this._urlInput){
16822 this.connect(this._urlInput, "onChange", "_checkAndFixInput");
16823 }
16824 if(this._textInput){
16825 this.connect(this._textInput, "onChange", "_checkAndFixInput");
16826 }
16827
16828 // Build up the dual check for http/https/file:, and mailto formats.
16829 this._urlRegExp = new RegExp("^" + this.urlRegExp + "$", "i");
16830 this._emailRegExp = new RegExp("^" + this.emailRegExp + "$", "i");
16831 this._urlInput.isValid = dojo.hitch(this, function(){
16832 // Function over-ride of isValid to test if the input matches a url or a mailto style link.
16833 var value = this._urlInput.get("value");
16834 return this._urlRegExp.test(value) || this._emailRegExp.test(value);
16835 });
16836
16837 this._connectTagEvents();
16838 this.inherited(arguments);
16839 },
16840
16841 _checkAndFixInput: function(){
16842 // summary:
16843 // A function to listen for onChange events and test the input contents
16844 // for valid information, such as valid urls with http/https/ftp and if
16845 // not present, try and guess if the input url is relative or not, and if
16846 // not, append http:// to it. Also validates other fields as determined by
16847 // the internal _isValid function.
16848 var self = this;
16849 var url = this._urlInput.get("value");
16850 var fixupUrl = function(url){
16851 var appendHttp = false;
16852 var appendMailto = false;
16853 if(url && url.length > 1){
16854 url = dojo.trim(url);
16855 if(url.indexOf("mailto:") !== 0){
16856 if(url.indexOf("/") > 0){
16857 if(url.indexOf("://") === -1){
16858 // Check that it doesn't start with / or ./, which would
16859 // imply 'target server relativeness'
16860 if(url.charAt(0) !== '/' && url.indexOf("./") !== 0){
16861 if(self._hostRxp.test(url)){
16862 appendHttp = true;
16863 }
16864 }
16865 }
16866 }else if(self._userAtRxp.test(url)){
16867 // If it looks like a foo@, append a mailto.
16868 appendMailto = true;
16869 }
16870 }
16871 }
16872 if(appendHttp){
16873 self._urlInput.set("value", "http://" + url);
16874 }
16875 if(appendMailto){
16876 self._urlInput.set("value", "mailto:" + url);
16877 }
16878 self._setButton.set("disabled", !self._isValid());
16879 };
16880 if(this._delayedCheck){
16881 clearTimeout(this._delayedCheck);
16882 this._delayedCheck = null;
16883 }
16884 this._delayedCheck = setTimeout(function(){
16885 fixupUrl(url);
16886 }, 250);
16887 },
16888
16889 _connectTagEvents: function(){
16890 // summary:
16891 // Over-ridable function that connects tag specific events.
16892 this.editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
16893 this.connect(this.editor.editNode, "ondblclick", this._onDblClick);
16894 }));
16895 },
16896
16897 _isValid: function(){
16898 // summary:
16899 // Internal function to allow validating of the inputs
16900 // for a link to determine if set should be disabled or not
16901 // tags:
16902 // protected
16903 return this._urlInput.isValid() && this._textInput.isValid();
16904 },
16905
16906 _setContent: function(staticPanel){
16907 // summary:
16908 // Helper for _initButton above. Not sure why it's a separate method.
16909 this.dropDown.set('content', staticPanel);
16910 },
16911
16912 _checkValues: function(args){
16913 // summary:
16914 // Function to check the values in args and 'fix' them up as needed.
16915 // args: Object
16916 // Content being set.
16917 // tags:
16918 // protected
16919 if(args && args.urlInput){
16920 args.urlInput = args.urlInput.replace(/"/g, "&quot;");
16921 }
16922 return args;
16923 },
16924
16925 setValue: function(args){
16926 // summary:
16927 // Callback from the dialog when user presses "set" button.
16928 // tags:
16929 // private
16930 //TODO: prevent closing popup if the text is empty
16931 this._onCloseDialog();
16932 if(dojo.isIE){ //see #4151
16933 var sel = dijit.range.getSelection(this.editor.window);
16934 var range = sel.getRangeAt(0);
16935 var a = range.endContainer;
16936 if(a.nodeType === 3){
16937 // Text node, may be the link contents, so check parent.
16938 // This plugin doesn't really support nested HTML elements
16939 // in the link, it assumes all link content is text.
16940 a = a.parentNode;
16941 }
16942 if(a && (a.nodeName && a.nodeName.toLowerCase() !== this.tag)){
16943 // Stll nothing, one last thing to try on IE, as it might be 'img'
16944 // and thus considered a control.
16945 a = dojo.withGlobal(this.editor.window,
16946 "getSelectedElement", dijit._editor.selection, [this.tag]);
16947 }
16948 if(a && (a.nodeName && a.nodeName.toLowerCase() === this.tag)){
16949 // Okay, we do have a match. IE, for some reason, sometimes pastes before
16950 // instead of removing the targetted paste-over element, so we unlink the
16951 // old one first. If we do not the <a> tag remains, but it has no content,
16952 // so isn't readily visible (but is wrong for the action).
16953 if(this.editor.queryCommandEnabled("unlink")){
16954 // Select all the link childent, then unlink. The following insert will
16955 // then replace the selected text.
16956 dojo.withGlobal(this.editor.window,
16957 "selectElementChildren", dijit._editor.selection, [a]);
16958 this.editor.execCommand("unlink");
16959 }
16960 }
16961 }
16962 // make sure values are properly escaped, etc.
16963 args = this._checkValues(args);
16964 this.editor.execCommand('inserthtml',
16965 dojo.string.substitute(this.htmlTemplate, args));
16966 },
16967
16968 _onCloseDialog: function(){
16969 // summary:
16970 // Handler for close event on the dialog
16971 this.editor.focus();
16972 },
16973
16974 _getCurrentValues: function(a){
16975 // summary:
16976 // Over-ride for getting the values to set in the dropdown.
16977 // a:
16978 // The anchor/link to process for data for the dropdown.
16979 // tags:
16980 // protected
16981 var url, text, target;
16982 if(a && a.tagName.toLowerCase() === this.tag){
16983 url = a.getAttribute('_djrealurl') || a.getAttribute('href');
16984 target = a.getAttribute('target') || "_self";
16985 text = a.textContent || a.innerText;
16986 dojo.withGlobal(this.editor.window, "selectElement", dijit._editor.selection, [a, true]);
16987 }else{
16988 text = dojo.withGlobal(this.editor.window, dijit._editor.selection.getSelectedText);
16989 }
16990 return {urlInput: url || '', textInput: text || '', targetSelect: target || ''}; //Object;
16991 },
16992
16993 _onOpenDialog: function(){
16994 // summary:
16995 // Handler for when the dialog is opened.
16996 // If the caret is currently in a URL then populate the URL's info into the dialog.
16997 var a;
16998 if(dojo.isIE){
16999 // IE is difficult to select the element in, using the range unified
17000 // API seems to work reasonably well.
17001 var sel = dijit.range.getSelection(this.editor.window);
17002 var range = sel.getRangeAt(0);
17003 a = range.endContainer;
17004 if(a.nodeType === 3){
17005 // Text node, may be the link contents, so check parent.
17006 // This plugin doesn't really support nested HTML elements
17007 // in the link, it assumes all link content is text.
17008 a = a.parentNode;
17009 }
17010 if(a && (a.nodeName && a.nodeName.toLowerCase() !== this.tag)){
17011 // Stll nothing, one last thing to try on IE, as it might be 'img'
17012 // and thus considered a control.
17013 a = dojo.withGlobal(this.editor.window,
17014 "getSelectedElement", dijit._editor.selection, [this.tag]);
17015 }
17016 }else{
17017 a = dojo.withGlobal(this.editor.window,
17018 "getAncestorElement", dijit._editor.selection, [this.tag]);
17019 }
17020 this.dropDown.reset();
17021 this._setButton.set("disabled", true);
17022 this.dropDown.set("value", this._getCurrentValues(a));
17023 },
17024
17025 _onDblClick: function(e){
17026 // summary:
17027 // Function to define a behavior on double clicks on the element
17028 // type this dialog edits to select it and pop up the editor
17029 // dialog.
17030 // e: Object
17031 // The double-click event.
17032 // tags:
17033 // protected.
17034 if(e && e.target){
17035 var t = e.target;
17036 var tg = t.tagName? t.tagName.toLowerCase() : "";
17037 if(tg === this.tag && dojo.attr(t,"href")){
17038 dojo.withGlobal(this.editor.window,
17039 "selectElement",
17040 dijit._editor.selection, [t]);
17041 this.editor.onDisplayChanged();
17042 setTimeout(dojo.hitch(this, function(){
17043 // Focus shift outside the event handler.
17044 // IE doesn't like focus changes in event handles.
17045 this.button.set("disabled", false);
17046 this.button.openDropDown();
17047 }), 10);
17048 }
17049 }
17050 }
17051});
17052
17053dojo.declare("dijit._editor.plugins.ImgLinkDialog", [dijit._editor.plugins.LinkDialog], {
17054 // summary:
17055 // This plugin extends LinkDialog and adds in a plugin for handling image links.
17056 // provides the image link dialog.
17057 //
17058 // description:
17059 // The command provided by this plugin is:
17060 // * insertImage
17061
17062 // linkDialogTemplate: [protected] String
17063 // Over-ride for template since img dialog doesn't need target that anchor tags may.
17064 linkDialogTemplate: [
17065 "<table><tr><td>",
17066 "<label for='${id}_urlInput'>${url}</label>",
17067 "</td><td>",
17068 "<input dojoType='dijit.form.ValidationTextBox' regExp='${urlRegExp}' " +
17069 "required='true' id='${id}_urlInput' name='urlInput' intermediateChanges='true'>",
17070 "</td></tr><tr><td>",
17071 "<label for='${id}_textInput'>${text}</label>",
17072 "</td><td>",
17073 "<input dojoType='dijit.form.ValidationTextBox' required='false' id='${id}_textInput' " +
17074 "name='textInput' intermediateChanges='true'>",
17075 "</td></tr><tr><td>",
17076 "</td><td>",
17077 "</td></tr><tr><td colspan='2'>",
17078 "<button dojoType='dijit.form.Button' type='submit' id='${id}_setButton'>${set}</button>",
17079 "<button dojoType='dijit.form.Button' type='button' id='${id}_cancelButton'>${buttonCancel}</button>",
17080 "</td></tr></table>"
17081 ].join(""),
17082
17083 // htmlTemplate: [protected] String
17084 // String used for templating the <img> HTML to insert at the desired point.
17085 htmlTemplate: "<img src=\"${urlInput}\" _djrealurl=\"${urlInput}\" alt=\"${textInput}\" />",
17086
17087 // tag: [protected] String
17088 // Tag used for the link type (img).
17089 tag: "img",
17090
17091 _getCurrentValues: function(img){
17092 // summary:
17093 // Over-ride for getting the values to set in the dropdown.
17094 // a:
17095 // The anchor/link to process for data for the dropdown.
17096 // tags:
17097 // protected
17098 var url, text;
17099 if(img && img.tagName.toLowerCase() === this.tag){
17100 url = img.getAttribute('_djrealurl') || img.getAttribute('src');
17101 text = img.getAttribute('alt');
17102 dojo.withGlobal(this.editor.window,
17103 "selectElement", dijit._editor.selection, [img, true]);
17104 }else{
17105 text = dojo.withGlobal(this.editor.window, dijit._editor.selection.getSelectedText);
17106 }
17107 return {urlInput: url || '', textInput: text || ''}; //Object;
17108 },
17109
17110 _isValid: function(){
17111 // summary:
17112 // Over-ride for images. You can have alt text of blank, it is valid.
17113 // tags:
17114 // protected
17115 return this._urlInput.isValid();
17116 },
17117
17118 _connectTagEvents: function(){
17119 // summary:
17120 // Over-ridable function that connects tag specific events.
17121 this.inherited(arguments);
17122 this.editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
17123 // Use onmousedown instead of onclick. Seems that IE eats the first onclick
17124 // to wrap it in a selector box, then the second one acts as onclick. See #10420
17125 this.connect(this.editor.editNode, "onmousedown", this._selectTag);
17126 }));
17127 },
17128
17129 _selectTag: function(e){
17130 // summary:
17131 // A simple event handler that lets me select an image if it is clicked on.
17132 // makes it easier to select images in a standard way across browsers. Otherwise
17133 // selecting an image for edit becomes difficult.
17134 // e: Event
17135 // The mousedown event.
17136 // tags:
17137 // private
17138 if(e && e.target){
17139 var t = e.target;
17140 var tg = t.tagName? t.tagName.toLowerCase() : "";
17141 if(tg === this.tag){
17142 dojo.withGlobal(this.editor.window,
17143 "selectElement",
17144 dijit._editor.selection, [t]);
17145 }
17146 }
17147 },
17148
17149 _checkValues: function(args){
17150 // summary:
17151 // Function to check the values in args and 'fix' them up as needed
17152 // (special characters in the url or alt text)
17153 // args: Object
17154 // Content being set.
17155 // tags:
17156 // protected
17157 if(args && args.urlInput){
17158 args.urlInput = args.urlInput.replace(/"/g, "&quot;");
17159 }
17160 if(args && args.textInput){
17161 args.textInput = args.textInput.replace(/"/g, "&quot;");
17162 }
17163 return args;
17164 },
17165
17166 _onDblClick: function(e){
17167 // summary:
17168 // Function to define a behavior on double clicks on the element
17169 // type this dialog edits to select it and pop up the editor
17170 // dialog.
17171 // e: Object
17172 // The double-click event.
17173 // tags:
17174 // protected.
17175 if(e && e.target){
17176 var t = e.target;
17177 var tg = t.tagName? t.tagName.toLowerCase() : "";
17178 if(tg === this.tag && dojo.attr(t,"src")){
17179 dojo.withGlobal(this.editor.window,
17180 "selectElement",
17181 dijit._editor.selection, [t]);
17182 this.editor.onDisplayChanged();
17183 setTimeout(dojo.hitch(this, function(){
17184 // Focus shift outside the event handler.
17185 // IE doesn't like focus changes in event handles.
17186 this.button.set("disabled", false);
17187 this.button.openDropDown();
17188 }), 10);
17189 }
17190 }
17191 }
17192});
17193
17194// Register this plugin.
17195dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){
17196 if(o.plugin){ return; }
17197 switch(o.args.name){
17198 case "createLink":
17199 o.plugin = new dijit._editor.plugins.LinkDialog({command: o.args.name});
17200 break;
17201 case "insertImage":
17202 o.plugin = new dijit._editor.plugins.ImgLinkDialog({command: o.args.name});
17203 break;
17204 }
17205});
17206
17207}
17208
17209if(!dojo._hasResource["dijit.MenuBar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
17210dojo._hasResource["dijit.MenuBar"] = true;
17211dojo.provide("dijit.MenuBar");
17212
17213
17214
17215dojo.declare("dijit.MenuBar", dijit._MenuBase, {
17216 // summary:
17217 // A menu bar, listing menu choices horizontally, like the "File" menu in most desktop applications
17218
17219 templateString: dojo.cache("dijit", "templates/MenuBar.html", "<div class=\"dijitMenuBar dijitMenuPassive\" dojoAttachPoint=\"containerNode\" waiRole=\"menubar\" tabIndex=\"${tabIndex}\" dojoAttachEvent=\"onkeypress: _onKeyPress\"></div>\n"),
17220
17221 baseClass: "dijitMenuBar",
17222
17223 // _isMenuBar: [protected] Boolean
17224 // This is a MenuBar widget, not a (vertical) Menu widget.
17225 _isMenuBar: true,
17226
17227 postCreate: function(){
17228 var k = dojo.keys, l = this.isLeftToRight();
17229 this.connectKeyNavHandlers(
17230 l ? [k.LEFT_ARROW] : [k.RIGHT_ARROW],
17231 l ? [k.RIGHT_ARROW] : [k.LEFT_ARROW]
17232 );
17233
17234 // parameter to dijit.popup.open() about where to put popup (relative to this.domNode)
17235 this._orient = this.isLeftToRight() ? {BL: 'TL'} : {BR: 'TR'};
17236 },
17237
17238 focusChild: function(item){
17239 // overload focusChild so that whenever the focus is moved to a new item,
17240 // check the previous focused whether it has its popup open, if so, after
17241 // focusing the new item, open its submenu immediately
17242 var prev_item = this.focusedChild,
17243 showpopup = prev_item && prev_item.popup && prev_item.popup.isShowingNow;
17244 this.inherited(arguments);
17245 if(showpopup && item.popup && !item.disabled){
17246 this._openPopup(); // TODO: on down arrow, _openPopup() is called here and in onItemClick()
17247 }
17248 },
17249
17250 _onKeyPress: function(/*Event*/ evt){
17251 // summary:
17252 // Handle keyboard based menu navigation.
17253 // tags:
17254 // protected
17255
17256 if(evt.ctrlKey || evt.altKey){ return; }
17257
17258 switch(evt.charOrCode){
17259 case dojo.keys.DOWN_ARROW:
17260 this._moveToPopup(evt);
17261 dojo.stopEvent(evt);
17262 }
17263 },
17264
17265 onItemClick: function(/*dijit._Widget*/ item, /*Event*/ evt){
17266 // summary:
17267 // Handle clicks on an item. Cancels a dropdown if already open.
17268 // tags:
17269 // private
17270 if(item.popup && item.popup.isShowingNow){
17271 item.popup.onCancel();
17272 }else{
17273 this.inherited(arguments);
17274 }
17275 }
17276});
17277
17278}
17279
17280if(!dojo._hasResource["dijit.MenuBarItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
17281dojo._hasResource["dijit.MenuBarItem"] = true;
17282dojo.provide("dijit.MenuBarItem");
17283
17284
17285
17286dojo.declare("dijit._MenuBarItemMixin", null, {
17287 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"),
17288
17289 // overriding attributeMap because we don't have icon
17290 attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
17291 label: { node: "containerNode", type: "innerHTML" }
17292 })
17293});
17294
17295dojo.declare("dijit.MenuBarItem", [dijit.MenuItem, dijit._MenuBarItemMixin], {
17296 // summary:
17297 // Item in a MenuBar that's clickable, and doesn't spawn a submenu when pressed (or hovered)
17298
17299});
17300
17301}
17302
17303if(!dojo._hasResource["dijit.PopupMenuBarItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
17304dojo._hasResource["dijit.PopupMenuBarItem"] = true;
17305dojo.provide("dijit.PopupMenuBarItem");
17306
17307
17308
17309
17310dojo.declare("dijit.PopupMenuBarItem", [dijit.PopupMenuItem, dijit._MenuBarItemMixin], {
17311 // summary:
17312 // Item in a MenuBar like "File" or "Edit", that spawns a submenu when pressed (or hovered)
17313});
17314
17315
17316}
17317
17318if(!dojo._hasResource["dojo.number"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
17319dojo._hasResource["dojo.number"] = true;
17320dojo.provide("dojo.number");
17321
17322
17323
17324
17325
17326
17327
17328/*=====
17329dojo.number = {
17330 // summary: localized formatting and parsing routines for Number
17331}
17332
17333dojo.number.__FormatOptions = function(){
17334 // pattern: String?
17335 // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
17336 // with this string. Default value is based on locale. Overriding this property will defeat
17337 // localization. Literal characters in patterns are not supported.
17338 // type: String?
17339 // choose a format type based on the locale from the following:
17340 // decimal, scientific (not yet supported), percent, currency. decimal by default.
17341 // places: Number?
17342 // fixed number of decimal places to show. This overrides any
17343 // information in the provided pattern.
17344 // round: Number?
17345 // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
17346 // means do not round.
17347 // locale: String?
17348 // override the locale used to determine formatting rules
17349 // fractional: Boolean?
17350 // If false, show no decimal places, overriding places and pattern settings.
17351 this.pattern = pattern;
17352 this.type = type;
17353 this.places = places;
17354 this.round = round;
17355 this.locale = locale;
17356 this.fractional = fractional;
17357}
17358=====*/
17359
17360dojo.number.format = function(/*Number*/value, /*dojo.number.__FormatOptions?*/options){
17361 // summary:
17362 // Format a Number as a String, using locale-specific settings
17363 // description:
17364 // Create a string from a Number using a known localized pattern.
17365 // Formatting patterns appropriate to the locale are chosen from the
17366 // [Common Locale Data Repository](http://unicode.org/cldr) as well as the appropriate symbols and
17367 // delimiters.
17368 // If value is Infinity, -Infinity, or is not a valid JavaScript number, return null.
17369 // value:
17370 // the number to be formatted
17371
17372 options = dojo.mixin({}, options || {});
17373 var locale = dojo.i18n.normalizeLocale(options.locale),
17374 bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale);
17375 options.customs = bundle;
17376 var pattern = options.pattern || bundle[(options.type || "decimal") + "Format"];
17377 if(isNaN(value) || Math.abs(value) == Infinity){ return null; } // null
17378 return dojo.number._applyPattern(value, pattern, options); // String
17379};
17380
17381//dojo.number._numberPatternRE = /(?:[#0]*,?)*[#0](?:\.0*#*)?/; // not precise, but good enough
17382dojo.number._numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough
17383
17384dojo.number._applyPattern = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatOptions?*/options){
17385 // summary:
17386 // Apply pattern to format value as a string using options. Gives no
17387 // consideration to local customs.
17388 // value:
17389 // the number to be formatted.
17390 // pattern:
17391 // a pattern string as described by
17392 // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
17393 // options: dojo.number.__FormatOptions?
17394 // _applyPattern is usually called via `dojo.number.format()` which
17395 // populates an extra property in the options parameter, "customs".
17396 // The customs object specifies group and decimal parameters if set.
17397
17398 //TODO: support escapes
17399 options = options || {};
17400 var group = options.customs.group,
17401 decimal = options.customs.decimal,
17402 patternList = pattern.split(';'),
17403 positivePattern = patternList[0];
17404 pattern = patternList[(value < 0) ? 1 : 0] || ("-" + positivePattern);
17405
17406 //TODO: only test against unescaped
17407 if(pattern.indexOf('%') != -1){
17408 value *= 100;
17409 }else if(pattern.indexOf('\u2030') != -1){
17410 value *= 1000; // per mille
17411 }else if(pattern.indexOf('\u00a4') != -1){
17412 group = options.customs.currencyGroup || group;//mixins instead?
17413 decimal = options.customs.currencyDecimal || decimal;// Should these be mixins instead?
17414 pattern = pattern.replace(/\u00a4{1,3}/, function(match){
17415 var prop = ["symbol", "currency", "displayName"][match.length-1];
17416 return options[prop] || options.currency || "";
17417 });
17418 }else if(pattern.indexOf('E') != -1){
17419 throw new Error("exponential notation not supported");
17420 }
17421
17422 //TODO: support @ sig figs?
17423 var numberPatternRE = dojo.number._numberPatternRE;
17424 var numberPattern = positivePattern.match(numberPatternRE);
17425 if(!numberPattern){
17426 throw new Error("unable to find a number expression in pattern: "+pattern);
17427 }
17428 if(options.fractional === false){ options.places = 0; }
17429 return pattern.replace(numberPatternRE,
17430 dojo.number._formatAbsolute(value, numberPattern[0], {decimal: decimal, group: group, places: options.places, round: options.round}));
17431}
17432
17433dojo.number.round = function(/*Number*/value, /*Number?*/places, /*Number?*/increment){
17434 // summary:
17435 // Rounds to the nearest value with the given number of decimal places, away from zero
17436 // description:
17437 // Rounds to the nearest value with the given number of decimal places, away from zero if equal.
17438 // Similar to Number.toFixed(), but compensates for browser quirks. Rounding can be done by
17439 // fractional increments also, such as the nearest quarter.
17440 // NOTE: Subject to floating point errors. See dojox.math.round for experimental workaround.
17441 // value:
17442 // The number to round
17443 // places:
17444 // The number of decimal places where rounding takes place. Defaults to 0 for whole rounding.
17445 // Must be non-negative.
17446 // increment:
17447 // Rounds next place to nearest value of increment/10. 10 by default.
17448 // example:
17449 // >>> dojo.number.round(-0.5)
17450 // -1
17451 // >>> dojo.number.round(162.295, 2)
17452 // 162.29 // note floating point error. Should be 162.3
17453 // >>> dojo.number.round(10.71, 0, 2.5)
17454 // 10.75
17455 var factor = 10 / (increment || 10);
17456 return (factor * +value).toFixed(places) / factor; // Number
17457}
17458
17459if((0.9).toFixed() == 0){
17460 // (isIE) toFixed() bug workaround: Rounding fails on IE when most significant digit
17461 // is just after the rounding place and is >=5
17462 (function(){
17463 var round = dojo.number.round;
17464 dojo.number.round = function(v, p, m){
17465 var d = Math.pow(10, -p || 0), a = Math.abs(v);
17466 if(!v || a >= d || a * Math.pow(10, p + 1) < 5){
17467 d = 0;
17468 }
17469 return round(v, p, m) + (v > 0 ? d : -d);
17470 }
17471 })();
17472}
17473
17474/*=====
17475dojo.number.__FormatAbsoluteOptions = function(){
17476 // decimal: String?
17477 // the decimal separator
17478 // group: String?
17479 // the group separator
17480 // places: Number?|String?
17481 // number of decimal places. the range "n,m" will format to m places.
17482 // round: Number?
17483 // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
17484 // means don't round.
17485 this.decimal = decimal;
17486 this.group = group;
17487 this.places = places;
17488 this.round = round;
17489}
17490=====*/
17491
17492dojo.number._formatAbsolute = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatAbsoluteOptions?*/options){
17493 // summary:
17494 // Apply numeric pattern to absolute value using options. Gives no
17495 // consideration to local customs.
17496 // value:
17497 // the number to be formatted, ignores sign
17498 // pattern:
17499 // the number portion of a pattern (e.g. `#,##0.00`)
17500 options = options || {};
17501 if(options.places === true){options.places=0;}
17502 if(options.places === Infinity){options.places=6;} // avoid a loop; pick a limit
17503
17504 var patternParts = pattern.split("."),
17505 comma = typeof options.places == "string" && options.places.indexOf(","),
17506 maxPlaces = options.places;
17507 if(comma){
17508 maxPlaces = options.places.substring(comma + 1);
17509 }else if(!(maxPlaces >= 0)){
17510 maxPlaces = (patternParts[1] || []).length;
17511 }
17512 if(!(options.round < 0)){
17513 value = dojo.number.round(value, maxPlaces, options.round);
17514 }
17515
17516 var valueParts = String(Math.abs(value)).split("."),
17517 fractional = valueParts[1] || "";
17518 if(patternParts[1] || options.places){
17519 if(comma){
17520 options.places = options.places.substring(0, comma);
17521 }
17522 // Pad fractional with trailing zeros
17523 var pad = options.places !== undefined ? options.places : (patternParts[1] && patternParts[1].lastIndexOf("0") + 1);
17524 if(pad > fractional.length){
17525 valueParts[1] = dojo.string.pad(fractional, pad, '0', true);
17526 }
17527
17528 // Truncate fractional
17529 if(maxPlaces < fractional.length){
17530 valueParts[1] = fractional.substr(0, maxPlaces);
17531 }
17532 }else{
17533 if(valueParts[1]){ valueParts.pop(); }
17534 }
17535
17536 // Pad whole with leading zeros
17537 var patternDigits = patternParts[0].replace(',', '');
17538 pad = patternDigits.indexOf("0");
17539 if(pad != -1){
17540 pad = patternDigits.length - pad;
17541 if(pad > valueParts[0].length){
17542 valueParts[0] = dojo.string.pad(valueParts[0], pad);
17543 }
17544
17545 // Truncate whole
17546 if(patternDigits.indexOf("#") == -1){
17547 valueParts[0] = valueParts[0].substr(valueParts[0].length - pad);
17548 }
17549 }
17550
17551 // Add group separators
17552 var index = patternParts[0].lastIndexOf(','),
17553 groupSize, groupSize2;
17554 if(index != -1){
17555 groupSize = patternParts[0].length - index - 1;
17556 var remainder = patternParts[0].substr(0, index);
17557 index = remainder.lastIndexOf(',');
17558 if(index != -1){
17559 groupSize2 = remainder.length - index - 1;
17560 }
17561 }
17562 var pieces = [];
17563 for(var whole = valueParts[0]; whole;){
17564 var off = whole.length - groupSize;
17565 pieces.push((off > 0) ? whole.substr(off) : whole);
17566 whole = (off > 0) ? whole.slice(0, off) : "";
17567 if(groupSize2){
17568 groupSize = groupSize2;
17569 delete groupSize2;
17570 }
17571 }
17572 valueParts[0] = pieces.reverse().join(options.group || ",");
17573
17574 return valueParts.join(options.decimal || ".");
17575};
17576
17577/*=====
17578dojo.number.__RegexpOptions = function(){
17579 // pattern: String?
17580 // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
17581 // with this string. Default value is based on locale. Overriding this property will defeat
17582 // localization.
17583 // type: String?
17584 // choose a format type based on the locale from the following:
17585 // decimal, scientific (not yet supported), percent, currency. decimal by default.
17586 // locale: String?
17587 // override the locale used to determine formatting rules
17588 // strict: Boolean?
17589 // strict parsing, false by default. Strict parsing requires input as produced by the format() method.
17590 // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
17591 // places: Number|String?
17592 // number of decimal places to accept: Infinity, a positive number, or
17593 // a range "n,m". Defined by pattern or Infinity if pattern not provided.
17594 this.pattern = pattern;
17595 this.type = type;
17596 this.locale = locale;
17597 this.strict = strict;
17598 this.places = places;
17599}
17600=====*/
17601dojo.number.regexp = function(/*dojo.number.__RegexpOptions?*/options){
17602 // summary:
17603 // Builds the regular needed to parse a number
17604 // description:
17605 // Returns regular expression with positive and negative match, group
17606 // and decimal separators
17607 return dojo.number._parseInfo(options).regexp; // String
17608}
17609
17610dojo.number._parseInfo = function(/*Object?*/options){
17611 options = options || {};
17612 var locale = dojo.i18n.normalizeLocale(options.locale),
17613 bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale),
17614 pattern = options.pattern || bundle[(options.type || "decimal") + "Format"],
17615//TODO: memoize?
17616 group = bundle.group,
17617 decimal = bundle.decimal,
17618 factor = 1;
17619
17620 if(pattern.indexOf('%') != -1){
17621 factor /= 100;
17622 }else if(pattern.indexOf('\u2030') != -1){
17623 factor /= 1000; // per mille
17624 }else{
17625 var isCurrency = pattern.indexOf('\u00a4') != -1;
17626 if(isCurrency){
17627 group = bundle.currencyGroup || group;
17628 decimal = bundle.currencyDecimal || decimal;
17629 }
17630 }
17631
17632 //TODO: handle quoted escapes
17633 var patternList = pattern.split(';');
17634 if(patternList.length == 1){
17635 patternList.push("-" + patternList[0]);
17636 }
17637
17638 var re = dojo.regexp.buildGroupRE(patternList, function(pattern){
17639 pattern = "(?:"+dojo.regexp.escapeString(pattern, '.')+")";
17640 return pattern.replace(dojo.number._numberPatternRE, function(format){
17641 var flags = {
17642 signed: false,
17643 separator: options.strict ? group : [group,""],
17644 fractional: options.fractional,
17645 decimal: decimal,
17646 exponent: false
17647 },
17648
17649 parts = format.split('.'),
17650 places = options.places;
17651
17652 // special condition for percent (factor != 1)
17653 // allow decimal places even if not specified in pattern
17654 if(parts.length == 1 && factor != 1){
17655 parts[1] = "###";
17656 }
17657 if(parts.length == 1 || places === 0){
17658 flags.fractional = false;
17659 }else{
17660 if(places === undefined){ places = options.pattern ? parts[1].lastIndexOf('0') + 1 : Infinity; }
17661 if(places && options.fractional == undefined){flags.fractional = true;} // required fractional, unless otherwise specified
17662 if(!options.places && (places < parts[1].length)){ places += "," + parts[1].length; }
17663 flags.places = places;
17664 }
17665 var groups = parts[0].split(',');
17666 if(groups.length > 1){
17667 flags.groupSize = groups.pop().length;
17668 if(groups.length > 1){
17669 flags.groupSize2 = groups.pop().length;
17670 }
17671 }
17672 return "("+dojo.number._realNumberRegexp(flags)+")";
17673 });
17674 }, true);
17675
17676 if(isCurrency){
17677 // substitute the currency symbol for the placeholder in the pattern
17678 re = re.replace(/([\s\xa0]*)(\u00a4{1,3})([\s\xa0]*)/g, function(match, before, target, after){
17679 var prop = ["symbol", "currency", "displayName"][target.length-1],
17680 symbol = dojo.regexp.escapeString(options[prop] || options.currency || "");
17681 before = before ? "[\\s\\xa0]" : "";
17682 after = after ? "[\\s\\xa0]" : "";
17683 if(!options.strict){
17684 if(before){before += "*";}
17685 if(after){after += "*";}
17686 return "(?:"+before+symbol+after+")?";
17687 }
17688 return before+symbol+after;
17689 });
17690 }
17691
17692//TODO: substitute localized sign/percent/permille/etc.?
17693
17694 // normalize whitespace and return
17695 return {regexp: re.replace(/[\xa0 ]/g, "[\\s\\xa0]"), group: group, decimal: decimal, factor: factor}; // Object
17696}
17697
17698/*=====
17699dojo.number.__ParseOptions = function(){
17700 // pattern: String?
17701 // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
17702 // with this string. Default value is based on locale. Overriding this property will defeat
17703 // localization. Literal characters in patterns are not supported.
17704 // type: String?
17705 // choose a format type based on the locale from the following:
17706 // decimal, scientific (not yet supported), percent, currency. decimal by default.
17707 // locale: String?
17708 // override the locale used to determine formatting rules
17709 // strict: Boolean?
17710 // strict parsing, false by default. Strict parsing requires input as produced by the format() method.
17711 // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
17712 // fractional: Boolean?|Array?
17713 // Whether to include the fractional portion, where the number of decimal places are implied by pattern
17714 // or explicit 'places' parameter. The value [true,false] makes the fractional portion optional.
17715 this.pattern = pattern;
17716 this.type = type;
17717 this.locale = locale;
17718 this.strict = strict;
17719 this.fractional = fractional;
17720}
17721=====*/
17722dojo.number.parse = function(/*String*/expression, /*dojo.number.__ParseOptions?*/options){
17723 // summary:
17724 // Convert a properly formatted string to a primitive Number, using
17725 // locale-specific settings.
17726 // description:
17727 // Create a Number from a string using a known localized pattern.
17728 // Formatting patterns are chosen appropriate to the locale
17729 // and follow the syntax described by
17730 // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
17731 // Note that literal characters in patterns are not supported.
17732 // expression:
17733 // A string representation of a Number
17734 var info = dojo.number._parseInfo(options),
17735 results = (new RegExp("^"+info.regexp+"$")).exec(expression);
17736 if(!results){
17737 return NaN; //NaN
17738 }
17739 var absoluteMatch = results[1]; // match for the positive expression
17740 if(!results[1]){
17741 if(!results[2]){
17742 return NaN; //NaN
17743 }
17744 // matched the negative pattern
17745 absoluteMatch =results[2];
17746 info.factor *= -1;
17747 }
17748
17749 // Transform it to something Javascript can parse as a number. Normalize
17750 // decimal point and strip out group separators or alternate forms of whitespace
17751 absoluteMatch = absoluteMatch.
17752 replace(new RegExp("["+info.group + "\\s\\xa0"+"]", "g"), "").
17753 replace(info.decimal, ".");
17754 // Adjust for negative sign, percent, etc. as necessary
17755 return absoluteMatch * info.factor; //Number
17756};
17757
17758/*=====
17759dojo.number.__RealNumberRegexpFlags = function(){
17760 // places: Number?
17761 // The integer number of decimal places or a range given as "n,m". If
17762 // not given, the decimal part is optional and the number of places is
17763 // unlimited.
17764 // decimal: String?
17765 // A string for the character used as the decimal point. Default
17766 // is ".".
17767 // fractional: Boolean?|Array?
17768 // Whether decimal places are used. Can be true, false, or [true,
17769 // false]. Default is [true, false] which means optional.
17770 // exponent: Boolean?|Array?
17771 // Express in exponential notation. Can be true, false, or [true,
17772 // false]. Default is [true, false], (i.e. will match if the
17773 // exponential part is present are not).
17774 // eSigned: Boolean?|Array?
17775 // The leading plus-or-minus sign on the exponent. Can be true,
17776 // false, or [true, false]. Default is [true, false], (i.e. will
17777 // match if it is signed or unsigned). flags in regexp.integer can be
17778 // applied.
17779 this.places = places;
17780 this.decimal = decimal;
17781 this.fractional = fractional;
17782 this.exponent = exponent;
17783 this.eSigned = eSigned;
17784}
17785=====*/
17786
17787dojo.number._realNumberRegexp = function(/*dojo.number.__RealNumberRegexpFlags?*/flags){
17788 // summary:
17789 // Builds a regular expression to match a real number in exponential
17790 // notation
17791
17792 // assign default values to missing parameters
17793 flags = flags || {};
17794 //TODO: use mixin instead?
17795 if(!("places" in flags)){ flags.places = Infinity; }
17796 if(typeof flags.decimal != "string"){ flags.decimal = "."; }
17797 if(!("fractional" in flags) || /^0/.test(flags.places)){ flags.fractional = [true, false]; }
17798 if(!("exponent" in flags)){ flags.exponent = [true, false]; }
17799 if(!("eSigned" in flags)){ flags.eSigned = [true, false]; }
17800
17801 var integerRE = dojo.number._integerRegexp(flags),
17802 decimalRE = dojo.regexp.buildGroupRE(flags.fractional,
17803 function(q){
17804 var re = "";
17805 if(q && (flags.places!==0)){
17806 re = "\\" + flags.decimal;
17807 if(flags.places == Infinity){
17808 re = "(?:" + re + "\\d+)?";
17809 }else{
17810 re += "\\d{" + flags.places + "}";
17811 }
17812 }
17813 return re;
17814 },
17815 true
17816 );
17817
17818 var exponentRE = dojo.regexp.buildGroupRE(flags.exponent,
17819 function(q){
17820 if(q){ return "([eE]" + dojo.number._integerRegexp({ signed: flags.eSigned}) + ")"; }
17821 return "";
17822 }
17823 );
17824
17825 var realRE = integerRE + decimalRE;
17826 // allow for decimals without integers, e.g. .25
17827 if(decimalRE){realRE = "(?:(?:"+ realRE + ")|(?:" + decimalRE + "))";}
17828 return realRE + exponentRE; // String
17829};
17830
17831/*=====
17832dojo.number.__IntegerRegexpFlags = function(){
17833 // signed: Boolean?
17834 // The leading plus-or-minus sign. Can be true, false, or `[true,false]`.
17835 // Default is `[true, false]`, (i.e. will match if it is signed
17836 // or unsigned).
17837 // separator: String?
17838 // The character used as the thousands separator. Default is no
17839 // separator. For more than one symbol use an array, e.g. `[",", ""]`,
17840 // makes ',' optional.
17841 // groupSize: Number?
17842 // group size between separators
17843 // groupSize2: Number?
17844 // second grouping, where separators 2..n have a different interval than the first separator (for India)
17845 this.signed = signed;
17846 this.separator = separator;
17847 this.groupSize = groupSize;
17848 this.groupSize2 = groupSize2;
17849}
17850=====*/
17851
17852dojo.number._integerRegexp = function(/*dojo.number.__IntegerRegexpFlags?*/flags){
17853 // summary:
17854 // Builds a regular expression that matches an integer
17855
17856 // assign default values to missing parameters
17857 flags = flags || {};
17858 if(!("signed" in flags)){ flags.signed = [true, false]; }
17859 if(!("separator" in flags)){
17860 flags.separator = "";
17861 }else if(!("groupSize" in flags)){
17862 flags.groupSize = 3;
17863 }
17864
17865 var signRE = dojo.regexp.buildGroupRE(flags.signed,
17866 function(q){ return q ? "[-+]" : ""; },
17867 true
17868 );
17869
17870 var numberRE = dojo.regexp.buildGroupRE(flags.separator,
17871 function(sep){
17872 if(!sep){
17873 return "(?:\\d+)";
17874 }
17875
17876 sep = dojo.regexp.escapeString(sep);
17877 if(sep == " "){ sep = "\\s"; }
17878 else if(sep == "\xa0"){ sep = "\\s\\xa0"; }
17879
17880 var grp = flags.groupSize, grp2 = flags.groupSize2;
17881 //TODO: should we continue to enforce that numbers with separators begin with 1-9? See #6933
17882 if(grp2){
17883 var grp2RE = "(?:0|[1-9]\\d{0," + (grp2-1) + "}(?:[" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})";
17884 return ((grp-grp2) > 0) ? "(?:" + grp2RE + "|(?:0|[1-9]\\d{0," + (grp-1) + "}))" : grp2RE;
17885 }
17886 return "(?:0|[1-9]\\d{0," + (grp-1) + "}(?:[" + sep + "]\\d{" + grp + "})*)";
17887 },
17888 true
17889 );
17890
17891 return signRE + numberRE; // String
17892}
17893
17894}
17895
17896if(!dojo._hasResource["dijit.ProgressBar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
17897dojo._hasResource["dijit.ProgressBar"] = true;
17898dojo.provide("dijit.ProgressBar");
17899
17900
17901
17902
17903
17904
17905
17906dojo.declare("dijit.ProgressBar", [dijit._Widget, dijit._Templated], {
17907 // summary:
17908 // A progress indication widget, showing the amount completed
17909 // (often the percentage completed) of a task.
17910 //
17911 // example:
17912 // | <div dojoType="ProgressBar"
17913 // | places="0"
17914 // | progress="..." maximum="...">
17915 // | </div>
17916 //
17917 // description:
17918 // Note that the progress bar is updated via (a non-standard)
17919 // update() method, rather than via attr() like other widgets.
17920
17921 // progress: [const] String (Percentage or Number)
17922 // Number or percentage indicating amount of task completed.
17923 // With "%": percentage value, 0% <= progress <= 100%, or
17924 // without "%": absolute value, 0 <= progress <= maximum
17925 // TODO: rename to value for 2.0
17926 progress: "0",
17927
17928 // maximum: [const] Float
17929 // Max sample number
17930 maximum: 100,
17931
17932 // places: [const] Number
17933 // Number of places to show in values; 0 by default
17934 places: 0,
17935
17936 // indeterminate: [const] Boolean
17937 // If false: show progress value (number or percentage).
17938 // If true: show that a process is underway but that the amount completed is unknown.
17939 indeterminate: false,
17940
17941 // name: String
17942 // this is the field name (for a form) if set. This needs to be set if you want to use
17943 // this widget in a dijit.form.Form widget (such as dijit.Dialog)
17944 name: '',
17945
17946 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\">&nbsp;</span\n\t></div\n\t><div dojoAttachPoint=\"label\" class=\"dijitProgressBarLabel\" id=\"${id}_label\">&nbsp;</div\n\t><img dojoAttachPoint=\"indeterminateHighContrastImage\" class=\"dijitProgressBarIndeterminateHighContrastImage\" alt=\"\"\n/></div>\n"),
17947
17948 // _indeterminateHighContrastImagePath: [private] dojo._URL
17949 // URL to image to use for indeterminate progress bar when display is in high contrast mode
17950 _indeterminateHighContrastImagePath:
17951 dojo.moduleUrl("dijit", "themes/a11y/indeterminate_progress.gif"),
17952
17953 // public functions
17954 postCreate: function(){
17955 this.inherited(arguments);
17956 this.indeterminateHighContrastImage.setAttribute("src",
17957 this._indeterminateHighContrastImagePath.toString());
17958 this.update();
17959 },
17960
17961 update: function(/*Object?*/attributes){
17962 // summary:
17963 // Change attributes of ProgressBar, similar to attr(hash).
17964 //
17965 // attributes:
17966 // May provide progress and/or maximum properties on this parameter;
17967 // see attribute specs for details.
17968 //
17969 // example:
17970 // | myProgressBar.update({'indeterminate': true});
17971 // | myProgressBar.update({'progress': 80});
17972
17973 // TODO: deprecate this method and use set() instead
17974
17975 dojo.mixin(this, attributes || {});
17976 var tip = this.internalProgress;
17977 var percent = 1, classFunc;
17978 if(this.indeterminate){
17979 classFunc = "addClass";
17980 dijit.removeWaiState(tip, "valuenow");
17981 dijit.removeWaiState(tip, "valuemin");
17982 dijit.removeWaiState(tip, "valuemax");
17983 }else{
17984 classFunc = "removeClass";
17985 if(String(this.progress).indexOf("%") != -1){
17986 percent = Math.min(parseFloat(this.progress)/100, 1);
17987 this.progress = percent * this.maximum;
17988 }else{
17989 this.progress = Math.min(this.progress, this.maximum);
17990 percent = this.progress / this.maximum;
17991 }
17992 var text = this.report(percent);
17993 this.label.firstChild.nodeValue = text;
17994 dijit.setWaiState(tip, "describedby", this.label.id);
17995 dijit.setWaiState(tip, "valuenow", this.progress);
17996 dijit.setWaiState(tip, "valuemin", 0);
17997 dijit.setWaiState(tip, "valuemax", this.maximum);
17998 }
17999 dojo[classFunc](this.domNode, "dijitProgressBarIndeterminate");
18000 tip.style.width = (percent * 100) + "%";
18001 this.onChange();
18002 },
18003
18004 _setValueAttr: function(v){
18005 if(v == Infinity){
18006 this.update({indeterminate:true});
18007 }else{
18008 this.update({indeterminate:false, progress:v});
18009 }
18010 },
18011
18012 _getValueAttr: function(){
18013 return this.progress;
18014 },
18015
18016 report: function(/*float*/percent){
18017 // summary:
18018 // Generates message to show inside progress bar (normally indicating amount of task completed).
18019 // May be overridden.
18020 // tags:
18021 // extension
18022
18023 return dojo.number.format(percent, { type: "percent", places: this.places, locale: this.lang });
18024 },
18025
18026 onChange: function(){
18027 // summary:
18028 // Callback fired when progress updates.
18029 // tags:
18030 // progress
18031 }
18032});
18033
18034}
18035
18036if(!dojo._hasResource["dijit.TitlePane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
18037dojo._hasResource["dijit.TitlePane"] = true;
18038dojo.provide("dijit.TitlePane");
18039
18040
18041
18042
18043
18044
18045
18046dojo.declare(
18047 "dijit.TitlePane",
18048 [dijit.layout.ContentPane, dijit._Templated, dijit._CssStateMixin],
18049{
18050 // summary:
18051 // A pane with a title on top, that can be expanded or collapsed.
18052 //
18053 // description:
18054 // An accessible container with a title Heading, and a content
18055 // section that slides open and closed. TitlePane is an extension to
18056 // `dijit.layout.ContentPane`, providing all the useful content-control aspects from it.
18057 //
18058 // example:
18059 // | // load a TitlePane from remote file:
18060 // | var foo = new dijit.TitlePane({ href: "foobar.html", title:"Title" });
18061 // | foo.startup();
18062 //
18063 // example:
18064 // | <!-- markup href example: -->
18065 // | <div dojoType="dijit.TitlePane" href="foobar.html" title="Title"></div>
18066 //
18067 // example:
18068 // | <!-- markup with inline data -->
18069 // | <div dojoType="dijit.TitlePane" title="Title">
18070 // | <p>I am content</p>
18071 // | </div>
18072
18073 // title: String
18074 // Title of the pane
18075 title: "",
18076
18077 // open: Boolean
18078 // Whether pane is opened or closed.
18079 open: true,
18080
18081 // toggleable: Boolean
18082 // Whether pane can be opened or closed by clicking the title bar.
18083 toggleable: true,
18084
18085 // tabIndex: String
18086 // Tabindex setting for the title (so users can tab to the title then
18087 // use space/enter to open/close the title pane)
18088 tabIndex: "0",
18089
18090 // duration: Integer
18091 // Time in milliseconds to fade in/fade out
18092 duration: dijit.defaultDuration,
18093
18094 // baseClass: [protected] String
18095 // The root className to be placed on this widget's domNode.
18096 baseClass: "dijitTitlePane",
18097
18098 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"),
18099
18100 attributeMap: dojo.delegate(dijit.layout.ContentPane.prototype.attributeMap, {
18101 title: { node: "titleNode", type: "innerHTML" },
18102 tooltip: {node: "focusNode", type: "attribute", attribute: "title"}, // focusNode spans the entire width, titleNode doesn't
18103 id:""
18104 }),
18105
18106 postCreate: function(){
18107 if(!this.open){
18108 this.hideNode.style.display = this.wipeNode.style.display = "none";
18109 }
18110
18111 // Hover and focus effect on title bar, except for non-toggleable TitlePanes
18112 // This should really be controlled from _setToggleableAttr() but _CssStateMixin
18113 // doesn't provide a way to disconnect a previous _trackMouseState() call
18114 if(this.toggleable){
18115 this._trackMouseState(this.titleBarNode, "dijitTitlePaneTitle");
18116 }
18117 this._setCss();
18118 dojo.setSelectable(this.titleNode, false);
18119
18120 // setup open/close animations
18121 var hideNode = this.hideNode, wipeNode = this.wipeNode;
18122 this._wipeIn = dojo.fx.wipeIn({
18123 node: this.wipeNode,
18124 duration: this.duration,
18125 beforeBegin: function(){
18126 hideNode.style.display="";
18127 }
18128 });
18129 this._wipeOut = dojo.fx.wipeOut({
18130 node: this.wipeNode,
18131 duration: this.duration,
18132 onEnd: function(){
18133 hideNode.style.display="none";
18134 }
18135 });
18136 this.inherited(arguments);
18137 },
18138
18139 _setOpenAttr: function(/* Boolean */ open){
18140 // summary:
18141 // Hook to make attr("open", boolean) control the open/closed state of the pane.
18142 // open: Boolean
18143 // True if you want to open the pane, false if you want to close it.
18144 if(this.open !== open){ this.toggle(); }
18145 dijit.setWaiState(this.containerNode,"hidden", this.open ? "false" : "true");
18146 dijit.setWaiState(this.focusNode, "pressed", this.open ? "true" : "false");
18147 },
18148
18149 _setToggleableAttr: function(/* Boolean */ canToggle){
18150 // summary:
18151 // Hook to make attr("canToggle", boolean) work.
18152 // canToggle: Boolean
18153 // True to allow user to open/close pane by clicking title bar.
18154 this.toggleable = canToggle;
18155 dijit.setWaiRole(this.focusNode, canToggle ? "button" : "heading");
18156 if(canToggle){
18157 // TODO: if canToggle is switched from true false shouldn't we remove this setting?
18158 dijit.setWaiState(this.focusNode, "controls", this.id+"_pane");
18159 dojo.attr(this.focusNode, "tabIndex", this.tabIndex);
18160 }
18161 else{
18162 dojo.removeAttr(this.focusNode, "tabIndex");
18163 }
18164 this._setCss();
18165 },
18166
18167 _setContentAttr: function(content){
18168 // summary:
18169 // Hook to make attr("content", ...) work.
18170 // Typically called when an href is loaded. Our job is to make the animation smooth.
18171
18172 if(!this.open || !this._wipeOut || this._wipeOut.status() == "playing"){
18173 // we are currently *closing* the pane (or the pane is closed), so just let that continue
18174 this.inherited(arguments);
18175 }else{
18176 if(this._wipeIn && this._wipeIn.status() == "playing"){
18177 this._wipeIn.stop();
18178 }
18179
18180 // freeze container at current height so that adding new content doesn't make it jump
18181 dojo.marginBox(this.wipeNode, { h: dojo.marginBox(this.wipeNode).h });
18182
18183 // add the new content (erasing the old content, if any)
18184 this.inherited(arguments);
18185
18186 // call _wipeIn.play() to animate from current height to new height
18187 if(this._wipeIn){
18188 this._wipeIn.play();
18189 }else{
18190 this.hideNode.style.display = "";
18191 }
18192 }
18193 },
18194
18195 toggle: function(){
18196 // summary:
18197 // Switches between opened and closed state
18198 // tags:
18199 // private
18200
18201 dojo.forEach([this._wipeIn, this._wipeOut], function(animation){
18202 if(animation && animation.status() == "playing"){
18203 animation.stop();
18204 }
18205 });
18206
18207 var anim = this[this.open ? "_wipeOut" : "_wipeIn"]
18208 if(anim){
18209 anim.play();
18210 }else{
18211 this.hideNode.style.display = this.open ? "" : "none";
18212 }
18213 this.open =! this.open;
18214
18215 // load content (if this is the first time we are opening the TitlePane
18216 // and content is specified as an href, or href was set when hidden)
18217 if(this.open){
18218 this._onShow();
18219 }else{
18220 this.onHide();
18221 }
18222
18223 this._setCss();
18224 },
18225
18226 _setCss: function(){
18227 // summary:
18228 // Set the open/close css state for the TitlePane
18229 // tags:
18230 // private
18231
18232 var node = this.titleBarNode || this.focusNode;
18233
18234 if(this._titleBarClass){
18235 dojo.removeClass(node, this._titleBarClass);
18236 }
18237 this._titleBarClass = "dijit" + (this.toggleable ? "" : "Fixed") + (this.open ? "Open" : "Closed");
18238 dojo.addClass(node, this._titleBarClass);
18239 this.arrowNodeInner.innerHTML = this.open ? "-" : "+";
18240 },
18241
18242 _onTitleKey: function(/*Event*/ e){
18243 // summary:
18244 // Handler for when user hits a key
18245 // tags:
18246 // private
18247
18248 if(e.charOrCode == dojo.keys.ENTER || e.charOrCode == ' '){
18249 if(this.toggleable){
18250 this.toggle();
18251 }
18252 dojo.stopEvent(e);
18253 }else if(e.charOrCode == dojo.keys.DOWN_ARROW && this.open){
18254 this.containerNode.focus();
18255 e.preventDefault();
18256 }
18257 },
18258
18259 _onTitleClick: function(){
18260 // summary:
18261 // Handler when user clicks the title bar
18262 // tags:
18263 // private
18264 if(this.toggleable){
18265 this.toggle();
18266 }
18267 },
18268
18269 setTitle: function(/*String*/ title){
18270 // summary:
18271 // Deprecated. Use set('title', ...) instead.
18272 // tags:
18273 // deprecated
18274 dojo.deprecated("dijit.TitlePane.setTitle() is deprecated. Use set('title', ...) instead.", "", "2.0");
18275 this.set("title", title);
18276 }
18277});
18278
18279}
18280
18281if(!dojo._hasResource["dojo.DeferredList"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
18282dojo._hasResource["dojo.DeferredList"] = true;
18283dojo.provide("dojo.DeferredList");
18284dojo.DeferredList = function(/*Array*/ list, /*Boolean?*/ fireOnOneCallback, /*Boolean?*/ fireOnOneErrback, /*Boolean?*/ consumeErrors, /*Function?*/ canceller){
18285 // summary:
18286 // Provides event handling for a group of Deferred objects.
18287 // description:
18288 // DeferredList takes an array of existing deferreds and returns a new deferred of its own
18289 // this new deferred will typically have its callback fired when all of the deferreds in
18290 // the given list have fired their own deferreds. The parameters `fireOnOneCallback` and
18291 // fireOnOneErrback, will fire before all the deferreds as appropriate
18292 //
18293 // list:
18294 // The list of deferreds to be synchronizied with this DeferredList
18295 // fireOnOneCallback:
18296 // Will cause the DeferredLists callback to be fired as soon as any
18297 // of the deferreds in its list have been fired instead of waiting until
18298 // the entire list has finished
18299 // fireonOneErrback:
18300 // Will cause the errback to fire upon any of the deferreds errback
18301 // canceller:
18302 // A deferred canceller function, see dojo.Deferred
18303 var resultList = [];
18304 dojo.Deferred.call(this);
18305 var self = this;
18306 if(list.length === 0 && !fireOnOneCallback){
18307 this.resolve([0, []]);
18308 }
18309 var finished = 0;
18310 dojo.forEach(list, function(item, i){
18311 item.then(function(result){
18312 if(fireOnOneCallback){
18313 self.resolve([i, result]);
18314 }else{
18315 addResult(true, result);
18316 }
18317 },function(error){
18318 if(fireOnOneErrback){
18319 self.reject(error);
18320 }else{
18321 addResult(false, error);
18322 }
18323 if(consumeErrors){
18324 return null;
18325 }
18326 throw error;
18327 });
18328 function addResult(succeeded, result){
18329 resultList[i] = [succeeded, result];
18330 finished++;
18331 if(finished === list.length){
18332 self.resolve(resultList);
18333 }
18334
18335 }
18336 });
18337};
18338dojo.DeferredList.prototype = new dojo.Deferred();
18339
18340dojo.DeferredList.prototype.gatherResults= function(deferredList){
18341 // summary:
18342 // Gathers the results of the deferreds for packaging
18343 // as the parameters to the Deferred Lists' callback
18344
18345 var d = new dojo.DeferredList(deferredList, false, true, false);
18346 d.addCallback(function(results){
18347 var ret = [];
18348 dojo.forEach(results, function(result){
18349 ret.push(result[1]);
18350 });
18351 return ret;
18352 });
18353 return d;
18354};
18355
18356}
18357
18358if(!dojo._hasResource["dojo.cookie"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
18359dojo._hasResource["dojo.cookie"] = true;
18360dojo.provide("dojo.cookie");
18361
18362
18363
18364/*=====
18365dojo.__cookieProps = function(){
18366 // expires: Date|String|Number?
18367 // If a number, the number of days from today at which the cookie
18368 // will expire. If a date, the date past which the cookie will expire.
18369 // If expires is in the past, the cookie will be deleted.
18370 // If expires is omitted or is 0, the cookie will expire when the browser closes. << FIXME: 0 seems to disappear right away? FF3.
18371 // path: String?
18372 // The path to use for the cookie.
18373 // domain: String?
18374 // The domain to use for the cookie.
18375 // secure: Boolean?
18376 // Whether to only send the cookie on secure connections
18377 this.expires = expires;
18378 this.path = path;
18379 this.domain = domain;
18380 this.secure = secure;
18381}
18382=====*/
18383
18384
18385dojo.cookie = function(/*String*/name, /*String?*/value, /*dojo.__cookieProps?*/props){
18386 // summary:
18387 // Get or set a cookie.
18388 // description:
18389 // If one argument is passed, returns the value of the cookie
18390 // For two or more arguments, acts as a setter.
18391 // name:
18392 // Name of the cookie
18393 // value:
18394 // Value for the cookie
18395 // props:
18396 // Properties for the cookie
18397 // example:
18398 // set a cookie with the JSON-serialized contents of an object which
18399 // will expire 5 days from now:
18400 // | dojo.cookie("configObj", dojo.toJson(config), { expires: 5 });
18401 //
18402 // example:
18403 // de-serialize a cookie back into a JavaScript object:
18404 // | var config = dojo.fromJson(dojo.cookie("configObj"));
18405 //
18406 // example:
18407 // delete a cookie:
18408 // | dojo.cookie("configObj", null, {expires: -1});
18409 var c = document.cookie;
18410 if(arguments.length == 1){
18411 var matches = c.match(new RegExp("(?:^|; )" + dojo.regexp.escapeString(name) + "=([^;]*)"));
18412 return matches ? decodeURIComponent(matches[1]) : undefined; // String or undefined
18413 }else{
18414 props = props || {};
18415// FIXME: expires=0 seems to disappear right away, not on close? (FF3) Change docs?
18416 var exp = props.expires;
18417 if(typeof exp == "number"){
18418 var d = new Date();
18419 d.setTime(d.getTime() + exp*24*60*60*1000);
18420 exp = props.expires = d;
18421 }
18422 if(exp && exp.toUTCString){ props.expires = exp.toUTCString(); }
18423
18424 value = encodeURIComponent(value);
18425 var updatedCookie = name + "=" + value, propName;
18426 for(propName in props){
18427 updatedCookie += "; " + propName;
18428 var propValue = props[propName];
18429 if(propValue !== true){ updatedCookie += "=" + propValue; }
18430 }
18431 document.cookie = updatedCookie;
18432 }
18433};
18434
18435dojo.cookie.isSupported = function(){
18436 // summary:
18437 // Use to determine if the current browser supports cookies or not.
18438 //
18439 // Returns true if user allows cookies.
18440 // Returns false if user doesn't allow cookies.
18441
18442 if(!("cookieEnabled" in navigator)){
18443 this("__djCookieTest__", "CookiesAllowed");
18444 navigator.cookieEnabled = this("__djCookieTest__") == "CookiesAllowed";
18445 if(navigator.cookieEnabled){
18446 this("__djCookieTest__", "", {expires: -1});
18447 }
18448 }
18449 return navigator.cookieEnabled;
18450};
18451
18452}
18453
18454if(!dojo._hasResource["dijit.tree.TreeStoreModel"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
18455dojo._hasResource["dijit.tree.TreeStoreModel"] = true;
18456dojo.provide("dijit.tree.TreeStoreModel");
18457
18458dojo.declare(
18459 "dijit.tree.TreeStoreModel",
18460 null,
18461 {
18462 // summary:
18463 // Implements dijit.Tree.model connecting to a store with a single
18464 // root item. Any methods passed into the constructor will override
18465 // the ones defined here.
18466
18467 // store: dojo.data.Store
18468 // Underlying store
18469 store: null,
18470
18471 // childrenAttrs: String[]
18472 // One or more attribute names (attributes in the dojo.data item) that specify that item's children
18473 childrenAttrs: ["children"],
18474
18475 // newItemIdAttr: String
18476 // Name of attribute in the Object passed to newItem() that specifies the id.
18477 //
18478 // If newItemIdAttr is set then it's used when newItem() is called to see if an
18479 // item with the same id already exists, and if so just links to the old item
18480 // (so that the old item ends up with two parents).
18481 //
18482 // Setting this to null or "" will make every drop create a new item.
18483 newItemIdAttr: "id",
18484
18485 // labelAttr: String
18486 // If specified, get label for tree node from this attribute, rather
18487 // than by calling store.getLabel()
18488 labelAttr: "",
18489
18490 // root: [readonly] dojo.data.Item
18491 // Pointer to the root item (read only, not a parameter)
18492 root: null,
18493
18494 // query: anything
18495 // Specifies datastore query to return the root item for the tree.
18496 // Must only return a single item. Alternately can just pass in pointer
18497 // to root item.
18498 // example:
18499 // | {id:'ROOT'}
18500 query: null,
18501
18502 // deferItemLoadingUntilExpand: Boolean
18503 // Setting this to true will cause the TreeStoreModel to defer calling loadItem on nodes
18504 // until they are expanded. This allows for lazying loading where only one
18505 // loadItem (and generally one network call, consequently) per expansion
18506 // (rather than one for each child).
18507 // This relies on partial loading of the children items; each children item of a
18508 // fully loaded item should contain the label and info about having children.
18509 deferItemLoadingUntilExpand: false,
18510
18511 constructor: function(/* Object */ args){
18512 // summary:
18513 // Passed the arguments listed above (store, etc)
18514 // tags:
18515 // private
18516
18517 dojo.mixin(this, args);
18518
18519 this.connects = [];
18520
18521 var store = this.store;
18522 if(!store.getFeatures()['dojo.data.api.Identity']){
18523 throw new Error("dijit.Tree: store must support dojo.data.Identity");
18524 }
18525
18526 // if the store supports Notification, subscribe to the notification events
18527 if(store.getFeatures()['dojo.data.api.Notification']){
18528 this.connects = this.connects.concat([
18529 dojo.connect(store, "onNew", this, "onNewItem"),
18530 dojo.connect(store, "onDelete", this, "onDeleteItem"),
18531 dojo.connect(store, "onSet", this, "onSetItem")
18532 ]);
18533 }
18534 },
18535
18536 destroy: function(){
18537 dojo.forEach(this.connects, dojo.disconnect);
18538 // TODO: should cancel any in-progress processing of getRoot(), getChildren()
18539 },
18540
18541 // =======================================================================
18542 // Methods for traversing hierarchy
18543
18544 getRoot: function(onItem, onError){
18545 // summary:
18546 // Calls onItem with the root item for the tree, possibly a fabricated item.
18547 // Calls onError on error.
18548 if(this.root){
18549 onItem(this.root);
18550 }else{
18551 this.store.fetch({
18552 query: this.query,
18553 onComplete: dojo.hitch(this, function(items){
18554 if(items.length != 1){
18555 throw new Error(this.declaredClass + ": query " + dojo.toJson(this.query) + " returned " + items.length +
18556 " items, but must return exactly one item");
18557 }
18558 this.root = items[0];
18559 onItem(this.root);
18560 }),
18561 onError: onError
18562 });
18563 }
18564 },
18565
18566 mayHaveChildren: function(/*dojo.data.Item*/ item){
18567 // summary:
18568 // Tells if an item has or may have children. Implementing logic here
18569 // avoids showing +/- expando icon for nodes that we know don't have children.
18570 // (For efficiency reasons we may not want to check if an element actually
18571 // has children until user clicks the expando node)
18572 return dojo.some(this.childrenAttrs, function(attr){
18573 return this.store.hasAttribute(item, attr);
18574 }, this);
18575 },
18576
18577 getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete, /*function*/ onError){
18578 // summary:
18579 // Calls onComplete() with array of child items of given parent item, all loaded.
18580
18581 var store = this.store;
18582 if(!store.isItemLoaded(parentItem)){
18583 // The parent is not loaded yet, we must be in deferItemLoadingUntilExpand
18584 // mode, so we will load it and just return the children (without loading each
18585 // child item)
18586 var getChildren = dojo.hitch(this, arguments.callee);
18587 store.loadItem({
18588 item: parentItem,
18589 onItem: function(parentItem){
18590 getChildren(parentItem, onComplete, onError);
18591 },
18592 onError: onError
18593 });
18594 return;
18595 }
18596 // get children of specified item
18597 var childItems = [];
18598 for(var i=0; i<this.childrenAttrs.length; i++){
18599 var vals = store.getValues(parentItem, this.childrenAttrs[i]);
18600 childItems = childItems.concat(vals);
18601 }
18602
18603 // count how many items need to be loaded
18604 var _waitCount = 0;
18605 if(!this.deferItemLoadingUntilExpand){
18606 dojo.forEach(childItems, function(item){ if(!store.isItemLoaded(item)){ _waitCount++; } });
18607 }
18608
18609 if(_waitCount == 0){
18610 // all items are already loaded (or we aren't loading them). proceed...
18611 onComplete(childItems);
18612 }else{
18613 // still waiting for some or all of the items to load
18614 dojo.forEach(childItems, function(item, idx){
18615 if(!store.isItemLoaded(item)){
18616 store.loadItem({
18617 item: item,
18618 onItem: function(item){
18619 childItems[idx] = item;
18620 if(--_waitCount == 0){
18621 // all nodes have been loaded, send them to the tree
18622 onComplete(childItems);
18623 }
18624 },
18625 onError: onError
18626 });
18627 }
18628 });
18629 }
18630 },
18631
18632 // =======================================================================
18633 // Inspecting items
18634
18635 isItem: function(/* anything */ something){
18636 return this.store.isItem(something); // Boolean
18637 },
18638
18639 fetchItemByIdentity: function(/* object */ keywordArgs){
18640 this.store.fetchItemByIdentity(keywordArgs);
18641 },
18642
18643 getIdentity: function(/* item */ item){
18644 return this.store.getIdentity(item); // Object
18645 },
18646
18647 getLabel: function(/*dojo.data.Item*/ item){
18648 // summary:
18649 // Get the label for an item
18650 if(this.labelAttr){
18651 return this.store.getValue(item,this.labelAttr); // String
18652 }else{
18653 return this.store.getLabel(item); // String
18654 }
18655 },
18656
18657 // =======================================================================
18658 // Write interface
18659
18660 newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
18661 // summary:
18662 // Creates a new item. See `dojo.data.api.Write` for details on args.
18663 // Used in drag & drop when item from external source dropped onto tree.
18664 // description:
18665 // Developers will need to override this method if new items get added
18666 // to parents with multiple children attributes, in order to define which
18667 // children attribute points to the new item.
18668
18669 var pInfo = {parent: parent, attribute: this.childrenAttrs[0], insertIndex: insertIndex};
18670
18671 if(this.newItemIdAttr && args[this.newItemIdAttr]){
18672 // Maybe there's already a corresponding item in the store; if so, reuse it.
18673 this.fetchItemByIdentity({identity: args[this.newItemIdAttr], scope: this, onItem: function(item){
18674 if(item){
18675 // There's already a matching item in store, use it
18676 this.pasteItem(item, null, parent, true, insertIndex);
18677 }else{
18678 // Create new item in the tree, based on the drag source.
18679 this.store.newItem(args, pInfo);
18680 }
18681 }});
18682 }else{
18683 // [as far as we know] there is no id so we must assume this is a new item
18684 this.store.newItem(args, pInfo);
18685 }
18686 },
18687
18688 pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
18689 // summary:
18690 // Move or copy an item from one parent item to another.
18691 // Used in drag & drop
18692 var store = this.store,
18693 parentAttr = this.childrenAttrs[0]; // name of "children" attr in parent item
18694
18695 // remove child from source item, and record the attribute that child occurred in
18696 if(oldParentItem){
18697 dojo.forEach(this.childrenAttrs, function(attr){
18698 if(store.containsValue(oldParentItem, attr, childItem)){
18699 if(!bCopy){
18700 var values = dojo.filter(store.getValues(oldParentItem, attr), function(x){
18701 return x != childItem;
18702 });
18703 store.setValues(oldParentItem, attr, values);
18704 }
18705 parentAttr = attr;
18706 }
18707 });
18708 }
18709
18710 // modify target item's children attribute to include this item
18711 if(newParentItem){
18712 if(typeof insertIndex == "number"){
18713 // call slice() to avoid modifying the original array, confusing the data store
18714 var childItems = store.getValues(newParentItem, parentAttr).slice();
18715 childItems.splice(insertIndex, 0, childItem);
18716 store.setValues(newParentItem, parentAttr, childItems);
18717 }else{
18718 store.setValues(newParentItem, parentAttr,
18719 store.getValues(newParentItem, parentAttr).concat(childItem));
18720 }
18721 }
18722 },
18723
18724 // =======================================================================
18725 // Callbacks
18726
18727 onChange: function(/*dojo.data.Item*/ item){
18728 // summary:
18729 // Callback whenever an item has changed, so that Tree
18730 // can update the label, icon, etc. Note that changes
18731 // to an item's children or parent(s) will trigger an
18732 // onChildrenChange() so you can ignore those changes here.
18733 // tags:
18734 // callback
18735 },
18736
18737 onChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
18738 // summary:
18739 // Callback to do notifications about new, updated, or deleted items.
18740 // tags:
18741 // callback
18742 },
18743
18744 onDelete: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
18745 // summary:
18746 // Callback when an item has been deleted.
18747 // description:
18748 // Note that there will also be an onChildrenChange() callback for the parent
18749 // of this item.
18750 // tags:
18751 // callback
18752 },
18753
18754 // =======================================================================
18755 // Events from data store
18756
18757 onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
18758 // summary:
18759 // Handler for when new items appear in the store, either from a drop operation
18760 // or some other way. Updates the tree view (if necessary).
18761 // description:
18762 // If the new item is a child of an existing item,
18763 // calls onChildrenChange() with the new list of children
18764 // for that existing item.
18765 //
18766 // tags:
18767 // extension
18768
18769 // We only care about the new item if it has a parent that corresponds to a TreeNode
18770 // we are currently displaying
18771 if(!parentInfo){
18772 return;
18773 }
18774
18775 // Call onChildrenChange() on parent (ie, existing) item with new list of children
18776 // In the common case, the new list of children is simply parentInfo.newValue or
18777 // [ parentInfo.newValue ], although if items in the store has multiple
18778 // child attributes (see `childrenAttr`), then it's a superset of parentInfo.newValue,
18779 // so call getChildren() to be sure to get right answer.
18780 this.getChildren(parentInfo.item, dojo.hitch(this, function(children){
18781 this.onChildrenChange(parentInfo.item, children);
18782 }));
18783 },
18784
18785 onDeleteItem: function(/*Object*/ item){
18786 // summary:
18787 // Handler for delete notifications from underlying store
18788 this.onDelete(item);
18789 },
18790
18791 onSetItem: function(/* item */ item,
18792 /* attribute-name-string */ attribute,
18793 /* object | array */ oldValue,
18794 /* object | array */ newValue){
18795 // summary:
18796 // Updates the tree view according to changes in the data store.
18797 // description:
18798 // Handles updates to an item's children by calling onChildrenChange(), and
18799 // other updates to an item by calling onChange().
18800 //
18801 // See `onNewItem` for more details on handling updates to an item's children.
18802 // tags:
18803 // extension
18804
18805 if(dojo.indexOf(this.childrenAttrs, attribute) != -1){
18806 // item's children list changed
18807 this.getChildren(item, dojo.hitch(this, function(children){
18808 // See comments in onNewItem() about calling getChildren()
18809 this.onChildrenChange(item, children);
18810 }));
18811 }else{
18812 // item's label/icon/etc. changed.
18813 this.onChange(item);
18814 }
18815 }
18816 });
18817
18818
18819
18820}
18821
18822if(!dojo._hasResource["dijit.tree.ForestStoreModel"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
18823dojo._hasResource["dijit.tree.ForestStoreModel"] = true;
18824dojo.provide("dijit.tree.ForestStoreModel");
18825
18826
18827
18828dojo.declare("dijit.tree.ForestStoreModel", dijit.tree.TreeStoreModel, {
18829 // summary:
18830 // Interface between Tree and a dojo.store that doesn't have a root item,
18831 // i.e. has multiple "top level" items.
18832 //
18833 // description
18834 // Use this class to wrap a dojo.store, making all the items matching the specified query
18835 // appear as children of a fabricated "root item". If no query is specified then all the
18836 // items returned by fetch() on the underlying store become children of the root item.
18837 // It allows dijit.Tree to assume a single root item, even if the store doesn't have one.
18838
18839 // Parameters to constructor
18840
18841 // rootId: String
18842 // ID of fabricated root item
18843 rootId: "$root$",
18844
18845 // rootLabel: String
18846 // Label of fabricated root item
18847 rootLabel: "ROOT",
18848
18849 // query: String
18850 // Specifies the set of children of the root item.
18851 // example:
18852 // | {type:'continent'}
18853 query: null,
18854
18855 // End of parameters to constructor
18856
18857 constructor: function(params){
18858 // summary:
18859 // Sets up variables, etc.
18860 // tags:
18861 // private
18862
18863 // Make dummy root item
18864 this.root = {
18865 store: this,
18866 root: true,
18867 id: params.rootId,
18868 label: params.rootLabel,
18869 children: params.rootChildren // optional param
18870 };
18871 },
18872
18873 // =======================================================================
18874 // Methods for traversing hierarchy
18875
18876 mayHaveChildren: function(/*dojo.data.Item*/ item){
18877 // summary:
18878 // Tells if an item has or may have children. Implementing logic here
18879 // avoids showing +/- expando icon for nodes that we know don't have children.
18880 // (For efficiency reasons we may not want to check if an element actually
18881 // has children until user clicks the expando node)
18882 // tags:
18883 // extension
18884 return item === this.root || this.inherited(arguments);
18885 },
18886
18887 getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ callback, /*function*/ onError){
18888 // summary:
18889 // Calls onComplete() with array of child items of given parent item, all loaded.
18890 if(parentItem === this.root){
18891 if(this.root.children){
18892 // already loaded, just return
18893 callback(this.root.children);
18894 }else{
18895 this.store.fetch({
18896 query: this.query,
18897 onComplete: dojo.hitch(this, function(items){
18898 this.root.children = items;
18899 callback(items);
18900 }),
18901 onError: onError
18902 });
18903 }
18904 }else{
18905 this.inherited(arguments);
18906 }
18907 },
18908
18909 // =======================================================================
18910 // Inspecting items
18911
18912 isItem: function(/* anything */ something){
18913 return (something === this.root) ? true : this.inherited(arguments);
18914 },
18915
18916 fetchItemByIdentity: function(/* object */ keywordArgs){
18917 if(keywordArgs.identity == this.root.id){
18918 var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
18919 if(keywordArgs.onItem){
18920 keywordArgs.onItem.call(scope, this.root);
18921 }
18922 }else{
18923 this.inherited(arguments);
18924 }
18925 },
18926
18927 getIdentity: function(/* item */ item){
18928 return (item === this.root) ? this.root.id : this.inherited(arguments);
18929 },
18930
18931 getLabel: function(/* item */ item){
18932 return (item === this.root) ? this.root.label : this.inherited(arguments);
18933 },
18934
18935 // =======================================================================
18936 // Write interface
18937
18938 newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
18939 // summary:
18940 // Creates a new item. See dojo.data.api.Write for details on args.
18941 // Used in drag & drop when item from external source dropped onto tree.
18942 if(parent === this.root){
18943 this.onNewRootItem(args);
18944 return this.store.newItem(args);
18945 }else{
18946 return this.inherited(arguments);
18947 }
18948 },
18949
18950 onNewRootItem: function(args){
18951 // summary:
18952 // User can override this method to modify a new element that's being
18953 // added to the root of the tree, for example to add a flag like root=true
18954 },
18955
18956 pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
18957 // summary:
18958 // Move or copy an item from one parent item to another.
18959 // Used in drag & drop
18960 if(oldParentItem === this.root){
18961 if(!bCopy){
18962 // It's onLeaveRoot()'s responsibility to modify the item so it no longer matches
18963 // this.query... thus triggering an onChildrenChange() event to notify the Tree
18964 // that this element is no longer a child of the root node
18965 this.onLeaveRoot(childItem);
18966 }
18967 }
18968 dijit.tree.TreeStoreModel.prototype.pasteItem.call(this, childItem,
18969 oldParentItem === this.root ? null : oldParentItem,
18970 newParentItem === this.root ? null : newParentItem,
18971 bCopy,
18972 insertIndex
18973 );
18974 if(newParentItem === this.root){
18975 // It's onAddToRoot()'s responsibility to modify the item so it matches
18976 // this.query... thus triggering an onChildrenChange() event to notify the Tree
18977 // that this element is now a child of the root node
18978 this.onAddToRoot(childItem);
18979 }
18980 },
18981
18982 // =======================================================================
18983 // Handling for top level children
18984
18985 onAddToRoot: function(/* item */ item){
18986 // summary:
18987 // Called when item added to root of tree; user must override this method
18988 // to modify the item so that it matches the query for top level items
18989 // example:
18990 // | store.setValue(item, "root", true);
18991 // tags:
18992 // extension
18993 console.log(this, ": item ", item, " added to root");
18994 },
18995
18996 onLeaveRoot: function(/* item */ item){
18997 // summary:
18998 // Called when item removed from root of tree; user must override this method
18999 // to modify the item so it doesn't match the query for top level items
19000 // example:
19001 // | store.unsetAttribute(item, "root");
19002 // tags:
19003 // extension
19004 console.log(this, ": item ", item, " removed from root");
19005 },
19006
19007 // =======================================================================
19008 // Events from data store
19009
19010 _requeryTop: function(){
19011 // reruns the query for the children of the root node,
19012 // sending out an onSet notification if those children have changed
19013 var oldChildren = this.root.children || [];
19014 this.store.fetch({
19015 query: this.query,
19016 onComplete: dojo.hitch(this, function(newChildren){
19017 this.root.children = newChildren;
19018
19019 // If the list of children or the order of children has changed...
19020 if(oldChildren.length != newChildren.length ||
19021 dojo.some(oldChildren, function(item, idx){ return newChildren[idx] != item;})){
19022 this.onChildrenChange(this.root, newChildren);
19023 }
19024 })
19025 });
19026 },
19027
19028 onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
19029 // summary:
19030 // Handler for when new items appear in the store. Developers should override this
19031 // method to be more efficient based on their app/data.
19032 // description:
19033 // Note that the default implementation requeries the top level items every time
19034 // a new item is created, since any new item could be a top level item (even in
19035 // addition to being a child of another item, since items can have multiple parents).
19036 //
19037 // Developers can override this function to do something more efficient if they can
19038 // detect which items are possible top level items (based on the item and the
19039 // parentInfo parameters). Often all top level items have parentInfo==null, but
19040 // that will depend on which store you use and what your data is like.
19041 // tags:
19042 // extension
19043 this._requeryTop();
19044
19045 this.inherited(arguments);
19046 },
19047
19048 onDeleteItem: function(/*Object*/ item){
19049 // summary:
19050 // Handler for delete notifications from underlying store
19051
19052 // check if this was a child of root, and if so send notification that root's children
19053 // have changed
19054 if(dojo.indexOf(this.root.children, item) != -1){
19055 this._requeryTop();
19056 }
19057
19058 this.inherited(arguments);
19059 }
19060});
19061
19062
19063
19064}
19065
19066if(!dojo._hasResource["dijit.Tree"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
19067dojo._hasResource["dijit.Tree"] = true;
19068dojo.provide("dijit.Tree");
19069
19070
19071
19072
19073
19074
19075
19076
19077
19078
19079
19080dojo.declare(
19081 "dijit._TreeNode",
19082 [dijit._Widget, dijit._Templated, dijit._Container, dijit._Contained, dijit._CssStateMixin],
19083{
19084 // summary:
19085 // Single node within a tree. This class is used internally
19086 // by Tree and should not be accessed directly.
19087 // tags:
19088 // private
19089
19090 // item: dojo.data.Item
19091 // the dojo.data entry this tree represents
19092 item: null,
19093
19094 // isTreeNode: [protected] Boolean
19095 // Indicates that this is a TreeNode. Used by `dijit.Tree` only,
19096 // should not be accessed directly.
19097 isTreeNode: true,
19098
19099 // label: String
19100 // Text of this tree node
19101 label: "",
19102
19103 // isExpandable: [private] Boolean
19104 // This node has children, so show the expando node (+ sign)
19105 isExpandable: null,
19106
19107 // isExpanded: [readonly] Boolean
19108 // This node is currently expanded (ie, opened)
19109 isExpanded: false,
19110
19111 // state: [private] String
19112 // Dynamic loading-related stuff.
19113 // When an empty folder node appears, it is "UNCHECKED" first,
19114 // then after dojo.data query it becomes "LOADING" and, finally "LOADED"
19115 state: "UNCHECKED",
19116
19117 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"),
19118
19119 baseClass: "dijitTreeNode",
19120
19121 // For hover effect for tree node, and focus effect for label
19122 cssStateNodes: {
19123 rowNode: "dijitTreeRow",
19124 labelNode: "dijitTreeLabel"
19125 },
19126
19127 attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
19128 label: {node: "labelNode", type: "innerText"},
19129 tooltip: {node: "rowNode", type: "attribute", attribute: "title"}
19130 }),
19131
19132 postCreate: function(){
19133 this.inherited(arguments);
19134
19135 // set expand icon for leaf
19136 this._setExpando();
19137
19138 // set icon and label class based on item
19139 this._updateItemClasses(this.item);
19140
19141 if(this.isExpandable){
19142 dijit.setWaiState(this.labelNode, "expanded", this.isExpanded);
19143 }
19144 },
19145
19146 _setIndentAttr: function(indent){
19147 // summary:
19148 // Tell this node how many levels it should be indented
19149 // description:
19150 // 0 for top level nodes, 1 for their children, 2 for their
19151 // grandchildren, etc.
19152 this.indent = indent;
19153
19154 // Math.max() is to prevent negative padding on hidden root node (when indent == -1)
19155 var pixels = (Math.max(indent, 0) * this.tree._nodePixelIndent) + "px";
19156
19157 dojo.style(this.domNode, "backgroundPosition", pixels + " 0px");
19158 dojo.style(this.rowNode, this.isLeftToRight() ? "paddingLeft" : "paddingRight", pixels);
19159
19160 dojo.forEach(this.getChildren(), function(child){
19161 child.set("indent", indent+1);
19162 });
19163 },
19164
19165 markProcessing: function(){
19166 // summary:
19167 // Visually denote that tree is loading data, etc.
19168 // tags:
19169 // private
19170 this.state = "LOADING";
19171 this._setExpando(true);
19172 },
19173
19174 unmarkProcessing: function(){
19175 // summary:
19176 // Clear markup from markProcessing() call
19177 // tags:
19178 // private
19179 this._setExpando(false);
19180 },
19181
19182 _updateItemClasses: function(item){
19183 // summary:
19184 // Set appropriate CSS classes for icon and label dom node
19185 // (used to allow for item updates to change respective CSS)
19186 // tags:
19187 // private
19188 var tree = this.tree, model = tree.model;
19189 if(tree._v10Compat && item === model.root){
19190 // For back-compat with 1.0, need to use null to specify root item (TODO: remove in 2.0)
19191 item = null;
19192 }
19193 this._applyClassAndStyle(item, "icon", "Icon");
19194 this._applyClassAndStyle(item, "label", "Label");
19195 this._applyClassAndStyle(item, "row", "Row");
19196 },
19197
19198 _applyClassAndStyle: function(item, lower, upper){
19199 // summary:
19200 // Set the appropriate CSS classes and styles for labels, icons and rows.
19201 //
19202 // item:
19203 // The data item.
19204 //
19205 // lower:
19206 // The lower case attribute to use, e.g. 'icon', 'label' or 'row'.
19207 //
19208 // upper:
19209 // The upper case attribute to use, e.g. 'Icon', 'Label' or 'Row'.
19210 //
19211 // tags:
19212 // private
19213
19214 var clsName = "_" + lower + "Class";
19215 var nodeName = lower + "Node";
19216
19217 if(this[clsName]){
19218 dojo.removeClass(this[nodeName], this[clsName]);
19219 }
19220 this[clsName] = this.tree["get" + upper + "Class"](item, this.isExpanded);
19221 if(this[clsName]){
19222 dojo.addClass(this[nodeName], this[clsName]);
19223 }
19224 dojo.style(this[nodeName], this.tree["get" + upper + "Style"](item, this.isExpanded) || {});
19225 },
19226
19227 _updateLayout: function(){
19228 // summary:
19229 // Set appropriate CSS classes for this.domNode
19230 // tags:
19231 // private
19232 var parent = this.getParent();
19233 if(!parent || parent.rowNode.style.display == "none"){
19234 /* if we are hiding the root node then make every first level child look like a root node */
19235 dojo.addClass(this.domNode, "dijitTreeIsRoot");
19236 }else{
19237 dojo.toggleClass(this.domNode, "dijitTreeIsLast", !this.getNextSibling());
19238 }
19239 },
19240
19241 _setExpando: function(/*Boolean*/ processing){
19242 // summary:
19243 // Set the right image for the expando node
19244 // tags:
19245 // private
19246
19247 var styles = ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened",
19248 "dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"],
19249 _a11yStates = ["*","-","+","*"],
19250 idx = processing ? 0 : (this.isExpandable ? (this.isExpanded ? 1 : 2) : 3);
19251
19252 // apply the appropriate class to the expando node
19253 dojo.removeClass(this.expandoNode, styles);
19254 dojo.addClass(this.expandoNode, styles[idx]);
19255
19256 // provide a non-image based indicator for images-off mode
19257 this.expandoNodeText.innerHTML = _a11yStates[idx];
19258
19259 },
19260
19261 expand: function(){
19262 // summary:
19263 // Show my children
19264 // returns:
19265 // Deferred that fires when expansion is complete
19266
19267 // If there's already an expand in progress or we are already expanded, just return
19268 if(this._expandDeferred){
19269 return this._expandDeferred; // dojo.Deferred
19270 }
19271
19272 // cancel in progress collapse operation
19273 this._wipeOut && this._wipeOut.stop();
19274
19275 // All the state information for when a node is expanded, maybe this should be
19276 // set when the animation completes instead
19277 this.isExpanded = true;
19278 dijit.setWaiState(this.labelNode, "expanded", "true");
19279 dijit.setWaiRole(this.containerNode, "group");
19280 dojo.addClass(this.contentNode,'dijitTreeContentExpanded');
19281 this._setExpando();
19282 this._updateItemClasses(this.item);
19283 if(this == this.tree.rootNode){
19284 dijit.setWaiState(this.tree.domNode, "expanded", "true");
19285 }
19286
19287 var def,
19288 wipeIn = dojo.fx.wipeIn({
19289 node: this.containerNode, duration: dijit.defaultDuration,
19290 onEnd: function(){
19291 def.callback(true);
19292 }
19293 });
19294
19295 // Deferred that fires when expand is complete
19296 def = (this._expandDeferred = new dojo.Deferred(function(){
19297 // Canceller
19298 wipeIn.stop();
19299 }));
19300
19301 wipeIn.play();
19302
19303 return def; // dojo.Deferred
19304 },
19305
19306 collapse: function(){
19307 // summary:
19308 // Collapse this node (if it's expanded)
19309
19310 if(!this.isExpanded){ return; }
19311
19312 // cancel in progress expand operation
19313 if(this._expandDeferred){
19314 this._expandDeferred.cancel();
19315 delete this._expandDeferred;
19316 }
19317
19318 this.isExpanded = false;
19319 dijit.setWaiState(this.labelNode, "expanded", "false");
19320 if(this == this.tree.rootNode){
19321 dijit.setWaiState(this.tree.domNode, "expanded", "false");
19322 }
19323 dojo.removeClass(this.contentNode,'dijitTreeContentExpanded');
19324 this._setExpando();
19325 this._updateItemClasses(this.item);
19326
19327 if(!this._wipeOut){
19328 this._wipeOut = dojo.fx.wipeOut({
19329 node: this.containerNode, duration: dijit.defaultDuration
19330 });
19331 }
19332 this._wipeOut.play();
19333 },
19334
19335 // indent: Integer
19336 // Levels from this node to the root node
19337 indent: 0,
19338
19339 setChildItems: function(/* Object[] */ items){
19340 // summary:
19341 // Sets the child items of this node, removing/adding nodes
19342 // from current children to match specified items[] array.
19343 // Also, if this.persist == true, expands any children that were previously
19344 // opened.
19345 // returns:
19346 // Deferred object that fires after all previously opened children
19347 // have been expanded again (or fires instantly if there are no such children).
19348
19349 var tree = this.tree,
19350 model = tree.model,
19351 defs = []; // list of deferreds that need to fire before I am complete
19352
19353
19354 // Orphan all my existing children.
19355 // If items contains some of the same items as before then we will reattach them.
19356 // Don't call this.removeChild() because that will collapse the tree etc.
19357 dojo.forEach(this.getChildren(), function(child){
19358 dijit._Container.prototype.removeChild.call(this, child);
19359 }, this);
19360
19361 this.state = "LOADED";
19362
19363 if(items && items.length > 0){
19364 this.isExpandable = true;
19365
19366 // Create _TreeNode widget for each specified tree node, unless one already
19367 // exists and isn't being used (presumably it's from a DnD move and was recently
19368 // released
19369 dojo.forEach(items, function(item){
19370 var id = model.getIdentity(item),
19371 existingNodes = tree._itemNodesMap[id],
19372 node;
19373 if(existingNodes){
19374 for(var i=0;i<existingNodes.length;i++){
19375 if(existingNodes[i] && !existingNodes[i].getParent()){
19376 node = existingNodes[i];
19377 node.set('indent', this.indent+1);
19378 break;
19379 }
19380 }
19381 }
19382 if(!node){
19383 node = this.tree._createTreeNode({
19384 item: item,
19385 tree: tree,
19386 isExpandable: model.mayHaveChildren(item),
19387 label: tree.getLabel(item),
19388 tooltip: tree.getTooltip(item),
19389 dir: tree.dir,
19390 lang: tree.lang,
19391 indent: this.indent + 1
19392 });
19393 if(existingNodes){
19394 existingNodes.push(node);
19395 }else{
19396 tree._itemNodesMap[id] = [node];
19397 }
19398 }
19399 this.addChild(node);
19400
19401 // If node was previously opened then open it again now (this may trigger
19402 // more data store accesses, recursively)
19403 if(this.tree.autoExpand || this.tree._state(item)){
19404 defs.push(tree._expandNode(node));
19405 }
19406 }, this);
19407
19408 // note that updateLayout() needs to be called on each child after
19409 // _all_ the children exist
19410 dojo.forEach(this.getChildren(), function(child, idx){
19411 child._updateLayout();
19412 });
19413 }else{
19414 this.isExpandable=false;
19415 }
19416
19417 if(this._setExpando){
19418 // change expando to/from dot or + icon, as appropriate
19419 this._setExpando(false);
19420 }
19421
19422 // Set leaf icon or folder icon, as appropriate
19423 this._updateItemClasses(this.item);
19424
19425 // On initial tree show, make the selected TreeNode as either the root node of the tree,
19426 // or the first child, if the root node is hidden
19427 if(this == tree.rootNode){
19428 var fc = this.tree.showRoot ? this : this.getChildren()[0];
19429 if(fc){
19430 fc.setFocusable(true);
19431 tree.lastFocused = fc;
19432 }else{
19433 // fallback: no nodes in tree so focus on Tree <div> itself
19434 tree.domNode.setAttribute("tabIndex", "0");
19435 }
19436 }
19437
19438 return new dojo.DeferredList(defs); // dojo.Deferred
19439 },
19440
19441 removeChild: function(/* treeNode */ node){
19442 this.inherited(arguments);
19443
19444 var children = this.getChildren();
19445 if(children.length == 0){
19446 this.isExpandable = false;
19447 this.collapse();
19448 }
19449
19450 dojo.forEach(children, function(child){
19451 child._updateLayout();
19452 });
19453 },
19454
19455 makeExpandable: function(){
19456 // summary:
19457 // if this node wasn't already showing the expando node,
19458 // turn it into one and call _setExpando()
19459
19460 // TODO: hmm this isn't called from anywhere, maybe should remove it for 2.0
19461
19462 this.isExpandable = true;
19463 this._setExpando(false);
19464 },
19465
19466 _onLabelFocus: function(evt){
19467 // summary:
19468 // Called when this row is focused (possibly programatically)
19469 // Note that we aren't using _onFocus() builtin to dijit
19470 // because it's called when focus is moved to a descendant TreeNode.
19471 // tags:
19472 // private
19473 this.tree._onNodeFocus(this);
19474 },
19475
19476 setSelected: function(/*Boolean*/ selected){
19477 // summary:
19478 // A Tree has a (single) currently selected node.
19479 // Mark that this node is/isn't that currently selected node.
19480 // description:
19481 // In particular, setting a node as selected involves setting tabIndex
19482 // so that when user tabs to the tree, focus will go to that node (only).
19483 dijit.setWaiState(this.labelNode, "selected", selected);
19484 dojo.toggleClass(this.rowNode, "dijitTreeRowSelected", selected);
19485 },
19486
19487 setFocusable: function(/*Boolean*/ selected){
19488 // summary:
19489 // A Tree has a (single) node that's focusable.
19490 // Mark that this node is/isn't that currently focsuable node.
19491 // description:
19492 // In particular, setting a node as selected involves setting tabIndex
19493 // so that when user tabs to the tree, focus will go to that node (only).
19494
19495 this.labelNode.setAttribute("tabIndex", selected ? "0" : "-1");
19496 },
19497
19498 _onClick: function(evt){
19499 // summary:
19500 // Handler for onclick event on a node
19501 // tags:
19502 // private
19503 this.tree._onClick(this, evt);
19504 },
19505 _onDblClick: function(evt){
19506 // summary:
19507 // Handler for ondblclick event on a node
19508 // tags:
19509 // private
19510 this.tree._onDblClick(this, evt);
19511 },
19512
19513 _onMouseEnter: function(evt){
19514 // summary:
19515 // Handler for onmouseenter event on a node
19516 // tags:
19517 // private
19518 this.tree._onNodeMouseEnter(this, evt);
19519 },
19520
19521 _onMouseLeave: function(evt){
19522 // summary:
19523 // Handler for onmouseenter event on a node
19524 // tags:
19525 // private
19526 this.tree._onNodeMouseLeave(this, evt);
19527 }
19528});
19529
19530dojo.declare(
19531 "dijit.Tree",
19532 [dijit._Widget, dijit._Templated],
19533{
19534 // summary:
19535 // This widget displays hierarchical data from a store.
19536
19537 // store: [deprecated] String||dojo.data.Store
19538 // Deprecated. Use "model" parameter instead.
19539 // The store to get data to display in the tree.
19540 store: null,
19541
19542 // model: dijit.Tree.model
19543 // Interface to read tree data, get notifications of changes to tree data,
19544 // and for handling drop operations (i.e drag and drop onto the tree)
19545 model: null,
19546
19547 // query: [deprecated] anything
19548 // Deprecated. User should specify query to the model directly instead.
19549 // Specifies datastore query to return the root item or top items for the tree.
19550 query: null,
19551
19552 // label: [deprecated] String
19553 // Deprecated. Use dijit.tree.ForestStoreModel directly instead.
19554 // Used in conjunction with query parameter.
19555 // If a query is specified (rather than a root node id), and a label is also specified,
19556 // then a fake root node is created and displayed, with this label.
19557 label: "",
19558
19559 // showRoot: [const] Boolean
19560 // Should the root node be displayed, or hidden?
19561 showRoot: true,
19562
19563 // childrenAttr: [deprecated] String[]
19564 // Deprecated. This information should be specified in the model.
19565 // One ore more attributes that holds children of a tree node
19566 childrenAttr: ["children"],
19567
19568 // path: String[] or Item[]
19569 // Full path from rootNode to selected node expressed as array of items or array of ids.
19570 // Since setting the path may be asynchronous (because ofwaiting on dojo.data), set("path", ...)
19571 // returns a Deferred to indicate when the set is complete.
19572 path: [],
19573
19574 // selectedItem: [readonly] Item
19575 // The currently selected item in this tree.
19576 // This property can only be set (via set('selectedItem', ...)) when that item is already
19577 // visible in the tree. (I.e. the tree has already been expanded to show that node.)
19578 // Should generally use `path` attribute to set the selected item instead.
19579 selectedItem: null,
19580
19581 // openOnClick: Boolean
19582 // If true, clicking a folder node's label will open it, rather than calling onClick()
19583 openOnClick: false,
19584
19585 // openOnDblClick: Boolean
19586 // If true, double-clicking a folder node's label will open it, rather than calling onDblClick()
19587 openOnDblClick: false,
19588
19589 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"),
19590
19591 // persist: Boolean
19592 // Enables/disables use of cookies for state saving.
19593 persist: true,
19594
19595 // autoExpand: Boolean
19596 // Fully expand the tree on load. Overrides `persist`
19597 autoExpand: false,
19598
19599 // dndController: [protected] String
19600 // Class name to use as as the dnd controller. Specifying this class enables DnD.
19601 // Generally you should specify this as "dijit.tree.dndSource".
19602 dndController: null,
19603
19604 // parameters to pull off of the tree and pass on to the dndController as its params
19605 dndParams: ["onDndDrop","itemCreator","onDndCancel","checkAcceptance", "checkItemAcceptance", "dragThreshold", "betweenThreshold"],
19606
19607 //declare the above items so they can be pulled from the tree's markup
19608
19609 // onDndDrop: [protected] Function
19610 // Parameter to dndController, see `dijit.tree.dndSource.onDndDrop`.
19611 // Generally this doesn't need to be set.
19612 onDndDrop: null,
19613
19614 /*=====
19615 itemCreator: function(nodes, target, source){
19616 // summary:
19617 // Returns objects passed to `Tree.model.newItem()` based on DnD nodes
19618 // dropped onto the tree. Developer must override this method to enable
19619 // dropping from external sources onto this Tree, unless the Tree.model's items
19620 // happen to look like {id: 123, name: "Apple" } with no other attributes.
19621 // description:
19622 // For each node in nodes[], which came from source, create a hash of name/value
19623 // pairs to be passed to Tree.model.newItem(). Returns array of those hashes.
19624 // nodes: DomNode[]
19625 // The DOMNodes dragged from the source container
19626 // target: DomNode
19627 // The target TreeNode.rowNode
19628 // source: dojo.dnd.Source
19629 // The source container the nodes were dragged from, perhaps another Tree or a plain dojo.dnd.Source
19630 // returns: Object[]
19631 // Array of name/value hashes for each new item to be added to the Tree, like:
19632 // | [
19633 // | { id: 123, label: "apple", foo: "bar" },
19634 // | { id: 456, label: "pear", zaz: "bam" }
19635 // | ]
19636 // tags:
19637 // extension
19638 return [{}];
19639 },
19640 =====*/
19641 itemCreator: null,
19642
19643 // onDndCancel: [protected] Function
19644 // Parameter to dndController, see `dijit.tree.dndSource.onDndCancel`.
19645 // Generally this doesn't need to be set.
19646 onDndCancel: null,
19647
19648/*=====
19649 checkAcceptance: function(source, nodes){
19650 // summary:
19651 // Checks if the Tree itself can accept nodes from this source
19652 // source: dijit.tree._dndSource
19653 // The source which provides items
19654 // nodes: DOMNode[]
19655 // Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
19656 // source is a dijit.Tree.
19657 // tags:
19658 // extension
19659 return true; // Boolean
19660 },
19661=====*/
19662 checkAcceptance: null,
19663
19664/*=====
19665 checkItemAcceptance: function(target, source, position){
19666 // summary:
19667 // Stub function to be overridden if one wants to check for the ability to drop at the node/item level
19668 // description:
19669 // In the base case, this is called to check if target can become a child of source.
19670 // When betweenThreshold is set, position="before" or "after" means that we
19671 // are asking if the source node can be dropped before/after the target node.
19672 // target: DOMNode
19673 // The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
19674 // Use dijit.getEnclosingWidget(target) to get the TreeNode.
19675 // source: dijit.tree.dndSource
19676 // The (set of) nodes we are dropping
19677 // position: String
19678 // "over", "before", or "after"
19679 // tags:
19680 // extension
19681 return true; // Boolean
19682 },
19683=====*/
19684 checkItemAcceptance: null,
19685
19686 // dragThreshold: Integer
19687 // Number of pixels mouse moves before it's considered the start of a drag operation
19688 dragThreshold: 5,
19689
19690 // betweenThreshold: Integer
19691 // Set to a positive value to allow drag and drop "between" nodes.
19692 //
19693 // If during DnD mouse is over a (target) node but less than betweenThreshold
19694 // pixels from the bottom edge, dropping the the dragged node will make it
19695 // the next sibling of the target node, rather than the child.
19696 //
19697 // Similarly, if mouse is over a target node but less that betweenThreshold
19698 // pixels from the top edge, dropping the dragged node will make it
19699 // the target node's previous sibling rather than the target node's child.
19700 betweenThreshold: 0,
19701
19702 // _nodePixelIndent: Integer
19703 // Number of pixels to indent tree nodes (relative to parent node).
19704 // Default is 19 but can be overridden by setting CSS class dijitTreeIndent
19705 // and calling resize() or startup() on tree after it's in the DOM.
19706 _nodePixelIndent: 19,
19707
19708 _publish: function(/*String*/ topicName, /*Object*/ message){
19709 // summary:
19710 // Publish a message for this widget/topic
19711 dojo.publish(this.id, [dojo.mixin({tree: this, event: topicName}, message || {})]);
19712 },
19713
19714 postMixInProperties: function(){
19715 this.tree = this;
19716
19717 if(this.autoExpand){
19718 // There's little point in saving opened/closed state of nodes for a Tree
19719 // that initially opens all it's nodes.
19720 this.persist = false;
19721 }
19722
19723 this._itemNodesMap={};
19724
19725 if(!this.cookieName){
19726 this.cookieName = this.id + "SaveStateCookie";
19727 }
19728
19729 this._loadDeferred = new dojo.Deferred();
19730
19731 this.inherited(arguments);
19732 },
19733
19734 postCreate: function(){
19735 this._initState();
19736
19737 // Create glue between store and Tree, if not specified directly by user
19738 if(!this.model){
19739 this._store2model();
19740 }
19741
19742 // monitor changes to items
19743 this.connect(this.model, "onChange", "_onItemChange");
19744 this.connect(this.model, "onChildrenChange", "_onItemChildrenChange");
19745 this.connect(this.model, "onDelete", "_onItemDelete");
19746
19747 this._load();
19748
19749 this.inherited(arguments);
19750
19751 if(this.dndController){
19752 if(dojo.isString(this.dndController)){
19753 this.dndController = dojo.getObject(this.dndController);
19754 }
19755 var params={};
19756 for(var i=0; i<this.dndParams.length;i++){
19757 if(this[this.dndParams[i]]){
19758 params[this.dndParams[i]] = this[this.dndParams[i]];
19759 }
19760 }
19761 this.dndController = new this.dndController(this, params);
19762 }
19763 },
19764
19765 _store2model: function(){
19766 // summary:
19767 // User specified a store&query rather than model, so create model from store/query
19768 this._v10Compat = true;
19769 dojo.deprecated("Tree: from version 2.0, should specify a model object rather than a store/query");
19770
19771 var modelParams = {
19772 id: this.id + "_ForestStoreModel",
19773 store: this.store,
19774 query: this.query,
19775 childrenAttrs: this.childrenAttr
19776 };
19777
19778 // Only override the model's mayHaveChildren() method if the user has specified an override
19779 if(this.params.mayHaveChildren){
19780 modelParams.mayHaveChildren = dojo.hitch(this, "mayHaveChildren");
19781 }
19782
19783 if(this.params.getItemChildren){
19784 modelParams.getChildren = dojo.hitch(this, function(item, onComplete, onError){
19785 this.getItemChildren((this._v10Compat && item === this.model.root) ? null : item, onComplete, onError);
19786 });
19787 }
19788 this.model = new dijit.tree.ForestStoreModel(modelParams);
19789
19790 // For backwards compatibility, the visibility of the root node is controlled by
19791 // whether or not the user has specified a label
19792 this.showRoot = Boolean(this.label);
19793 },
19794
19795 onLoad: function(){
19796 // summary:
19797 // Called when tree finishes loading and expanding.
19798 // description:
19799 // If persist == true the loading may encompass many levels of fetches
19800 // from the data store, each asynchronous. Waits for all to finish.
19801 // tags:
19802 // callback
19803 },
19804
19805 _load: function(){
19806 // summary:
19807 // Initial load of the tree.
19808 // Load root node (possibly hidden) and it's children.
19809 this.model.getRoot(
19810 dojo.hitch(this, function(item){
19811 var rn = (this.rootNode = this.tree._createTreeNode({
19812 item: item,
19813 tree: this,
19814 isExpandable: true,
19815 label: this.label || this.getLabel(item),
19816 indent: this.showRoot ? 0 : -1
19817 }));
19818 if(!this.showRoot){
19819 rn.rowNode.style.display="none";
19820 }
19821 this.domNode.appendChild(rn.domNode);
19822 var identity = this.model.getIdentity(item);
19823 if(this._itemNodesMap[identity]){
19824 this._itemNodesMap[identity].push(rn);
19825 }else{
19826 this._itemNodesMap[identity] = [rn];
19827 }
19828
19829 rn._updateLayout(); // sets "dijitTreeIsRoot" CSS classname
19830
19831 // load top level children and then fire onLoad() event
19832 this._expandNode(rn).addCallback(dojo.hitch(this, function(){
19833 this._loadDeferred.callback(true);
19834 this.onLoad();
19835 }));
19836 }),
19837 function(err){
19838 console.error(this, ": error loading root: ", err);
19839 }
19840 );
19841 },
19842
19843 getNodesByItem: function(/*dojo.data.Item or id*/ item){
19844 // summary:
19845 // Returns all tree nodes that refer to an item
19846 // returns:
19847 // Array of tree nodes that refer to passed item
19848
19849 if(!item){ return []; }
19850 var identity = dojo.isString(item) ? item : this.model.getIdentity(item);
19851 // return a copy so widget don't get messed up by changes to returned array
19852 return [].concat(this._itemNodesMap[identity]);
19853 },
19854
19855 _setSelectedItemAttr: function(/*dojo.data.Item or id*/ item){
19856 // summary:
19857 // Select a tree node related to passed item.
19858 // WARNING: if model use multi-parented items or desired tree node isn't already loaded
19859 // behavior is undefined. Use set('path', ...) instead.
19860
19861 var oldValue = this.get("selectedItem");
19862 var identity = (!item || dojo.isString(item)) ? item : this.model.getIdentity(item);
19863 if(identity == oldValue ? this.model.getIdentity(oldValue) : null){ return; }
19864 var nodes = this._itemNodesMap[identity];
19865 this._selectNode((nodes && nodes[0]) || null); //select the first item
19866 },
19867
19868 _getSelectedItemAttr: function(){
19869 // summary:
19870 // Return item related to selected tree node.
19871 return this.selectedNode && this.selectedNode.item;
19872 },
19873
19874 _setPathAttr: function(/*Item[] || String[]*/ path){
19875 // summary:
19876 // Select the tree node identified by passed path.
19877 // path:
19878 // Array of items or item id's
19879 // returns:
19880 // Deferred to indicate when the set is complete
19881
19882 var d = new dojo.Deferred();
19883
19884 this._selectNode(null);
19885 if(!path || !path.length){
19886 d.resolve(true);
19887 return d;
19888 }
19889
19890 // If this is called during initialization, defer running until Tree has finished loading
19891 this._loadDeferred.addCallback(dojo.hitch(this, function(){
19892 if(!this.rootNode){
19893 d.reject(new Error("!this.rootNode"));
19894 return;
19895 }
19896 if(path[0] !== this.rootNode.item && (dojo.isString(path[0]) && path[0] != this.model.getIdentity(this.rootNode.item))){
19897 d.reject(new Error(this.id + ":path[0] doesn't match this.rootNode.item. Maybe you are using the wrong tree."));
19898 return;
19899 }
19900 path.shift();
19901
19902 var node = this.rootNode;
19903
19904 function advance(){
19905 // summary:
19906 // Called when "node" has completed loading and expanding. Pop the next item from the path
19907 // (which must be a child of "node") and advance to it, and then recurse.
19908
19909 // Set item and identity to next item in path (node is pointing to the item that was popped
19910 // from the path _last_ time.
19911 var item = path.shift(),
19912 identity = dojo.isString(item) ? item : this.model.getIdentity(item);
19913
19914 // Change "node" from previous item in path to the item we just popped from path
19915 dojo.some(this._itemNodesMap[identity], function(n){
19916 if(n.getParent() == node){
19917 node = n;
19918 return true;
19919 }
19920 return false;
19921 });
19922
19923 if(path.length){
19924 // Need to do more expanding
19925 this._expandNode(node).addCallback(dojo.hitch(this, advance));
19926 }else{
19927 // Final destination node, select it
19928 this._selectNode(node);
19929
19930 // signal that path setting is finished
19931 d.resolve(true);
19932 }
19933 }
19934
19935 this._expandNode(node).addCallback(dojo.hitch(this, advance));
19936 }));
19937
19938 return d;
19939 },
19940
19941 _getPathAttr: function(){
19942 // summary:
19943 // Return an array of items that is the path to selected tree node.
19944 if(!this.selectedNode){ return; }
19945 var res = [];
19946 var treeNode = this.selectedNode;
19947 while(treeNode && treeNode !== this.rootNode){
19948 res.unshift(treeNode.item);
19949 treeNode = treeNode.getParent();
19950 }
19951 res.unshift(this.rootNode.item);
19952 return res;
19953 },
19954
19955 ////////////// Data store related functions //////////////////////
19956 // These just get passed to the model; they are here for back-compat
19957
19958 mayHaveChildren: function(/*dojo.data.Item*/ item){
19959 // summary:
19960 // Deprecated. This should be specified on the model itself.
19961 //
19962 // Overridable function to tell if an item has or may have children.
19963 // Controls whether or not +/- expando icon is shown.
19964 // (For efficiency reasons we may not want to check if an element actually
19965 // has children until user clicks the expando node)
19966 // tags:
19967 // deprecated
19968 },
19969
19970 getItemChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete){
19971 // summary:
19972 // Deprecated. This should be specified on the model itself.
19973 //
19974 // Overridable function that return array of child items of given parent item,
19975 // or if parentItem==null then return top items in tree
19976 // tags:
19977 // deprecated
19978 },
19979
19980 ///////////////////////////////////////////////////////
19981 // Functions for converting an item to a TreeNode
19982 getLabel: function(/*dojo.data.Item*/ item){
19983 // summary:
19984 // Overridable function to get the label for a tree node (given the item)
19985 // tags:
19986 // extension
19987 return this.model.getLabel(item); // String
19988 },
19989
19990 getIconClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
19991 // summary:
19992 // Overridable function to return CSS class name to display icon
19993 // tags:
19994 // extension
19995 return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "dijitLeaf"
19996 },
19997
19998 getLabelClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
19999 // summary:
20000 // Overridable function to return CSS class name to display label
20001 // tags:
20002 // extension
20003 },
20004
20005 getRowClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
20006 // summary:
20007 // Overridable function to return CSS class name to display row
20008 // tags:
20009 // extension
20010 },
20011
20012 getIconStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
20013 // summary:
20014 // Overridable function to return CSS styles to display icon
20015 // returns:
20016 // Object suitable for input to dojo.style() like {backgroundImage: "url(...)"}
20017 // tags:
20018 // extension
20019 },
20020
20021 getLabelStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
20022 // summary:
20023 // Overridable function to return CSS styles to display label
20024 // returns:
20025 // Object suitable for input to dojo.style() like {color: "red", background: "green"}
20026 // tags:
20027 // extension
20028 },
20029
20030 getRowStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
20031 // summary:
20032 // Overridable function to return CSS styles to display row
20033 // returns:
20034 // Object suitable for input to dojo.style() like {background-color: "#bbb"}
20035 // tags:
20036 // extension
20037 },
20038
20039 getTooltip: function(/*dojo.data.Item*/ item){
20040 // summary:
20041 // Overridable function to get the tooltip for a tree node (given the item)
20042 // tags:
20043 // extension
20044 return ""; // String
20045 },
20046
20047 /////////// Keyboard and Mouse handlers ////////////////////
20048
20049 _onKeyPress: function(/*Event*/ e){
20050 // summary:
20051 // Translates keypress events into commands for the controller
20052 if(e.altKey){ return; }
20053 var dk = dojo.keys;
20054 var treeNode = dijit.getEnclosingWidget(e.target);
20055 if(!treeNode){ return; }
20056
20057 var key = e.charOrCode;
20058 if(typeof key == "string"){ // handle printables (letter navigation)
20059 // Check for key navigation.
20060 if(!e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey){
20061 this._onLetterKeyNav( { node: treeNode, key: key.toLowerCase() } );
20062 dojo.stopEvent(e);
20063 }
20064 }else{ // handle non-printables (arrow keys)
20065 // clear record of recent printables (being saved for multi-char letter navigation),
20066 // because "a", down-arrow, "b" shouldn't search for "ab"
20067 if(this._curSearch){
20068 clearTimeout(this._curSearch.timer);
20069 delete this._curSearch;
20070 }
20071
20072 var map = this._keyHandlerMap;
20073 if(!map){
20074 // setup table mapping keys to events
20075 map = {};
20076 map[dk.ENTER]="_onEnterKey";
20077 map[this.isLeftToRight() ? dk.LEFT_ARROW : dk.RIGHT_ARROW]="_onLeftArrow";
20078 map[this.isLeftToRight() ? dk.RIGHT_ARROW : dk.LEFT_ARROW]="_onRightArrow";
20079 map[dk.UP_ARROW]="_onUpArrow";
20080 map[dk.DOWN_ARROW]="_onDownArrow";
20081 map[dk.HOME]="_onHomeKey";
20082 map[dk.END]="_onEndKey";
20083 this._keyHandlerMap = map;
20084 }
20085 if(this._keyHandlerMap[key]){
20086 this[this._keyHandlerMap[key]]( { node: treeNode, item: treeNode.item, evt: e } );
20087 dojo.stopEvent(e);
20088 }
20089 }
20090 },
20091
20092 _onEnterKey: function(/*Object*/ message, /*Event*/ evt){
20093 this._publish("execute", { item: message.item, node: message.node } );
20094 this._selectNode(message.node);
20095 this.onClick(message.item, message.node, evt);
20096 },
20097
20098 _onDownArrow: function(/*Object*/ message){
20099 // summary:
20100 // down arrow pressed; get next visible node, set focus there
20101 var node = this._getNextNode(message.node);
20102 if(node && node.isTreeNode){
20103 this.focusNode(node);
20104 }
20105 },
20106
20107 _onUpArrow: function(/*Object*/ message){
20108 // summary:
20109 // Up arrow pressed; move to previous visible node
20110
20111 var node = message.node;
20112
20113 // if younger siblings
20114 var previousSibling = node.getPreviousSibling();
20115 if(previousSibling){
20116 node = previousSibling;
20117 // if the previous node is expanded, dive in deep
20118 while(node.isExpandable && node.isExpanded && node.hasChildren()){
20119 // move to the last child
20120 var children = node.getChildren();
20121 node = children[children.length-1];
20122 }
20123 }else{
20124 // if this is the first child, return the parent
20125 // unless the parent is the root of a tree with a hidden root
20126 var parent = node.getParent();
20127 if(!(!this.showRoot && parent === this.rootNode)){
20128 node = parent;
20129 }
20130 }
20131
20132 if(node && node.isTreeNode){
20133 this.focusNode(node);
20134 }
20135 },
20136
20137 _onRightArrow: function(/*Object*/ message){
20138 // summary:
20139 // Right arrow pressed; go to child node
20140 var node = message.node;
20141
20142 // if not expanded, expand, else move to 1st child
20143 if(node.isExpandable && !node.isExpanded){
20144 this._expandNode(node);
20145 }else if(node.hasChildren()){
20146 node = node.getChildren()[0];
20147 if(node && node.isTreeNode){
20148 this.focusNode(node);
20149 }
20150 }
20151 },
20152
20153 _onLeftArrow: function(/*Object*/ message){
20154 // summary:
20155 // Left arrow pressed.
20156 // If not collapsed, collapse, else move to parent.
20157
20158 var node = message.node;
20159
20160 if(node.isExpandable && node.isExpanded){
20161 this._collapseNode(node);
20162 }else{
20163 var parent = node.getParent();
20164 if(parent && parent.isTreeNode && !(!this.showRoot && parent === this.rootNode)){
20165 this.focusNode(parent);
20166 }
20167 }
20168 },
20169
20170 _onHomeKey: function(){
20171 // summary:
20172 // Home key pressed; get first visible node, and set focus there
20173 var node = this._getRootOrFirstNode();
20174 if(node){
20175 this.focusNode(node);
20176 }
20177 },
20178
20179 _onEndKey: function(/*Object*/ message){
20180 // summary:
20181 // End key pressed; go to last visible node.
20182
20183 var node = this.rootNode;
20184 while(node.isExpanded){
20185 var c = node.getChildren();
20186 node = c[c.length - 1];
20187 }
20188
20189 if(node && node.isTreeNode){
20190 this.focusNode(node);
20191 }
20192 },
20193
20194 // multiCharSearchDuration: Number
20195 // If multiple characters are typed where each keystroke happens within
20196 // multiCharSearchDuration of the previous keystroke,
20197 // search for nodes matching all the keystrokes.
20198 //
20199 // For example, typing "ab" will search for entries starting with
20200 // "ab" unless the delay between "a" and "b" is greater than multiCharSearchDuration.
20201 multiCharSearchDuration: 250,
20202
20203 _onLetterKeyNav: function(message){
20204 // summary:
20205 // Called when user presses a prinatable key; search for node starting with recently typed letters.
20206 // message: Object
20207 // Like { node: TreeNode, key: 'a' } where key is the key the user pressed.
20208
20209 // Branch depending on whether this key starts a new search, or modifies an existing search
20210 var cs = this._curSearch;
20211 if(cs){
20212 // We are continuing a search. Ex: user has pressed 'a', and now has pressed
20213 // 'b', so we want to search for nodes starting w/"ab".
20214 cs.pattern = cs.pattern + message.key;
20215 clearTimeout(cs.timer);
20216 }else{
20217 // We are starting a new search
20218 cs = this._curSearch = {
20219 pattern: message.key,
20220 startNode: message.node
20221 };
20222 }
20223
20224 // set/reset timer to forget recent keystrokes
20225 var self = this;
20226 cs.timer = setTimeout(function(){
20227 delete self._curSearch;
20228 }, this.multiCharSearchDuration);
20229
20230 // Navigate to TreeNode matching keystrokes [entered so far].
20231 var node = cs.startNode;
20232 do{
20233 node = this._getNextNode(node);
20234 //check for last node, jump to first node if necessary
20235 if(!node){
20236 node = this._getRootOrFirstNode();
20237 }
20238 }while(node !== cs.startNode && (node.label.toLowerCase().substr(0, cs.pattern.length) != cs.pattern));
20239 if(node && node.isTreeNode){
20240 // no need to set focus if back where we started
20241 if(node !== cs.startNode){
20242 this.focusNode(node);
20243 }
20244 }
20245 },
20246
20247 _onClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
20248 // summary:
20249 // Translates click events into commands for the controller to process
20250
20251 var domElement = e.target,
20252 isExpandoClick = (domElement == nodeWidget.expandoNode || domElement == nodeWidget.expandoNodeText);
20253
20254 if( (this.openOnClick && nodeWidget.isExpandable) || isExpandoClick ){
20255 // expando node was clicked, or label of a folder node was clicked; open it
20256 if(nodeWidget.isExpandable){
20257 this._onExpandoClick({node:nodeWidget});
20258 }
20259 }else{
20260 this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
20261 this.onClick(nodeWidget.item, nodeWidget, e);
20262 this.focusNode(nodeWidget);
20263 }
20264 if(!isExpandoClick){
20265 this._selectNode(nodeWidget);
20266 }
20267 dojo.stopEvent(e);
20268 },
20269 _onDblClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
20270 // summary:
20271 // Translates double-click events into commands for the controller to process
20272
20273 var domElement = e.target,
20274 isExpandoClick = (domElement == nodeWidget.expandoNode || domElement == nodeWidget.expandoNodeText);
20275
20276 if( (this.openOnDblClick && nodeWidget.isExpandable) ||isExpandoClick ){
20277 // expando node was clicked, or label of a folder node was clicked; open it
20278 if(nodeWidget.isExpandable){
20279 this._onExpandoClick({node:nodeWidget});
20280 }
20281 }else{
20282 this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
20283 this.onDblClick(nodeWidget.item, nodeWidget, e);
20284 this.focusNode(nodeWidget);
20285 }
20286 if(!isExpandoClick){
20287 this._selectNode(nodeWidget);
20288 }
20289 dojo.stopEvent(e);
20290 },
20291
20292 _onExpandoClick: function(/*Object*/ message){
20293 // summary:
20294 // User clicked the +/- icon; expand or collapse my children.
20295 var node = message.node;
20296
20297 // If we are collapsing, we might be hiding the currently focused node.
20298 // Also, clicking the expando node might have erased focus from the current node.
20299 // For simplicity's sake just focus on the node with the expando.
20300 this.focusNode(node);
20301
20302 if(node.isExpanded){
20303 this._collapseNode(node);
20304 }else{
20305 this._expandNode(node);
20306 }
20307 },
20308
20309 onClick: function(/* dojo.data */ item, /*TreeNode*/ node, /*Event*/ evt){
20310 // summary:
20311 // Callback when a tree node is clicked
20312 // tags:
20313 // callback
20314 },
20315 onDblClick: function(/* dojo.data */ item, /*TreeNode*/ node, /*Event*/ evt){
20316 // summary:
20317 // Callback when a tree node is double-clicked
20318 // tags:
20319 // callback
20320 },
20321 onOpen: function(/* dojo.data */ item, /*TreeNode*/ node){
20322 // summary:
20323 // Callback when a node is opened
20324 // tags:
20325 // callback
20326 },
20327 onClose: function(/* dojo.data */ item, /*TreeNode*/ node){
20328 // summary:
20329 // Callback when a node is closed
20330 // tags:
20331 // callback
20332 },
20333
20334 _getNextNode: function(node){
20335 // summary:
20336 // Get next visible node
20337
20338 if(node.isExpandable && node.isExpanded && node.hasChildren()){
20339 // if this is an expanded node, get the first child
20340 return node.getChildren()[0]; // _TreeNode
20341 }else{
20342 // find a parent node with a sibling
20343 while(node && node.isTreeNode){
20344 var returnNode = node.getNextSibling();
20345 if(returnNode){
20346 return returnNode; // _TreeNode
20347 }
20348 node = node.getParent();
20349 }
20350 return null;
20351 }
20352 },
20353
20354 _getRootOrFirstNode: function(){
20355 // summary:
20356 // Get first visible node
20357 return this.showRoot ? this.rootNode : this.rootNode.getChildren()[0];
20358 },
20359
20360 _collapseNode: function(/*_TreeNode*/ node){
20361 // summary:
20362 // Called when the user has requested to collapse the node
20363
20364 if(node._expandNodeDeferred){
20365 delete node._expandNodeDeferred;
20366 }
20367
20368 if(node.isExpandable){
20369 if(node.state == "LOADING"){
20370 // ignore clicks while we are in the process of loading data
20371 return;
20372 }
20373
20374 node.collapse();
20375 this.onClose(node.item, node);
20376
20377 if(node.item){
20378 this._state(node.item,false);
20379 this._saveState();
20380 }
20381 }
20382 },
20383
20384 _expandNode: function(/*_TreeNode*/ node, /*Boolean?*/ recursive){
20385 // summary:
20386 // Called when the user has requested to expand the node
20387 // recursive:
20388 // Internal flag used when _expandNode() calls itself, don't set.
20389 // returns:
20390 // Deferred that fires when the node is loaded and opened and (if persist=true) all it's descendants
20391 // that were previously opened too
20392
20393 if(node._expandNodeDeferred && !recursive){
20394 // there's already an expand in progress (or completed), so just return
20395 return node._expandNodeDeferred; // dojo.Deferred
20396 }
20397
20398 var model = this.model,
20399 item = node.item,
20400 _this = this;
20401
20402 switch(node.state){
20403 case "UNCHECKED":
20404 // need to load all the children, and then expand
20405 node.markProcessing();
20406
20407 // Setup deferred to signal when the load and expand are finished.
20408 // Save that deferred in this._expandDeferred as a flag that operation is in progress.
20409 var def = (node._expandNodeDeferred = new dojo.Deferred());
20410
20411 // Get the children
20412 model.getChildren(
20413 item,
20414 function(items){
20415 node.unmarkProcessing();
20416
20417 // Display the children and also start expanding any children that were previously expanded
20418 // (if this.persist == true). The returned Deferred will fire when those expansions finish.
20419 var scid = node.setChildItems(items);
20420
20421 // Call _expandNode() again but this time it will just to do the animation (default branch).
20422 // The returned Deferred will fire when the animation completes.
20423 // TODO: seems like I can avoid recursion and just use a deferred to sequence the events?
20424 var ed = _this._expandNode(node, true);
20425
20426 // After the above two tasks (setChildItems() and recursive _expandNode()) finish,
20427 // signal that I am done.
20428 scid.addCallback(function(){
20429 ed.addCallback(function(){
20430 def.callback();
20431 })
20432 });
20433 },
20434 function(err){
20435 console.error(_this, ": error loading root children: ", err);
20436 }
20437 );
20438 break;
20439
20440 default: // "LOADED"
20441 // data is already loaded; just expand node
20442 def = (node._expandNodeDeferred = node.expand());
20443
20444 this.onOpen(node.item, node);
20445
20446 if(item){
20447 this._state(item, true);
20448 this._saveState();
20449 }
20450 }
20451
20452 return def; // dojo.Deferred
20453 },
20454
20455 ////////////////// Miscellaneous functions ////////////////
20456
20457 focusNode: function(/* _tree.Node */ node){
20458 // summary:
20459 // Focus on the specified node (which must be visible)
20460 // tags:
20461 // protected
20462
20463 // set focus so that the label will be voiced using screen readers
20464 dijit.focus(node.labelNode);
20465 },
20466
20467 _selectNode: function(/*_tree.Node*/ node){
20468 // summary:
20469 // Mark specified node as select, and unmark currently selected node.
20470 // tags:
20471 // protected
20472
20473 if(this.selectedNode && !this.selectedNode._destroyed){
20474 this.selectedNode.setSelected(false);
20475 }
20476 if(node){
20477 node.setSelected(true);
20478 }
20479 this.selectedNode = node;
20480 },
20481
20482 _onNodeFocus: function(/*dijit._Widget*/ node){
20483 // summary:
20484 // Called when a TreeNode gets focus, either by user clicking
20485 // it, or programatically by arrow key handling code.
20486 // description:
20487 // It marks that the current node is the selected one, and the previously
20488 // selected node no longer is.
20489
20490 if(node && node != this.lastFocused){
20491 if(this.lastFocused && !this.lastFocused._destroyed){
20492 // mark that the previously focsable node is no longer focusable
20493 this.lastFocused.setFocusable(false);
20494 }
20495
20496 // mark that the new node is the currently selected one
20497 node.setFocusable(true);
20498 this.lastFocused = node;
20499 }
20500 },
20501
20502 _onNodeMouseEnter: function(/*dijit._Widget*/ node){
20503 // summary:
20504 // Called when mouse is over a node (onmouseenter event),
20505 // this is monitored by the DND code
20506 },
20507
20508 _onNodeMouseLeave: function(/*dijit._Widget*/ node){
20509 // summary:
20510 // Called when mouse leaves a node (onmouseleave event),
20511 // this is monitored by the DND code
20512 },
20513
20514 //////////////// Events from the model //////////////////////////
20515
20516 _onItemChange: function(/*Item*/ item){
20517 // summary:
20518 // Processes notification of a change to an item's scalar values like label
20519 var model = this.model,
20520 identity = model.getIdentity(item),
20521 nodes = this._itemNodesMap[identity];
20522
20523 if(nodes){
20524 var label = this.getLabel(item),
20525 tooltip = this.getTooltip(item);
20526 dojo.forEach(nodes, function(node){
20527 node.set({
20528 item: item, // theoretically could be new JS Object representing same item
20529 label: label,
20530 tooltip: tooltip
20531 });
20532 node._updateItemClasses(item);
20533 });
20534 }
20535 },
20536
20537 _onItemChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
20538 // summary:
20539 // Processes notification of a change to an item's children
20540 var model = this.model,
20541 identity = model.getIdentity(parent),
20542 parentNodes = this._itemNodesMap[identity];
20543
20544 if(parentNodes){
20545 dojo.forEach(parentNodes,function(parentNode){
20546 parentNode.setChildItems(newChildrenList);
20547 });
20548 }
20549 },
20550
20551 _onItemDelete: function(/*Item*/ item){
20552 // summary:
20553 // Processes notification of a deletion of an item
20554 var model = this.model,
20555 identity = model.getIdentity(item),
20556 nodes = this._itemNodesMap[identity];
20557
20558 if(nodes){
20559 dojo.forEach(nodes,function(node){
20560 var parent = node.getParent();
20561 if(parent){
20562 // if node has not already been orphaned from a _onSetItem(parent, "children", ..) call...
20563 parent.removeChild(node);
20564 }
20565 node.destroyRecursive();
20566 });
20567 delete this._itemNodesMap[identity];
20568 }
20569 },
20570
20571 /////////////// Miscellaneous funcs
20572
20573 _initState: function(){
20574 // summary:
20575 // Load in which nodes should be opened automatically
20576 if(this.persist){
20577 var cookie = dojo.cookie(this.cookieName);
20578 this._openedItemIds = {};
20579 if(cookie){
20580 dojo.forEach(cookie.split(','), function(item){
20581 this._openedItemIds[item] = true;
20582 }, this);
20583 }
20584 }
20585 },
20586 _state: function(item,expanded){
20587 // summary:
20588 // Query or set expanded state for an item,
20589 if(!this.persist){
20590 return false;
20591 }
20592 var id=this.model.getIdentity(item);
20593 if(arguments.length === 1){
20594 return this._openedItemIds[id];
20595 }
20596 if(expanded){
20597 this._openedItemIds[id] = true;
20598 }else{
20599 delete this._openedItemIds[id];
20600 }
20601 },
20602 _saveState: function(){
20603 // summary:
20604 // Create and save a cookie with the currently expanded nodes identifiers
20605 if(!this.persist){
20606 return;
20607 }
20608 var ary = [];
20609 for(var id in this._openedItemIds){
20610 ary.push(id);
20611 }
20612 dojo.cookie(this.cookieName, ary.join(","), {expires:365});
20613 },
20614
20615 destroy: function(){
20616 if(this._curSearch){
20617 clearTimeout(this._curSearch.timer);
20618 delete this._curSearch;
20619 }
20620 if(this.rootNode){
20621 this.rootNode.destroyRecursive();
20622 }
20623 if(this.dndController && !dojo.isString(this.dndController)){
20624 this.dndController.destroy();
20625 }
20626 this.rootNode = null;
20627 this.inherited(arguments);
20628 },
20629
20630 destroyRecursive: function(){
20631 // A tree is treated as a leaf, not as a node with children (like a grid),
20632 // but defining destroyRecursive for back-compat.
20633 this.destroy();
20634 },
20635
20636 resize: function(changeSize){
20637 if(changeSize){
20638 dojo.marginBox(this.domNode, changeSize);
20639 dojo.style(this.domNode, "overflow", "auto"); // for scrollbars
20640 }
20641
20642 // The only JS sizing involved w/tree is the indentation, which is specified
20643 // in CSS and read in through this dummy indentDetector node (tree must be
20644 // visible and attached to the DOM to read this)
20645 this._nodePixelIndent = dojo.marginBox(this.tree.indentDetector).w;
20646
20647 if(this.tree.rootNode){
20648 // If tree has already loaded, then reset indent for all the nodes
20649 this.tree.rootNode.set('indent', this.showRoot ? 0 : -1);
20650 }
20651 },
20652
20653 _createTreeNode: function(/*Object*/ args){
20654 // summary:
20655 // creates a TreeNode
20656 // description:
20657 // Developers can override this method to define their own TreeNode class;
20658 // However it will probably be removed in a future release in favor of a way
20659 // of just specifying a widget for the label, rather than one that contains
20660 // the children too.
20661 return new dijit._TreeNode(args);
20662 }
20663});
20664
20665// For back-compat. TODO: remove in 2.0
20666
20667
20668
20669}
20670
20671if(!dojo._hasResource["dijit.InlineEditBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
20672dojo._hasResource["dijit.InlineEditBox"] = true;
20673dojo.provide("dijit.InlineEditBox");
20674
20675
20676
20677
20678
20679
20680
20681
20682
20683
20684dojo.declare("dijit.InlineEditBox",
20685 dijit._Widget,
20686 {
20687 // summary:
20688 // An element with in-line edit capabilites
20689 //
20690 // description:
20691 // Behavior for an existing node (`<p>`, `<div>`, `<span>`, etc.) so that
20692 // when you click it, an editor shows up in place of the original
20693 // text. Optionally, Save and Cancel button are displayed below the edit widget.
20694 // When Save is clicked, the text is pulled from the edit
20695 // widget and redisplayed and the edit widget is again hidden.
20696 // By default a plain Textarea widget is used as the editor (or for
20697 // inline values a TextBox), but you can specify an editor such as
20698 // dijit.Editor (for editing HTML) or a Slider (for adjusting a number).
20699 // An edit widget must support the following API to be used:
20700 // - displayedValue or value as initialization parameter,
20701 // and available through set('displayedValue') / set('value')
20702 // - void focus()
20703 // - DOM-node focusNode = node containing editable text
20704
20705 // editing: [readonly] Boolean
20706 // Is the node currently in edit mode?
20707 editing: false,
20708
20709 // autoSave: Boolean
20710 // Changing the value automatically saves it; don't have to push save button
20711 // (and save button isn't even displayed)
20712 autoSave: true,
20713
20714 // buttonSave: String
20715 // Save button label
20716 buttonSave: "",
20717
20718 // buttonCancel: String
20719 // Cancel button label
20720 buttonCancel: "",
20721
20722 // renderAsHtml: Boolean
20723 // Set this to true if the specified Editor's value should be interpreted as HTML
20724 // rather than plain text (ex: `dijit.Editor`)
20725 renderAsHtml: false,
20726
20727 // editor: String
20728 // Class name for Editor widget
20729 editor: "dijit.form.TextBox",
20730
20731 // editorWrapper: String
20732 // Class name for widget that wraps the editor widget, displaying save/cancel
20733 // buttons.
20734 editorWrapper: "dijit._InlineEditor",
20735
20736 // editorParams: Object
20737 // Set of parameters for editor, like {required: true}
20738 editorParams: {},
20739
20740 onChange: function(value){
20741 // summary:
20742 // Set this handler to be notified of changes to value.
20743 // tags:
20744 // callback
20745 },
20746
20747 onCancel: function(){
20748 // summary:
20749 // Set this handler to be notified when editing is cancelled.
20750 // tags:
20751 // callback
20752 },
20753
20754 // width: String
20755 // Width of editor. By default it's width=100% (ie, block mode).
20756 width: "100%",
20757
20758 // value: String
20759 // The display value of the widget in read-only mode
20760 value: "",
20761
20762 // noValueIndicator: [const] String
20763 // The text that gets displayed when there is no value (so that the user has a place to click to edit)
20764 noValueIndicator: dojo.isIE <= 6 ? // font-family needed on IE6 but it messes up IE8
20765 "<span style='font-family: wingdings; text-decoration: underline;'>&nbsp;&nbsp;&nbsp;&nbsp;&#x270d;&nbsp;&nbsp;&nbsp;&nbsp;</span>" :
20766 "<span style='text-decoration: underline;'>&nbsp;&nbsp;&nbsp;&nbsp;&#x270d;&nbsp;&nbsp;&nbsp;&nbsp;</span>",
20767
20768 constructor: function(){
20769 // summary:
20770 // Sets up private arrays etc.
20771 // tags:
20772 // private
20773 this.editorParams = {};
20774 },
20775
20776 postMixInProperties: function(){
20777 this.inherited(arguments);
20778
20779 // save pointer to original source node, since Widget nulls-out srcNodeRef
20780 this.displayNode = this.srcNodeRef;
20781
20782 // connect handlers to the display node
20783 var events = {
20784 ondijitclick: "_onClick",
20785 onmouseover: "_onMouseOver",
20786 onmouseout: "_onMouseOut",
20787 onfocus: "_onMouseOver",
20788 onblur: "_onMouseOut"
20789 };
20790 for(var name in events){
20791 this.connect(this.displayNode, name, events[name]);
20792 }
20793 dijit.setWaiRole(this.displayNode, "button");
20794 if(!this.displayNode.getAttribute("tabIndex")){
20795 this.displayNode.setAttribute("tabIndex", 0);
20796 }
20797
20798 if(!this.value && !("value" in this.params)){ // "" is a good value if specified directly so check params){
20799 this.value = dojo.trim(this.renderAsHtml ? this.displayNode.innerHTML :
20800 (this.displayNode.innerText||this.displayNode.textContent||""));
20801 }
20802 if(!this.value){
20803 this.displayNode.innerHTML = this.noValueIndicator;
20804 }
20805
20806 dojo.addClass(this.displayNode, 'dijitInlineEditBoxDisplayMode');
20807 },
20808
20809 setDisabled: function(/*Boolean*/ disabled){
20810 // summary:
20811 // Deprecated. Use set('disabled', ...) instead.
20812 // tags:
20813 // deprecated
20814 dojo.deprecated("dijit.InlineEditBox.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
20815 this.set('disabled', disabled);
20816 },
20817
20818 _setDisabledAttr: function(/*Boolean*/ disabled){
20819 // summary:
20820 // Hook to make set("disabled", ...) work.
20821 // Set disabled state of widget.
20822 this.disabled = disabled;
20823 dijit.setWaiState(this.domNode, "disabled", disabled);
20824 if(disabled){
20825 this.displayNode.removeAttribute("tabIndex");
20826 }else{
20827 this.displayNode.setAttribute("tabIndex", 0);
20828 }
20829 dojo.toggleClass(this.displayNode, "dijitInlineEditBoxDisplayModeDisabled", disabled);
20830 },
20831
20832 _onMouseOver: function(){
20833 // summary:
20834 // Handler for onmouseover and onfocus event.
20835 // tags:
20836 // private
20837 if(!this.disabled){
20838 dojo.addClass(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
20839 }
20840 },
20841
20842 _onMouseOut: function(){
20843 // summary:
20844 // Handler for onmouseout and onblur event.
20845 // tags:
20846 // private
20847 dojo.removeClass(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
20848 },
20849
20850 _onClick: function(/*Event*/ e){
20851 // summary:
20852 // Handler for onclick event.
20853 // tags:
20854 // private
20855 if(this.disabled){ return; }
20856 if(e){ dojo.stopEvent(e); }
20857 this._onMouseOut();
20858
20859 // Since FF gets upset if you move a node while in an event handler for that node...
20860 setTimeout(dojo.hitch(this, "edit"), 0);
20861 },
20862
20863 edit: function(){
20864 // summary:
20865 // Display the editor widget in place of the original (read only) markup.
20866 // tags:
20867 // private
20868
20869 if(this.disabled || this.editing){ return; }
20870 this.editing = true;
20871
20872 // save some display node values that can be restored later
20873 this._savedPosition = dojo.style(this.displayNode, "position") || "static";
20874 this._savedOpacity = dojo.style(this.displayNode, "opacity") || "1";
20875 this._savedTabIndex = dojo.attr(this.displayNode, "tabIndex") || "0";
20876
20877 if(this.wrapperWidget){
20878 var ew = this.wrapperWidget.editWidget;
20879 ew.set("displayedValue" in ew ? "displayedValue" : "value", this.value);
20880 }else{
20881 // Placeholder for edit widget
20882 // Put place holder (and eventually editWidget) before the display node so that it's positioned correctly
20883 // when Calendar dropdown appears, which happens automatically on focus.
20884 var placeholder = dojo.create("span", null, this.domNode, "before");
20885
20886 // Create the editor wrapper (the thing that holds the editor widget and the save/cancel buttons)
20887 var ewc = dojo.getObject(this.editorWrapper);
20888 this.wrapperWidget = new ewc({
20889 value: this.value,
20890 buttonSave: this.buttonSave,
20891 buttonCancel: this.buttonCancel,
20892 dir: this.dir,
20893 lang: this.lang,
20894 tabIndex: this._savedTabIndex,
20895 editor: this.editor,
20896 inlineEditBox: this,
20897 sourceStyle: dojo.getComputedStyle(this.displayNode),
20898 save: dojo.hitch(this, "save"),
20899 cancel: dojo.hitch(this, "cancel")
20900 }, placeholder);
20901 }
20902 var ww = this.wrapperWidget;
20903
20904 if(dojo.isIE){
20905 dijit.focus(dijit.getFocus()); // IE (at least 8) needs help with tab order changes
20906 }
20907 // to avoid screen jitter, we first create the editor with position:absolute, visibility:hidden,
20908 // and then when it's finished rendering, we switch from display mode to editor
20909 // position:absolute releases screen space allocated to the display node
20910 // opacity:0 is the same as visibility:hidden but is still focusable
20911 // visiblity:hidden removes focus outline
20912
20913 dojo.style(this.displayNode, { position: "absolute", opacity: "0", display: "none" }); // makes display node invisible, display style used for focus-ability
20914 dojo.style(ww.domNode, { position: this._savedPosition, visibility: "visible", opacity: "1" });
20915 dojo.attr(this.displayNode, "tabIndex", "-1"); // needed by WebKit for TAB from editor to skip displayNode
20916
20917 // Replace the display widget with edit widget, leaving them both displayed for a brief time so that
20918 // focus can be shifted without incident. (browser may needs some time to render the editor.)
20919 setTimeout(dojo.hitch(this, function(){
20920 ww.focus(); // both nodes are showing, so we can switch focus safely
20921 ww._resetValue = ww.getValue();
20922 }), 0);
20923 },
20924
20925 _onBlur: function(){
20926 // summary:
20927 // Called when focus moves outside the InlineEditBox.
20928 // Performs garbage collection.
20929 // tags:
20930 // private
20931
20932 this.inherited(arguments);
20933 if(!this.editing){
20934 /* causes IE focus problems, see TooltipDialog_a11y.html...
20935 setTimeout(dojo.hitch(this, function(){
20936 if(this.wrapperWidget){
20937 this.wrapperWidget.destroy();
20938 delete this.wrapperWidget;
20939 }
20940 }), 0);
20941 */
20942 }
20943 },
20944
20945 destroy: function(){
20946 if(this.wrapperWidget){
20947 this.wrapperWidget.destroy();
20948 delete this.wrapperWidget;
20949 }
20950 this.inherited(arguments);
20951 },
20952
20953 _showText: function(/*Boolean*/ focus){
20954 // summary:
20955 // Revert to display mode, and optionally focus on display node
20956 // tags:
20957 // private
20958
20959 var ww = this.wrapperWidget;
20960 dojo.style(ww.domNode, { position: "absolute", visibility: "hidden", opacity: "0" }); // hide the editor from mouse/keyboard events
20961 dojo.style(this.displayNode, { position: this._savedPosition, opacity: this._savedOpacity, display: "" }); // make the original text visible
20962 dojo.attr(this.displayNode, "tabIndex", this._savedTabIndex);
20963 if(focus){
20964 dijit.focus(this.displayNode);
20965 }
20966 },
20967
20968 save: function(/*Boolean*/ focus){
20969 // summary:
20970 // Save the contents of the editor and revert to display mode.
20971 // focus: Boolean
20972 // Focus on the display mode text
20973 // tags:
20974 // private
20975
20976 if(this.disabled || !this.editing){ return; }
20977 this.editing = false;
20978
20979 var ww = this.wrapperWidget;
20980 var value = ww.getValue();
20981 this.set('value', value); // display changed, formatted value
20982
20983 // tell the world that we have changed
20984 setTimeout(dojo.hitch(this, "onChange", value), 0); // setTimeout prevents browser freeze for long-running event handlers
20985
20986 this._showText(focus); // set focus as needed
20987 },
20988
20989 setValue: function(/*String*/ val){
20990 // summary:
20991 // Deprecated. Use set('value', ...) instead.
20992 // tags:
20993 // deprecated
20994 dojo.deprecated("dijit.InlineEditBox.setValue() is deprecated. Use set('value', ...) instead.", "", "2.0");
20995 return this.set("value", val);
20996 },
20997
20998 _setValueAttr: function(/*String*/ val){
20999 // summary:
21000 // Hook to make set("value", ...) work.
21001 // Inserts specified HTML value into this node, or an "input needed" character if node is blank.
21002
21003 this.value = val = dojo.trim(val);
21004 if(!this.renderAsHtml){
21005 val = val.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;").replace(/\n/g, "<br>");
21006 }
21007 this.displayNode.innerHTML = val || this.noValueIndicator;
21008 },
21009
21010 getValue: function(){
21011 // summary:
21012 // Deprecated. Use get('value') instead.
21013 // tags:
21014 // deprecated
21015 dojo.deprecated("dijit.InlineEditBox.getValue() is deprecated. Use get('value') instead.", "", "2.0");
21016 return this.get("value");
21017 },
21018
21019 cancel: function(/*Boolean*/ focus){
21020 // summary:
21021 // Revert to display mode, discarding any changes made in the editor
21022 // tags:
21023 // private
21024
21025 if(this.disabled || !this.editing){ return; }
21026 this.editing = false;
21027
21028 // tell the world that we have no changes
21029 setTimeout(dojo.hitch(this, "onCancel"), 0); // setTimeout prevents browser freeze for long-running event handlers
21030
21031 this._showText(focus);
21032 }
21033});
21034
21035dojo.declare(
21036 "dijit._InlineEditor",
21037 [dijit._Widget, dijit._Templated],
21038{
21039 // summary:
21040 // Internal widget used by InlineEditBox, displayed when in editing mode
21041 // to display the editor and maybe save/cancel buttons. Calling code should
21042 // connect to save/cancel methods to detect when editing is finished
21043 //
21044 // Has mainly the same parameters as InlineEditBox, plus these values:
21045 //
21046 // style: Object
21047 // Set of CSS attributes of display node, to replicate in editor
21048 //
21049 // value: String
21050 // Value as an HTML string or plain text string, depending on renderAsHTML flag
21051
21052 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"),
21053 widgetsInTemplate: true,
21054
21055 postMixInProperties: function(){
21056 this.inherited(arguments);
21057 this.messages = dojo.i18n.getLocalization("dijit", "common", this.lang);
21058 dojo.forEach(["buttonSave", "buttonCancel"], function(prop){
21059 if(!this[prop]){ this[prop] = this.messages[prop]; }
21060 }, this);
21061 },
21062
21063 postCreate: function(){
21064 // Create edit widget in place in the template
21065 var cls = dojo.getObject(this.editor);
21066
21067 // Copy the style from the source
21068 // Don't copy ALL properties though, just the necessary/applicable ones.
21069 // wrapperStyle/destStyle code is to workaround IE bug where getComputedStyle().fontSize
21070 // is a relative value like 200%, rather than an absolute value like 24px, and
21071 // the 200% can refer *either* to a setting on the node or it's ancestor (see #11175)
21072 var srcStyle = this.sourceStyle,
21073 editStyle = "line-height:" + srcStyle.lineHeight + ";",
21074 destStyle = dojo.getComputedStyle(this.domNode);
21075 dojo.forEach(["Weight","Family","Size","Style"], function(prop){
21076 var textStyle = srcStyle["font"+prop],
21077 wrapperStyle = destStyle["font"+prop];
21078 if(wrapperStyle != textStyle){
21079 editStyle += "font-"+prop+":"+srcStyle["font"+prop]+";";
21080 }
21081 }, this);
21082 dojo.forEach(["marginTop","marginBottom","marginLeft", "marginRight"], function(prop){
21083 this.domNode.style[prop] = srcStyle[prop];
21084 }, this);
21085 var width = this.inlineEditBox.width;
21086 if(width == "100%"){
21087 // block mode
21088 editStyle += "width:100%;";
21089 this.domNode.style.display = "block";
21090 }else{
21091 // inline-block mode
21092 editStyle += "width:" + (width + (Number(width) == width ? "px" : "")) + ";";
21093 }
21094 var editorParams = dojo.delegate(this.inlineEditBox.editorParams, {
21095 style: editStyle,
21096 dir: this.dir,
21097 lang: this.lang
21098 });
21099 editorParams[ "displayedValue" in cls.prototype ? "displayedValue" : "value"] = this.value;
21100 var ew = (this.editWidget = new cls(editorParams, this.editorPlaceholder));
21101
21102 if(this.inlineEditBox.autoSave){
21103 // Remove the save/cancel buttons since saving is done by simply tabbing away or
21104 // selecting a value from the drop down list
21105 dojo.destroy(this.buttonContainer);
21106
21107 // Selecting a value from a drop down list causes an onChange event and then we save
21108 this.connect(ew, "onChange", "_onChange");
21109
21110 // ESC and TAB should cancel and save. Note that edit widgets do a stopEvent() on ESC key (to
21111 // prevent Dialog from closing when the user just wants to revert the value in the edit widget),
21112 // so this is the only way we can see the key press event.
21113 this.connect(ew, "onKeyPress", "_onKeyPress");
21114 }else{
21115 // If possible, enable/disable save button based on whether the user has changed the value
21116 if("intermediateChanges" in cls.prototype){
21117 ew.set("intermediateChanges", true);
21118 this.connect(ew, "onChange", "_onIntermediateChange");
21119 this.saveButton.set("disabled", true);
21120 }
21121 }
21122 },
21123
21124 _onIntermediateChange: function(val){
21125 // summary:
21126 // Called for editor widgets that support the intermediateChanges=true flag as a way
21127 // to detect when to enable/disabled the save button
21128 this.saveButton.set("disabled", (this.getValue() == this._resetValue) || !this.enableSave());
21129 },
21130
21131 destroy: function(){
21132 this.editWidget.destroy(true); // let the parent wrapper widget clean up the DOM
21133 this.inherited(arguments);
21134 },
21135
21136 getValue: function(){
21137 // summary:
21138 // Return the [display] value of the edit widget
21139 var ew = this.editWidget;
21140 return String(ew.get("displayedValue" in ew ? "displayedValue" : "value"));
21141 },
21142
21143 _onKeyPress: function(e){
21144 // summary:
21145 // Handler for keypress in the edit box in autoSave mode.
21146 // description:
21147 // For autoSave widgets, if Esc/Enter, call cancel/save.
21148 // tags:
21149 // private
21150
21151 if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
21152 if(e.altKey || e.ctrlKey){ return; }
21153 // If Enter/Esc pressed, treat as save/cancel.
21154 if(e.charOrCode == dojo.keys.ESCAPE){
21155 dojo.stopEvent(e);
21156 this.cancel(true); // sets editing=false which short-circuits _onBlur processing
21157 }else if(e.charOrCode == dojo.keys.ENTER && e.target.tagName == "INPUT"){
21158 dojo.stopEvent(e);
21159 this._onChange(); // fire _onBlur and then save
21160 }
21161
21162 // _onBlur will handle TAB automatically by allowing
21163 // the TAB to change focus before we mess with the DOM: #6227
21164 // Expounding by request:
21165 // The current focus is on the edit widget input field.
21166 // save() will hide and destroy this widget.
21167 // We want the focus to jump from the currently hidden
21168 // displayNode, but since it's hidden, it's impossible to
21169 // unhide it, focus it, and then have the browser focus
21170 // away from it to the next focusable element since each
21171 // of these events is asynchronous and the focus-to-next-element
21172 // is already queued.
21173 // So we allow the browser time to unqueue the move-focus event
21174 // before we do all the hide/show stuff.
21175 }
21176 },
21177
21178 _onBlur: function(){
21179 // summary:
21180 // Called when focus moves outside the editor
21181 // tags:
21182 // private
21183
21184 this.inherited(arguments);
21185 if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
21186 if(this.getValue() == this._resetValue){
21187 this.cancel(false);
21188 }else if(this.enableSave()){
21189 this.save(false);
21190 }
21191 }
21192 },
21193
21194 _onChange: function(){
21195 // summary:
21196 // Called when the underlying widget fires an onChange event,
21197 // such as when the user selects a value from the drop down list of a ComboBox,
21198 // which means that the user has finished entering the value and we should save.
21199 // tags:
21200 // private
21201
21202 if(this.inlineEditBox.autoSave && this.inlineEditBox.editing && this.enableSave()){
21203 dojo.style(this.inlineEditBox.displayNode, { display: "" });
21204 dijit.focus(this.inlineEditBox.displayNode); // fires _onBlur which will save the formatted value
21205 }
21206 },
21207
21208 enableSave: function(){
21209 // summary:
21210 // User overridable function returning a Boolean to indicate
21211 // if the Save button should be enabled or not - usually due to invalid conditions
21212 // tags:
21213 // extension
21214 return (
21215 this.editWidget.isValid
21216 ? this.editWidget.isValid()
21217 : true
21218 );
21219 },
21220
21221 focus: function(){
21222 // summary:
21223 // Focus the edit widget.
21224 // tags:
21225 // protected
21226
21227 this.editWidget.focus();
21228 setTimeout(dojo.hitch(this, function(){
21229 if(this.editWidget.focusNode && this.editWidget.focusNode.tagName == "INPUT"){
21230 dijit.selectInputText(this.editWidget.focusNode);
21231 }
21232 }), 0);
21233 }
21234});
21235
21236}
21237
21238if(!dojo._hasResource["dijit.form.Form"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21239dojo._hasResource["dijit.form.Form"] = true;
21240dojo.provide("dijit.form.Form");
21241
21242
21243
21244
21245
21246dojo.declare(
21247 "dijit.form.Form",
21248 [dijit._Widget, dijit._Templated, dijit.form._FormMixin],
21249 {
21250 // summary:
21251 // Widget corresponding to HTML form tag, for validation and serialization
21252 //
21253 // example:
21254 // | <form dojoType="dijit.form.Form" id="myForm">
21255 // | Name: <input type="text" name="name" />
21256 // | </form>
21257 // | myObj = {name: "John Doe"};
21258 // | dijit.byId('myForm').set('value', myObj);
21259 // |
21260 // | myObj=dijit.byId('myForm').get('value');
21261
21262 // HTML <FORM> attributes
21263
21264 // name: String?
21265 // Name of form for scripting.
21266 name: "",
21267
21268 // action: String?
21269 // Server-side form handler.
21270 action: "",
21271
21272 // method: String?
21273 // HTTP method used to submit the form, either "GET" or "POST".
21274 method: "",
21275
21276 // encType: String?
21277 // Encoding type for the form, ex: application/x-www-form-urlencoded.
21278 encType: "",
21279
21280 // accept-charset: String?
21281 // List of supported charsets.
21282 "accept-charset": "",
21283
21284 // accept: String?
21285 // List of MIME types for file upload.
21286 accept: "",
21287
21288 // target: String?
21289 // Target frame for the document to be opened in.
21290 target: "",
21291
21292 templateString: "<form dojoAttachPoint='containerNode' dojoAttachEvent='onreset:_onReset,onsubmit:_onSubmit' ${!nameAttrSetting}></form>",
21293
21294 attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
21295 action: "",
21296 method: "",
21297 encType: "",
21298 "accept-charset": "",
21299 accept: "",
21300 target: ""
21301 }),
21302
21303 postMixInProperties: function(){
21304 // Setup name=foo string to be referenced from the template (but only if a name has been specified)
21305 // Unfortunately we can't use attributeMap to set the name due to IE limitations, see #8660
21306 this.nameAttrSetting = this.name ? ("name='" + this.name + "'") : "";
21307 this.inherited(arguments);
21308 },
21309
21310 execute: function(/*Object*/ formContents){
21311 // summary:
21312 // Deprecated: use submit()
21313 // tags:
21314 // deprecated
21315 },
21316
21317 onExecute: function(){
21318 // summary:
21319 // Deprecated: use onSubmit()
21320 // tags:
21321 // deprecated
21322 },
21323
21324 _setEncTypeAttr: function(/*String*/ value){
21325 this.encType = value;
21326 dojo.attr(this.domNode, "encType", value);
21327 if(dojo.isIE){ this.domNode.encoding = value; }
21328 },
21329
21330 postCreate: function(){
21331 // IE tries to hide encType
21332 // TODO: this code should be in parser, not here.
21333 if(dojo.isIE && this.srcNodeRef && this.srcNodeRef.attributes){
21334 var item = this.srcNodeRef.attributes.getNamedItem('encType');
21335 if(item && !item.specified && (typeof item.value == "string")){
21336 this.set('encType', item.value);
21337 }
21338 }
21339 this.inherited(arguments);
21340 },
21341
21342 reset: function(/*Event?*/ e){
21343 // summary:
21344 // restores all widget values back to their init values,
21345 // calls onReset() which can cancel the reset by returning false
21346
21347 // create fake event so we can know if preventDefault() is called
21348 var faux = {
21349 returnValue: true, // the IE way
21350 preventDefault: function(){ // not IE
21351 this.returnValue = false;
21352 },
21353 stopPropagation: function(){},
21354 currentTarget: e ? e.target : this.domNode,
21355 target: e ? e.target : this.domNode
21356 };
21357 // if return value is not exactly false, and haven't called preventDefault(), then reset
21358 if(!(this.onReset(faux) === false) && faux.returnValue){
21359 this.inherited(arguments, []);
21360 }
21361 },
21362
21363 onReset: function(/*Event?*/ e){
21364 // summary:
21365 // Callback when user resets the form. This method is intended
21366 // to be over-ridden. When the `reset` method is called
21367 // programmatically, the return value from `onReset` is used
21368 // to compute whether or not resetting should proceed
21369 // tags:
21370 // callback
21371 return true; // Boolean
21372 },
21373
21374 _onReset: function(e){
21375 this.reset(e);
21376 dojo.stopEvent(e);
21377 return false;
21378 },
21379
21380 _onSubmit: function(e){
21381 var fp = dijit.form.Form.prototype;
21382 // TODO: remove this if statement beginning with 2.0
21383 if(this.execute != fp.execute || this.onExecute != fp.onExecute){
21384 dojo.deprecated("dijit.form.Form:execute()/onExecute() are deprecated. Use onSubmit() instead.", "", "2.0");
21385 this.onExecute();
21386 this.execute(this.getValues());
21387 }
21388 if(this.onSubmit(e) === false){ // only exactly false stops submit
21389 dojo.stopEvent(e);
21390 }
21391 },
21392
21393 onSubmit: function(/*Event?*/e){
21394 // summary:
21395 // Callback when user submits the form.
21396 // description:
21397 // This method is intended to be over-ridden, but by default it checks and
21398 // returns the validity of form elements. When the `submit`
21399 // method is called programmatically, the return value from
21400 // `onSubmit` is used to compute whether or not submission
21401 // should proceed
21402 // tags:
21403 // extension
21404
21405 return this.isValid(); // Boolean
21406 },
21407
21408 submit: function(){
21409 // summary:
21410 // programmatically submit form if and only if the `onSubmit` returns true
21411 if(!(this.onSubmit() === false)){
21412 this.containerNode.submit();
21413 }
21414 }
21415 }
21416);
21417
21418}
21419
21420if(!dojo._hasResource["dijit.form.DropDownButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21421dojo._hasResource["dijit.form.DropDownButton"] = true;
21422dojo.provide("dijit.form.DropDownButton");
21423
21424
21425
21426}
21427
21428if(!dojo._hasResource["dijit.form.ComboButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21429dojo._hasResource["dijit.form.ComboButton"] = true;
21430dojo.provide("dijit.form.ComboButton");
21431
21432
21433}
21434
21435if(!dojo._hasResource["dijit.form.ToggleButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21436dojo._hasResource["dijit.form.ToggleButton"] = true;
21437dojo.provide("dijit.form.ToggleButton");
21438
21439
21440}
21441
21442if(!dojo._hasResource["dijit.form.CheckBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21443dojo._hasResource["dijit.form.CheckBox"] = true;
21444dojo.provide("dijit.form.CheckBox");
21445
21446
21447
21448dojo.declare(
21449 "dijit.form.CheckBox",
21450 dijit.form.ToggleButton,
21451 {
21452 // summary:
21453 // Same as an HTML checkbox, but with fancy styling.
21454 //
21455 // description:
21456 // User interacts with real html inputs.
21457 // On onclick (which occurs by mouse click, space-bar, or
21458 // using the arrow keys to switch the selected radio button),
21459 // we update the state of the checkbox/radio.
21460 //
21461 // There are two modes:
21462 // 1. High contrast mode
21463 // 2. Normal mode
21464 //
21465 // In case 1, the regular html inputs are shown and used by the user.
21466 // In case 2, the regular html inputs are invisible but still used by
21467 // the user. They are turned quasi-invisible and overlay the background-image.
21468
21469 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"),
21470
21471 baseClass: "dijitCheckBox",
21472
21473 // type: [private] String
21474 // type attribute on <input> node.
21475 // Overrides `dijit.form.Button.type`. Users should not change this value.
21476 type: "checkbox",
21477
21478 // value: String
21479 // As an initialization parameter, equivalent to value field on normal checkbox
21480 // (if checked, the value is passed as the value when form is submitted).
21481 //
21482 // However, attr('value') will return either the string or false depending on
21483 // whether or not the checkbox is checked.
21484 //
21485 // attr('value', string) will check the checkbox and change the value to the
21486 // specified string
21487 //
21488 // attr('value', boolean) will change the checked state.
21489 value: "on",
21490
21491 // readOnly: Boolean
21492 // Should this widget respond to user input?
21493 // In markup, this is specified as "readOnly".
21494 // Similar to disabled except readOnly form values are submitted.
21495 readOnly: false,
21496
21497 // the attributeMap should inherit from dijit.form._FormWidget.prototype.attributeMap
21498 // instead of ToggleButton as the icon mapping has no meaning for a CheckBox
21499 attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
21500 readOnly: "focusNode"
21501 }),
21502
21503 _setReadOnlyAttr: function(/*Boolean*/ value){
21504 this.readOnly = value;
21505 dojo.attr(this.focusNode, 'readOnly', value);
21506 dijit.setWaiState(this.focusNode, "readonly", value);
21507 },
21508
21509 _setValueAttr: function(/*String or Boolean*/ newValue, /*Boolean*/ priorityChange){
21510 // summary:
21511 // Handler for value= attribute to constructor, and also calls to
21512 // attr('value', val).
21513 // description:
21514 // During initialization, just saves as attribute to the <input type=checkbox>.
21515 //
21516 // After initialization,
21517 // when passed a boolean, controls whether or not the CheckBox is checked.
21518 // If passed a string, changes the value attribute of the CheckBox (the one
21519 // specified as "value" when the CheckBox was constructed (ex: <input
21520 // dojoType="dijit.CheckBox" value="chicken">)
21521 if(typeof newValue == "string"){
21522 this.value = newValue;
21523 dojo.attr(this.focusNode, 'value', newValue);
21524 newValue = true;
21525 }
21526 if(this._created){
21527 this.set('checked', newValue, priorityChange);
21528 }
21529 },
21530 _getValueAttr: function(){
21531 // summary:
21532 // Hook so attr('value') works.
21533 // description:
21534 // If the CheckBox is checked, returns the value attribute.
21535 // Otherwise returns false.
21536 return (this.checked ? this.value : false);
21537 },
21538
21539 // Override dijit.form.Button._setLabelAttr() since we don't even have a containerNode.
21540 // Normally users won't try to set label, except when CheckBox or RadioButton is the child of a dojox.layout.TabContainer
21541 _setLabelAttr: undefined,
21542
21543 postMixInProperties: function(){
21544 if(this.value == ""){
21545 this.value = "on";
21546 }
21547
21548 // Need to set initial checked state as part of template, so that form submit works.
21549 // dojo.attr(node, "checked", bool) doesn't work on IEuntil node has been attached
21550 // to <body>, see #8666
21551 this.checkedAttrSetting = this.checked ? "checked" : "";
21552
21553 this.inherited(arguments);
21554 },
21555
21556 _fillContent: function(/*DomNode*/ source){
21557 // Override Button::_fillContent() since it doesn't make sense for CheckBox,
21558 // since CheckBox doesn't even have a container
21559 },
21560
21561 reset: function(){
21562 // Override ToggleButton.reset()
21563
21564 this._hasBeenBlurred = false;
21565
21566 this.set('checked', this.params.checked || false);
21567
21568 // Handle unlikely event that the <input type=checkbox> value attribute has changed
21569 this.value = this.params.value || "on";
21570 dojo.attr(this.focusNode, 'value', this.value);
21571 },
21572
21573 _onFocus: function(){
21574 if(this.id){
21575 dojo.query("label[for='"+this.id+"']").addClass("dijitFocusedLabel");
21576 }
21577 this.inherited(arguments);
21578 },
21579
21580 _onBlur: function(){
21581 if(this.id){
21582 dojo.query("label[for='"+this.id+"']").removeClass("dijitFocusedLabel");
21583 }
21584 this.inherited(arguments);
21585 },
21586
21587 _onClick: function(/*Event*/ e){
21588 // summary:
21589 // Internal function to handle click actions - need to check
21590 // readOnly, since button no longer does that check.
21591 if(this.readOnly){
21592 return false;
21593 }
21594 return this.inherited(arguments);
21595 }
21596 }
21597);
21598
21599dojo.declare(
21600 "dijit.form.RadioButton",
21601 dijit.form.CheckBox,
21602 {
21603 // summary:
21604 // Same as an HTML radio, but with fancy styling.
21605
21606 type: "radio",
21607 baseClass: "dijitRadio",
21608
21609 _setCheckedAttr: function(/*Boolean*/ value){
21610 // If I am being checked then have to deselect currently checked radio button
21611 this.inherited(arguments);
21612 if(!this._created){ return; }
21613 if(value){
21614 var _this = this;
21615 // search for radio buttons with the same name that need to be unchecked
21616 dojo.query("INPUT[type=radio]", this.focusNode.form || dojo.doc).forEach( // can't use name= since dojo.query doesn't support [] in the name
21617 function(inputNode){
21618 if(inputNode.name == _this.name && inputNode != _this.focusNode && inputNode.form == _this.focusNode.form){
21619 var widget = dijit.getEnclosingWidget(inputNode);
21620 if(widget && widget.checked){
21621 widget.set('checked', false);
21622 }
21623 }
21624 }
21625 );
21626 }
21627 },
21628
21629 _clicked: function(/*Event*/ e){
21630 if(!this.checked){
21631 this.set('checked', true);
21632 }
21633 }
21634 }
21635);
21636
21637}
21638
21639if(!dojo._hasResource["dijit.form.RadioButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21640dojo._hasResource["dijit.form.RadioButton"] = true;
21641dojo.provide("dijit.form.RadioButton");
21642
21643
21644// TODO: for 2.0, move the RadioButton code into this file
21645
21646}
21647
21648if(!dojo._hasResource["dojo.cldr.monetary"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21649dojo._hasResource["dojo.cldr.monetary"] = true;
21650dojo.provide("dojo.cldr.monetary");
21651
21652dojo.cldr.monetary.getData = function(/*String*/code){
21653// summary: A mapping of currency code to currency-specific formatting information. Returns a unique object with properties: places, round.
21654// code: an [ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code
21655
21656// from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/currencyData/fractions
21657
21658 var placesData = {
21659 ADP:0,AFN:0,ALL:0,AMD:0,BHD:3,BIF:0,BYR:0,CLF:0,CLP:0,
21660 COP:0,CRC:0,DJF:0,ESP:0,GNF:0,GYD:0,HUF:0,IDR:0,IQD:0,
21661 IRR:3,ISK:0,ITL:0,JOD:3,JPY:0,KMF:0,KPW:0,KRW:0,KWD:3,
21662 LAK:0,LBP:0,LUF:0,LYD:3,MGA:0,MGF:0,MMK:0,MNT:0,MRO:0,
21663 MUR:0,OMR:3,PKR:0,PYG:0,RSD:0,RWF:0,SLL:0,SOS:0,STD:0,
21664 SYP:0,TMM:0,TND:3,TRL:0,TZS:0,UGX:0,UZS:0,VND:0,VUV:0,
21665 XAF:0,XOF:0,XPF:0,YER:0,ZMK:0,ZWD:0
21666 };
21667
21668 var roundingData = {CHF:5};
21669
21670 var places = placesData[code], round = roundingData[code];
21671 if(typeof places == "undefined"){ places = 2; }
21672 if(typeof round == "undefined"){ round = 0; }
21673
21674 return {places: places, round: round}; // Object
21675};
21676
21677}
21678
21679if(!dojo._hasResource["dojo.currency"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21680dojo._hasResource["dojo.currency"] = true;
21681dojo.provide("dojo.currency");
21682
21683
21684
21685
21686
21687
21688/*=====
21689dojo.currency = {
21690 // summary: localized formatting and parsing routines for currencies
21691 //
21692 // description: extends dojo.number to provide culturally-appropriate formatting of values
21693 // in various world currencies, including use of a currency symbol. The currencies are specified
21694 // by a three-letter international symbol in all uppercase, and support for the currencies is
21695 // provided by the data in `dojo.cldr`. The scripts generating dojo.cldr specify which
21696 // currency support is included. A fixed number of decimal places is determined based
21697 // on the currency type and is not determined by the 'pattern' argument. The fractional
21698 // portion is optional, by default, and variable length decimals are not supported.
21699}
21700=====*/
21701
21702dojo.currency._mixInDefaults = function(options){
21703 options = options || {};
21704 options.type = "currency";
21705
21706 // Get locale-dependent currency data, like the symbol
21707 var bundle = dojo.i18n.getLocalization("dojo.cldr", "currency", options.locale) || {};
21708
21709 // Mixin locale-independent currency data, like # of places
21710 var iso = options.currency;
21711 var data = dojo.cldr.monetary.getData(iso);
21712
21713 dojo.forEach(["displayName","symbol","group","decimal"], function(prop){
21714 data[prop] = bundle[iso+"_"+prop];
21715 });
21716
21717 data.fractional = [true, false];
21718
21719 // Mixin with provided options
21720 return dojo.mixin(data, options);
21721}
21722
21723/*=====
21724dojo.declare("dojo.currency.__FormatOptions", [dojo.number.__FormatOptions], {
21725 // type: String?
21726 // Should not be set. Value is assumed to be "currency".
21727 // symbol: String?
21728 // localized currency symbol. The default will be looked up in table of supported currencies in `dojo.cldr`
21729 // A [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code will be used if not found.
21730 // currency: String?
21731 // an [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code, a three letter sequence like "USD".
21732 // For use with dojo.currency only.
21733 // places: Number?
21734 // number of decimal places to show. Default is defined based on which currency is used.
21735 type: "",
21736 symbol: "",
21737 currency: "",
21738 places: ""
21739});
21740=====*/
21741
21742dojo.currency.format = function(/*Number*/value, /*dojo.currency.__FormatOptions?*/options){
21743// summary:
21744// Format a Number as a currency, using locale-specific settings
21745//
21746// description:
21747// Create a string from a Number using a known, localized pattern.
21748// [Formatting patterns](http://www.unicode.org/reports/tr35/#Number_Elements)
21749// appropriate to the locale are chosen from the [CLDR](http://unicode.org/cldr)
21750// as well as the appropriate symbols and delimiters and number of decimal places.
21751//
21752// value:
21753// the number to be formatted.
21754
21755 return dojo.number.format(value, dojo.currency._mixInDefaults(options));
21756}
21757
21758dojo.currency.regexp = function(/*dojo.number.__RegexpOptions?*/options){
21759//
21760// summary:
21761// Builds the regular needed to parse a currency value
21762//
21763// description:
21764// Returns regular expression with positive and negative match, group and decimal separators
21765// Note: the options.places default, the number of decimal places to accept, is defined by the currency type.
21766 return dojo.number.regexp(dojo.currency._mixInDefaults(options)); // String
21767}
21768
21769/*=====
21770dojo.declare("dojo.currency.__ParseOptions", [dojo.number.__ParseOptions], {
21771 // type: String?
21772 // Should not be set. Value is assumed to be currency.
21773 // currency: String?
21774 // an [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code, a three letter sequence like "USD".
21775 // For use with dojo.currency only.
21776 // symbol: String?
21777 // localized currency symbol. The default will be looked up in table of supported currencies in `dojo.cldr`
21778 // A [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code will be used if not found.
21779 // places: Number?
21780 // fixed number of decimal places to accept. The default is determined based on which currency is used.
21781 // fractional: Boolean?|Array?
21782 // Whether to include the fractional portion, where the number of decimal places are implied by the currency
21783 // or explicit 'places' parameter. The value [true,false] makes the fractional portion optional.
21784 // By default for currencies, it the fractional portion is optional.
21785 type: "",
21786 currency: "",
21787 symbol: "",
21788 places: "",
21789 fractional: ""
21790});
21791=====*/
21792
21793dojo.currency.parse = function(/*String*/expression, /*dojo.currency.__ParseOptions?*/options){
21794 //
21795 // summary:
21796 // Convert a properly formatted currency string to a primitive Number,
21797 // using locale-specific settings.
21798 //
21799 // description:
21800 // Create a Number from a string using a known, localized pattern.
21801 // [Formatting patterns](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
21802 // are chosen appropriate to the locale, as well as the appropriate symbols and delimiters
21803 // and number of decimal places.
21804 //
21805 // expression: A string representation of a currency value
21806
21807 return dojo.number.parse(expression, dojo.currency._mixInDefaults(options));
21808}
21809
21810}
21811
21812if(!dojo._hasResource["dijit.form.NumberTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21813dojo._hasResource["dijit.form.NumberTextBox"] = true;
21814dojo.provide("dijit.form.NumberTextBox");
21815
21816
21817
21818
21819/*=====
21820dojo.declare(
21821 "dijit.form.NumberTextBox.__Constraints",
21822 [dijit.form.RangeBoundTextBox.__Constraints, dojo.number.__FormatOptions, dojo.number.__ParseOptions], {
21823 // summary:
21824 // Specifies both the rules on valid/invalid values (minimum, maximum,
21825 // number of required decimal places), and also formatting options for
21826 // displaying the value when the field is not focused.
21827 // example:
21828 // Minimum/maximum:
21829 // To specify a field between 0 and 120:
21830 // | {min:0,max:120}
21831 // To specify a field that must be an integer:
21832 // | {fractional:false}
21833 // To specify a field where 0 to 3 decimal places are allowed on input,
21834 // but after the field is blurred the value is displayed with 3 decimal places:
21835 // | {places:'0,3'}
21836});
21837=====*/
21838
21839dojo.declare("dijit.form.NumberTextBoxMixin",
21840 null,
21841 {
21842 // summary:
21843 // A mixin for all number textboxes
21844 // tags:
21845 // protected
21846
21847 // Override ValidationTextBox.regExpGen().... we use a reg-ex generating function rather
21848 // than a straight regexp to deal with locale (plus formatting options too?)
21849 regExpGen: dojo.number.regexp,
21850
21851 /*=====
21852 // constraints: dijit.form.NumberTextBox.__Constraints
21853 // Despite the name, this parameter specifies both constraints on the input
21854 // (including minimum/maximum allowed values) as well as
21855 // formatting options like places (the number of digits to display after
21856 // the decimal point). See `dijit.form.NumberTextBox.__Constraints` for details.
21857 constraints: {},
21858 ======*/
21859
21860 // value: Number
21861 // The value of this NumberTextBox as a Javascript Number (i.e., not a String).
21862 // If the displayed value is blank, the value is NaN, and if the user types in
21863 // an gibberish value (like "hello world"), the value is undefined
21864 // (i.e. attr('value') returns undefined).
21865 //
21866 // Symmetrically, attr('value', NaN) will clear the displayed value,
21867 // whereas attr('value', undefined) will have no effect.
21868 value: NaN,
21869
21870 // editOptions: [protected] Object
21871 // Properties to mix into constraints when the value is being edited.
21872 // This is here because we edit the number in the format "12345", which is
21873 // different than the display value (ex: "12,345")
21874 editOptions: { pattern: '#.######' },
21875
21876 /*=====
21877 _formatter: function(value, options){
21878 // summary:
21879 // _formatter() is called by format(). It's the base routine for formatting a number,
21880 // as a string, for example converting 12345 into "12,345".
21881 // value: Number
21882 // The number to be converted into a string.
21883 // options: dojo.number.__FormatOptions?
21884 // Formatting options
21885 // tags:
21886 // protected extension
21887
21888 return "12345"; // String
21889 },
21890 =====*/
21891 _formatter: dojo.number.format,
21892
21893 _setConstraintsAttr: function(/* Object */ constraints){
21894 var places = typeof constraints.places == "number"? constraints.places : 0;
21895 if(places){ places++; } // decimal rounding errors take away another digit of precision
21896 if(typeof constraints.max != "number"){
21897 constraints.max = 9 * Math.pow(10, 15-places);
21898 }
21899 if(typeof constraints.min != "number"){
21900 constraints.min = -9 * Math.pow(10, 15-places);
21901 }
21902 this.inherited(arguments, [ constraints ]);
21903 if(this.focusNode && this.focusNode.value && !isNaN(this.value)){
21904 this.set('value', this.value);
21905 }
21906 },
21907
21908 _onFocus: function(){
21909 if(this.disabled){ return; }
21910 var val = this.get('value');
21911 if(typeof val == "number" && !isNaN(val)){
21912 var formattedValue = this.format(val, this.constraints);
21913 if(formattedValue !== undefined){
21914 this.textbox.value = formattedValue;
21915 }
21916 }
21917 this.inherited(arguments);
21918 },
21919
21920 format: function(/*Number*/ value, /*dojo.number.__FormatOptions*/ constraints){
21921 // summary:
21922 // Formats the value as a Number, according to constraints.
21923 // tags:
21924 // protected
21925
21926 var formattedValue = String(value);
21927 if(typeof value != "number"){ return formattedValue; }
21928 if(isNaN(value)){ return ""; }
21929 // check for exponential notation that dojo.number.format chokes on
21930 if(!("rangeCheck" in this && this.rangeCheck(value, constraints)) && constraints.exponent !== false && /\de[-+]?\d/i.test(formattedValue)){
21931 return formattedValue;
21932 }
21933 if(this.editOptions && this._focused){
21934 constraints = dojo.mixin({}, constraints, this.editOptions);
21935 }
21936 return this._formatter(value, constraints);
21937 },
21938
21939 /*=====
21940 parse: function(value, constraints){
21941 // summary:
21942 // Parses the string value as a Number, according to constraints.
21943 // value: String
21944 // String representing a number
21945 // constraints: dojo.number.__ParseOptions
21946 // Formatting options
21947 // tags:
21948 // protected
21949
21950 return 123.45; // Number
21951 },
21952 =====*/
21953 parse: dojo.number.parse,
21954
21955 _getDisplayedValueAttr: function(){
21956 var v = this.inherited(arguments);
21957 return isNaN(v) ? this.textbox.value : v;
21958 },
21959
21960 filter: function(/*Number*/ value){
21961 // summary:
21962 // This is called with both the display value (string), and the actual value (a number).
21963 // When called with the actual value it does corrections so that '' etc. are represented as NaN.
21964 // Otherwise it dispatches to the superclass's filter() method.
21965 //
21966 // See `dijit.form.TextBox.filter` for more details.
21967 return (value === null || value === '' || value === undefined) ? NaN : this.inherited(arguments); // attr('value', null||''||undefined) should fire onChange(NaN)
21968 },
21969
21970 serialize: function(/*Number*/ value, /*Object?*/options){
21971 // summary:
21972 // Convert value (a Number) into a canonical string (ie, how the number literal is written in javascript/java/C/etc.)
21973 // tags:
21974 // protected
21975 return (typeof value != "number" || isNaN(value)) ? '' : this.inherited(arguments);
21976 },
21977
21978 _setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange, /*String?*/formattedValue){
21979 // summary:
21980 // Hook so attr('value', ...) works.
21981 if(value !== undefined && formattedValue === undefined){
21982 formattedValue = String(value);
21983 if(typeof value == "number"){
21984 if(isNaN(value)){ formattedValue = '' }
21985 // check for exponential notation that dojo.number.format chokes on
21986 else if(("rangeCheck" in this && this.rangeCheck(value, this.constraints)) || this.constraints.exponent === false || !/\de[-+]?\d/i.test(formattedValue)){
21987 formattedValue = undefined; // lets format comnpute a real string value
21988 }
21989 }else if(!value){ // 0 processed in if branch above, ''|null|undefined flow thru here
21990 formattedValue = '';
21991 value = NaN;
21992 }else{ // non-numeric values
21993 value = undefined;
21994 }
21995 }
21996 this.inherited(arguments, [value, priorityChange, formattedValue]);
21997 },
21998
21999 _getValueAttr: function(){
22000 // summary:
22001 // Hook so attr('value') works.
22002 // Returns Number, NaN for '', or undefined for unparsable text
22003 var v = this.inherited(arguments); // returns Number for all values accepted by parse() or NaN for all other displayed values
22004
22005 // If the displayed value of the textbox is gibberish (ex: "hello world"), this.inherited() above
22006 // returns NaN; this if() branch converts the return value to undefined.
22007 // Returning undefined prevents user text from being overwritten when doing _setValueAttr(_getValueAttr()).
22008 // A blank displayed value is still returned as NaN.
22009 if(isNaN(v) && this.textbox.value !== ''){
22010 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?)
22011 var n = Number(this.textbox.value);
22012 return isNaN(n) ? undefined : n; // return exponential Number or undefined for random text (may not be possible to do with the above RegExp check)
22013 }else{
22014 return undefined; // gibberish
22015 }
22016 }else{
22017 return v; // Number or NaN for ''
22018 }
22019 },
22020
22021 isValid: function(/*Boolean*/ isFocused){
22022 // Overrides dijit.form.RangeBoundTextBox.isValid to check that the editing-mode value is valid since
22023 // it may not be formatted according to the regExp vaidation rules
22024 if(!this._focused || this._isEmpty(this.textbox.value)){
22025 return this.inherited(arguments);
22026 }else{
22027 var v = this.get('value');
22028 if(!isNaN(v) && this.rangeCheck(v, this.constraints)){
22029 if(this.constraints.exponent !== false && /\de[-+]?\d/i.test(this.textbox.value)){ // exponential, parse doesn't like it
22030 return true; // valid exponential number in range
22031 }else{
22032 return this.inherited(arguments);
22033 }
22034 }else{
22035 return false;
22036 }
22037 }
22038 }
22039 }
22040);
22041
22042dojo.declare("dijit.form.NumberTextBox",
22043 [dijit.form.RangeBoundTextBox,dijit.form.NumberTextBoxMixin],
22044 {
22045 // summary:
22046 // A TextBox for entering numbers, with formatting and range checking
22047 // description:
22048 // NumberTextBox is a textbox for entering and displaying numbers, supporting
22049 // the following main features:
22050 //
22051 // 1. Enforce minimum/maximum allowed values (as well as enforcing that the user types
22052 // a number rather than a random string)
22053 // 2. NLS support (altering roles of comma and dot as "thousands-separator" and "decimal-point"
22054 // depending on locale).
22055 // 3. Separate modes for editing the value and displaying it, specifically that
22056 // the thousands separator character (typically comma) disappears when editing
22057 // but reappears after the field is blurred.
22058 // 4. Formatting and constraints regarding the number of places (digits after the decimal point)
22059 // allowed on input, and number of places displayed when blurred (see `constraints` parameter).
22060 }
22061);
22062
22063}
22064
22065if(!dojo._hasResource["dijit.form.CurrencyTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
22066dojo._hasResource["dijit.form.CurrencyTextBox"] = true;
22067dojo.provide("dijit.form.CurrencyTextBox");
22068
22069
22070
22071
22072/*=====
22073dojo.declare(
22074 "dijit.form.CurrencyTextBox.__Constraints",
22075 [dijit.form.NumberTextBox.__Constraints, dojo.currency.__FormatOptions, dojo.currency.__ParseOptions], {
22076 // summary:
22077 // Specifies both the rules on valid/invalid values (minimum, maximum,
22078 // number of required decimal places), and also formatting options for
22079 // displaying the value when the field is not focused (currency symbol,
22080 // etc.)
22081 // description:
22082 // Follows the pattern of `dijit.form.NumberTextBox.constraints`.
22083 // In general developers won't need to set this parameter
22084 // example:
22085 // To ensure that the user types in the cents (for example, 1.00 instead of just 1):
22086 // | {fractional:true}
22087});
22088=====*/
22089
22090dojo.declare(
22091 "dijit.form.CurrencyTextBox",
22092 dijit.form.NumberTextBox,
22093 {
22094 // summary:
22095 // A validating currency textbox
22096 // description:
22097 // CurrencyTextBox is similar to `dijit.form.NumberTextBox` but has a few
22098 // extra features related to currency:
22099 //
22100 // 1. After specifying the currency type (american dollars, euros, etc.) it automatically
22101 // sets parse/format options such as how many decimal places to show.
22102 // 2. The currency mark (dollar sign, euro mark, etc.) is displayed when the field is blurred
22103 // but erased during editing, so that the user can just enter a plain number.
22104
22105 // currency: [const] String
22106 // the [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code, a three letter sequence like "USD"
22107 currency: "",
22108
22109 // constraints: dijit.form.CurrencyTextBox.__Constraints
22110 // Despite the name, this parameter specifies both constraints on the input
22111 // (including minimum/maximum allowed values) as well as
22112 // formatting options. See `dijit.form.CurrencyTextBox.__Constraints` for details.
22113 /*=====
22114 constraints: {},
22115 ======*/
22116
22117 baseClass: "dijitTextBox dijitCurrencyTextBox",
22118
22119 // Override regExpGen ValidationTextBox.regExpGen().... we use a reg-ex generating function rather
22120 // than a straight regexp to deal with locale (plus formatting options too?)
22121 regExpGen: function(constraints){
22122 // if focused, accept either currency data or NumberTextBox format
22123 return '(' + (this._focused? this.inherited(arguments, [ dojo.mixin({}, constraints, this.editOptions) ]) + '|' : '')
22124 + dojo.currency.regexp(constraints) + ')';
22125 },
22126
22127 // Override NumberTextBox._formatter to deal with currencies, ex: converts "123.45" to "$123.45"
22128 _formatter: dojo.currency.format,
22129
22130 parse: function(/* String */ value, /* Object */ constraints){
22131 // summary:
22132 // Parses string value as a Currency, according to the constraints object
22133 // tags:
22134 // protected extension
22135 var v = dojo.currency.parse(value, constraints);
22136 if(isNaN(v) && /\d+/.test(value)){ // currency parse failed, but it could be because they are using NumberTextBox format so try its parse
22137 return this.inherited(arguments, [ value, dojo.mixin({}, constraints, this.editOptions) ]);
22138 }
22139 return v;
22140 },
22141
22142 _setConstraintsAttr: function(/* Object */ constraints){
22143 if(!constraints.currency && this.currency){
22144 constraints.currency = this.currency;
22145 }
22146 this.inherited(arguments, [ dojo.currency._mixInDefaults(dojo.mixin(constraints, { exponent: false })) ]); // get places
22147 }
22148 }
22149);
22150
22151}
22152
22153if(!dojo._hasResource["dojo.cldr.supplemental"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
22154dojo._hasResource["dojo.cldr.supplemental"] = true;
22155dojo.provide("dojo.cldr.supplemental");
22156
22157
22158
22159dojo.cldr.supplemental.getFirstDayOfWeek = function(/*String?*/locale){
22160// summary: Returns a zero-based index for first day of the week
22161// description:
22162// Returns a zero-based index for first day of the week, as used by the local (Gregorian) calendar.
22163// e.g. Sunday (returns 0), or Monday (returns 1)
22164
22165 // from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/weekData/firstDay
22166 var firstDay = {/*default is 1=Monday*/
22167 mv:5,
22168 af:6,bh:6,dj:6,dz:6,eg:6,er:6,et:6,iq:6,ir:6,jo:6,ke:6,kw:6,
22169 ly:6,ma:6,om:6,qa:6,sa:6,sd:6,so:6,tn:6,ye:6,
22170 ar:0,as:0,az:0,bw:0,ca:0,cn:0,fo:0,ge:0,gl:0,gu:0,hk:0,ie:0,
22171 il:0,'in':0,is:0,jm:0,jp:0,kg:0,kr:0,la:0,mh:0,mn:0,mo:0,mp:0,
22172 mt:0,nz:0,ph:0,pk:0,sg:0,sy:0,th:0,tt:0,tw:0,um:0,us:0,uz:0,
22173 vi:0,zw:0
22174// variant. do not use? gb:0,
22175 };
22176
22177 var country = dojo.cldr.supplemental._region(locale);
22178 var dow = firstDay[country];
22179 return (dow === undefined) ? 1 : dow; /*Number*/
22180};
22181
22182dojo.cldr.supplemental._region = function(/*String?*/locale){
22183 locale = dojo.i18n.normalizeLocale(locale);
22184 var tags = locale.split('-');
22185 var region = tags[1];
22186 if(!region){
22187 // IE often gives language only (#2269)
22188 // Arbitrary mappings of language-only locales to a country:
22189 region = {de:"de", en:"us", es:"es", fi:"fi", fr:"fr", he:"il", hu:"hu", it:"it",
22190 ja:"jp", ko:"kr", nl:"nl", pt:"br", sv:"se", zh:"cn"}[tags[0]];
22191 }else if(region.length == 4){
22192 // The ISO 3166 country code is usually in the second position, unless a
22193 // 4-letter script is given. See http://www.ietf.org/rfc/rfc4646.txt
22194 region = tags[2];
22195 }
22196 return region;
22197}
22198
22199dojo.cldr.supplemental.getWeekend = function(/*String?*/locale){
22200// summary: Returns a hash containing the start and end days of the weekend
22201// description:
22202// Returns a hash containing the start and end days of the weekend according to local custom using locale,
22203// or by default in the user's locale.
22204// e.g. {start:6, end:0}
22205
22206 // from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/weekData/weekend{Start,End}
22207 var weekendStart = {/*default is 6=Saturday*/
22208 'in':0,
22209 af:4,dz:4,ir:4,om:4,sa:4,ye:4,
22210 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
22211 };
22212
22213 var weekendEnd = {/*default is 0=Sunday*/
22214 af:5,dz:5,ir:5,om:5,sa:5,ye:5,
22215 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
22216 };
22217
22218 var country = dojo.cldr.supplemental._region(locale);
22219 var start = weekendStart[country];
22220 var end = weekendEnd[country];
22221 if(start === undefined){start=6;}
22222 if(end === undefined){end=0;}
22223 return {start:start, end:end}; /*Object {start,end}*/
22224};
22225
22226}
22227
22228if(!dojo._hasResource["dojo.date"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
22229dojo._hasResource["dojo.date"] = true;
22230dojo.provide("dojo.date");
22231
22232/*=====
22233dojo.date = {
22234 // summary: Date manipulation utilities
22235}
22236=====*/
22237
22238dojo.date.getDaysInMonth = function(/*Date*/dateObject){
22239 // summary:
22240 // Returns the number of days in the month used by dateObject
22241 var month = dateObject.getMonth();
22242 var days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
22243 if(month == 1 && dojo.date.isLeapYear(dateObject)){ return 29; } // Number
22244 return days[month]; // Number
22245}
22246
22247dojo.date.isLeapYear = function(/*Date*/dateObject){
22248 // summary:
22249 // Determines if the year of the dateObject is a leap year
22250 // description:
22251 // Leap years are years with an additional day YYYY-02-29, where the
22252 // year number is a multiple of four with the following exception: If
22253 // a year is a multiple of 100, then it is only a leap year if it is
22254 // also a multiple of 400. For example, 1900 was not a leap year, but
22255 // 2000 is one.
22256
22257 var year = dateObject.getFullYear();
22258 return !(year%400) || (!(year%4) && !!(year%100)); // Boolean
22259}
22260
22261// FIXME: This is not localized
22262dojo.date.getTimezoneName = function(/*Date*/dateObject){
22263 // summary:
22264 // Get the user's time zone as provided by the browser
22265 // dateObject:
22266 // Needed because the timezone may vary with time (daylight savings)
22267 // description:
22268 // Try to get time zone info from toString or toLocaleString method of
22269 // the Date object -- UTC offset is not a time zone. See
22270 // http://www.twinsun.com/tz/tz-link.htm Note: results may be
22271 // inconsistent across browsers.
22272
22273 var str = dateObject.toString(); // Start looking in toString
22274 var tz = ''; // The result -- return empty string if nothing found
22275 var match;
22276
22277 // First look for something in parentheses -- fast lookup, no regex
22278 var pos = str.indexOf('(');
22279 if(pos > -1){
22280 tz = str.substring(++pos, str.indexOf(')'));
22281 }else{
22282 // If at first you don't succeed ...
22283 // If IE knows about the TZ, it appears before the year
22284 // Capital letters or slash before a 4-digit year
22285 // at the end of string
22286 var pat = /([A-Z\/]+) \d{4}$/;
22287 if((match = str.match(pat))){
22288 tz = match[1];
22289 }else{
22290 // Some browsers (e.g. Safari) glue the TZ on the end
22291 // of toLocaleString instead of putting it in toString
22292 str = dateObject.toLocaleString();
22293 // Capital letters or slash -- end of string,
22294 // after space
22295 pat = / ([A-Z\/]+)$/;
22296 if((match = str.match(pat))){
22297 tz = match[1];
22298 }
22299 }
22300 }
22301
22302 // Make sure it doesn't somehow end up return AM or PM
22303 return (tz == 'AM' || tz == 'PM') ? '' : tz; // String
22304}
22305
22306// Utility methods to do arithmetic calculations with Dates
22307
22308dojo.date.compare = function(/*Date*/date1, /*Date?*/date2, /*String?*/portion){
22309 // summary:
22310 // Compare two date objects by date, time, or both.
22311 // description:
22312 // Returns 0 if equal, positive if a > b, else negative.
22313 // date1:
22314 // Date object
22315 // date2:
22316 // Date object. If not specified, the current Date is used.
22317 // portion:
22318 // A string indicating the "date" or "time" portion of a Date object.
22319 // Compares both "date" and "time" by default. One of the following:
22320 // "date", "time", "datetime"
22321
22322 // Extra step required in copy for IE - see #3112
22323 date1 = new Date(+date1);
22324 date2 = new Date(+(date2 || new Date()));
22325
22326 if(portion == "date"){
22327 // Ignore times and compare dates.
22328 date1.setHours(0, 0, 0, 0);
22329 date2.setHours(0, 0, 0, 0);
22330 }else if(portion == "time"){
22331 // Ignore dates and compare times.
22332 date1.setFullYear(0, 0, 0);
22333 date2.setFullYear(0, 0, 0);
22334 }
22335
22336 if(date1 > date2){ return 1; } // int
22337 if(date1 < date2){ return -1; } // int
22338 return 0; // int
22339};
22340
22341dojo.date.add = function(/*Date*/date, /*String*/interval, /*int*/amount){
22342 // summary:
22343 // Add to a Date in intervals of different size, from milliseconds to years
22344 // date: Date
22345 // Date object to start with
22346 // interval:
22347 // A string representing the interval. One of the following:
22348 // "year", "month", "day", "hour", "minute", "second",
22349 // "millisecond", "quarter", "week", "weekday"
22350 // amount:
22351 // How much to add to the date.
22352
22353 var sum = new Date(+date); // convert to Number before copying to accomodate IE (#3112)
22354 var fixOvershoot = false;
22355 var property = "Date";
22356
22357 switch(interval){
22358 case "day":
22359 break;
22360 case "weekday":
22361 //i18n FIXME: assumes Saturday/Sunday weekend, but this is not always true. see dojo.cldr.supplemental
22362
22363 // Divide the increment time span into weekspans plus leftover days
22364 // e.g., 8 days is one 5-day weekspan / and two leftover days
22365 // Can't have zero leftover days, so numbers divisible by 5 get
22366 // a days value of 5, and the remaining days make up the number of weeks
22367 var days, weeks;
22368 var mod = amount % 5;
22369 if(!mod){
22370 days = (amount > 0) ? 5 : -5;
22371 weeks = (amount > 0) ? ((amount-5)/5) : ((amount+5)/5);
22372 }else{
22373 days = mod;
22374 weeks = parseInt(amount/5);
22375 }
22376 // Get weekday value for orig date param
22377 var strt = date.getDay();
22378 // Orig date is Sat / positive incrementer
22379 // Jump over Sun
22380 var adj = 0;
22381 if(strt == 6 && amount > 0){
22382 adj = 1;
22383 }else if(strt == 0 && amount < 0){
22384 // Orig date is Sun / negative incrementer
22385 // Jump back over Sat
22386 adj = -1;
22387 }
22388 // Get weekday val for the new date
22389 var trgt = strt + days;
22390 // New date is on Sat or Sun
22391 if(trgt == 0 || trgt == 6){
22392 adj = (amount > 0) ? 2 : -2;
22393 }
22394 // Increment by number of weeks plus leftover days plus
22395 // weekend adjustments
22396 amount = (7 * weeks) + days + adj;
22397 break;
22398 case "year":
22399 property = "FullYear";
22400 // Keep increment/decrement from 2/29 out of March
22401 fixOvershoot = true;
22402 break;
22403 case "week":
22404 amount *= 7;
22405 break;
22406 case "quarter":
22407 // Naive quarter is just three months
22408 amount *= 3;
22409 // fallthrough...
22410 case "month":
22411 // Reset to last day of month if you overshoot
22412 fixOvershoot = true;
22413 property = "Month";
22414 break;
22415// case "hour":
22416// case "minute":
22417// case "second":
22418// case "millisecond":
22419 default:
22420 property = "UTC"+interval.charAt(0).toUpperCase() + interval.substring(1) + "s";
22421 }
22422
22423 if(property){
22424 sum["set"+property](sum["get"+property]()+amount);
22425 }
22426
22427 if(fixOvershoot && (sum.getDate() < date.getDate())){
22428 sum.setDate(0);
22429 }
22430
22431 return sum; // Date
22432};
22433
22434dojo.date.difference = function(/*Date*/date1, /*Date?*/date2, /*String?*/interval){
22435 // summary:
22436 // Get the difference in a specific unit of time (e.g., number of
22437 // months, weeks, days, etc.) between two dates, rounded to the
22438 // nearest integer.
22439 // date1:
22440 // Date object
22441 // date2:
22442 // Date object. If not specified, the current Date is used.
22443 // interval:
22444 // A string representing the interval. One of the following:
22445 // "year", "month", "day", "hour", "minute", "second",
22446 // "millisecond", "quarter", "week", "weekday"
22447 // Defaults to "day".
22448
22449 date2 = date2 || new Date();
22450 interval = interval || "day";
22451 var yearDiff = date2.getFullYear() - date1.getFullYear();
22452 var delta = 1; // Integer return value
22453
22454 switch(interval){
22455 case "quarter":
22456 var m1 = date1.getMonth();
22457 var m2 = date2.getMonth();
22458 // Figure out which quarter the months are in
22459 var q1 = Math.floor(m1/3) + 1;
22460 var q2 = Math.floor(m2/3) + 1;
22461 // Add quarters for any year difference between the dates
22462 q2 += (yearDiff * 4);
22463 delta = q2 - q1;
22464 break;
22465 case "weekday":
22466 var days = Math.round(dojo.date.difference(date1, date2, "day"));
22467 var weeks = parseInt(dojo.date.difference(date1, date2, "week"));
22468 var mod = days % 7;
22469
22470 // Even number of weeks
22471 if(mod == 0){
22472 days = weeks*5;
22473 }else{
22474 // Weeks plus spare change (< 7 days)
22475 var adj = 0;
22476 var aDay = date1.getDay();
22477 var bDay = date2.getDay();
22478
22479 weeks = parseInt(days/7);
22480 mod = days % 7;
22481 // Mark the date advanced by the number of
22482 // round weeks (may be zero)
22483 var dtMark = new Date(date1);
22484 dtMark.setDate(dtMark.getDate()+(weeks*7));
22485 var dayMark = dtMark.getDay();
22486
22487 // Spare change days -- 6 or less
22488 if(days > 0){
22489 switch(true){
22490 // Range starts on Sat
22491 case aDay == 6:
22492 adj = -1;
22493 break;
22494 // Range starts on Sun
22495 case aDay == 0:
22496 adj = 0;
22497 break;
22498 // Range ends on Sat
22499 case bDay == 6:
22500 adj = -1;
22501 break;
22502 // Range ends on Sun
22503 case bDay == 0:
22504 adj = -2;
22505 break;
22506 // Range contains weekend
22507 case (dayMark + mod) > 5:
22508 adj = -2;
22509 }
22510 }else if(days < 0){
22511 switch(true){
22512 // Range starts on Sat
22513 case aDay == 6:
22514 adj = 0;
22515 break;
22516 // Range starts on Sun
22517 case aDay == 0:
22518 adj = 1;
22519 break;
22520 // Range ends on Sat
22521 case bDay == 6:
22522 adj = 2;
22523 break;
22524 // Range ends on Sun
22525 case bDay == 0:
22526 adj = 1;
22527 break;
22528 // Range contains weekend
22529 case (dayMark + mod) < 0:
22530 adj = 2;
22531 }
22532 }
22533 days += adj;
22534 days -= (weeks*2);
22535 }
22536 delta = days;
22537 break;
22538 case "year":
22539 delta = yearDiff;
22540 break;
22541 case "month":
22542 delta = (date2.getMonth() - date1.getMonth()) + (yearDiff * 12);
22543 break;
22544 case "week":
22545 // Truncate instead of rounding
22546 // Don't use Math.floor -- value may be negative
22547 delta = parseInt(dojo.date.difference(date1, date2, "day")/7);
22548 break;
22549 case "day":
22550 delta /= 24;
22551 // fallthrough
22552 case "hour":
22553 delta /= 60;
22554 // fallthrough
22555 case "minute":
22556 delta /= 60;
22557 // fallthrough
22558 case "second":
22559 delta /= 1000;
22560 // fallthrough
22561 case "millisecond":
22562 delta *= date2.getTime() - date1.getTime();
22563 }
22564
22565 // Round for fractional values and DST leaps
22566 return Math.round(delta); // Number (integer)
22567};
22568
22569}
22570
22571if(!dojo._hasResource["dojo.date.locale"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
22572dojo._hasResource["dojo.date.locale"] = true;
22573dojo.provide("dojo.date.locale");
22574
22575// Localization methods for Date. Honor local customs using locale-dependent dojo.cldr data.
22576
22577
22578
22579
22580
22581
22582
22583// Load the bundles containing localization information for
22584// names and formats
22585
22586
22587//NOTE: Everything in this module assumes Gregorian calendars.
22588// Other calendars will be implemented in separate modules.
22589
22590(function(){
22591 // Format a pattern without literals
22592 function formatPattern(dateObject, bundle, options, pattern){
22593 return pattern.replace(/([a-z])\1*/ig, function(match){
22594 var s, pad,
22595 c = match.charAt(0),
22596 l = match.length,
22597 widthList = ["abbr", "wide", "narrow"];
22598 switch(c){
22599 case 'G':
22600 s = bundle[(l < 4) ? "eraAbbr" : "eraNames"][dateObject.getFullYear() < 0 ? 0 : 1];
22601 break;
22602 case 'y':
22603 s = dateObject.getFullYear();
22604 switch(l){
22605 case 1:
22606 break;
22607 case 2:
22608 if(!options.fullYear){
22609 s = String(s); s = s.substr(s.length - 2);
22610 break;
22611 }
22612 // fallthrough
22613 default:
22614 pad = true;
22615 }
22616 break;
22617 case 'Q':
22618 case 'q':
22619 s = Math.ceil((dateObject.getMonth()+1)/3);
22620// switch(l){
22621// case 1: case 2:
22622 pad = true;
22623// break;
22624// case 3: case 4: // unimplemented
22625// }
22626 break;
22627 case 'M':
22628 var m = dateObject.getMonth();
22629 if(l<3){
22630 s = m+1; pad = true;
22631 }else{
22632 var propM = ["months", "format", widthList[l-3]].join("-");
22633 s = bundle[propM][m];
22634 }
22635 break;
22636 case 'w':
22637 var firstDay = 0;
22638 s = dojo.date.locale._getWeekOfYear(dateObject, firstDay); pad = true;
22639 break;
22640 case 'd':
22641 s = dateObject.getDate(); pad = true;
22642 break;
22643 case 'D':
22644 s = dojo.date.locale._getDayOfYear(dateObject); pad = true;
22645 break;
22646 case 'E':
22647 var d = dateObject.getDay();
22648 if(l<3){
22649 s = d+1; pad = true;
22650 }else{
22651 var propD = ["days", "format", widthList[l-3]].join("-");
22652 s = bundle[propD][d];
22653 }
22654 break;
22655 case 'a':
22656 var timePeriod = (dateObject.getHours() < 12) ? 'am' : 'pm';
22657 s = bundle['dayPeriods-format-wide-' + timePeriod];
22658 break;
22659 case 'h':
22660 case 'H':
22661 case 'K':
22662 case 'k':
22663 var h = dateObject.getHours();
22664 // strange choices in the date format make it impossible to write this succinctly
22665 switch (c){
22666 case 'h': // 1-12
22667 s = (h % 12) || 12;
22668 break;
22669 case 'H': // 0-23
22670 s = h;
22671 break;
22672 case 'K': // 0-11
22673 s = (h % 12);
22674 break;
22675 case 'k': // 1-24
22676 s = h || 24;
22677 break;
22678 }
22679 pad = true;
22680 break;
22681 case 'm':
22682 s = dateObject.getMinutes(); pad = true;
22683 break;
22684 case 's':
22685 s = dateObject.getSeconds(); pad = true;
22686 break;
22687 case 'S':
22688 s = Math.round(dateObject.getMilliseconds() * Math.pow(10, l-3)); pad = true;
22689 break;
22690 case 'v': // FIXME: don't know what this is. seems to be same as z?
22691 case 'z':
22692 // We only have one timezone to offer; the one from the browser
22693 s = dojo.date.locale._getZone(dateObject, true, options);
22694 if(s){break;}
22695 l=4;
22696 // fallthrough... use GMT if tz not available
22697 case 'Z':
22698 var offset = dojo.date.locale._getZone(dateObject, false, options);
22699 var tz = [
22700 (offset<=0 ? "+" : "-"),
22701 dojo.string.pad(Math.floor(Math.abs(offset)/60), 2),
22702 dojo.string.pad(Math.abs(offset)% 60, 2)
22703 ];
22704 if(l==4){
22705 tz.splice(0, 0, "GMT");
22706 tz.splice(3, 0, ":");
22707 }
22708 s = tz.join("");
22709 break;
22710// case 'Y': case 'u': case 'W': case 'F': case 'g': case 'A': case 'e':
22711// console.log(match+" modifier unimplemented");
22712 default:
22713 throw new Error("dojo.date.locale.format: invalid pattern char: "+pattern);
22714 }
22715 if(pad){ s = dojo.string.pad(s, l); }
22716 return s;
22717 });
22718 }
22719
22720/*=====
22721 dojo.date.locale.__FormatOptions = function(){
22722 // selector: String
22723 // choice of 'time','date' (default: date and time)
22724 // formatLength: String
22725 // choice of long, short, medium or full (plus any custom additions). Defaults to 'short'
22726 // datePattern:String
22727 // override pattern with this string
22728 // timePattern:String
22729 // override pattern with this string
22730 // am: String
22731 // override strings for am in times
22732 // pm: String
22733 // override strings for pm in times
22734 // locale: String
22735 // override the locale used to determine formatting rules
22736 // fullYear: Boolean
22737 // (format only) use 4 digit years whenever 2 digit years are called for
22738 // strict: Boolean
22739 // (parse only) strict parsing, off by default
22740 this.selector = selector;
22741 this.formatLength = formatLength;
22742 this.datePattern = datePattern;
22743 this.timePattern = timePattern;
22744 this.am = am;
22745 this.pm = pm;
22746 this.locale = locale;
22747 this.fullYear = fullYear;
22748 this.strict = strict;
22749 }
22750=====*/
22751
22752dojo.date.locale._getZone = function(/*Date*/dateObject, /*boolean*/getName, /*dojo.date.locale.__FormatOptions?*/options){
22753 // summary:
22754 // Returns the zone (or offset) for the given date and options. This
22755 // is broken out into a separate function so that it can be overridden
22756 // by timezone-aware code.
22757 //
22758 // dateObject:
22759 // the date and/or time being formatted.
22760 //
22761 // getName:
22762 // Whether to return the timezone string (if true), or the offset (if false)
22763 //
22764 // options:
22765 // The options being used for formatting
22766 if(getName){
22767 return dojo.date.getTimezoneName(dateObject);
22768 }else{
22769 return dateObject.getTimezoneOffset();
22770 }
22771};
22772
22773
22774dojo.date.locale.format = function(/*Date*/dateObject, /*dojo.date.locale.__FormatOptions?*/options){
22775 // summary:
22776 // Format a Date object as a String, using locale-specific settings.
22777 //
22778 // description:
22779 // Create a string from a Date object using a known localized pattern.
22780 // By default, this method formats both date and time from dateObject.
22781 // Formatting patterns are chosen appropriate to the locale. Different
22782 // formatting lengths may be chosen, with "full" used by default.
22783 // Custom patterns may be used or registered with translations using
22784 // the dojo.date.locale.addCustomFormats method.
22785 // Formatting patterns are implemented using [the syntax described at
22786 // unicode.org](http://www.unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns)
22787 //
22788 // dateObject:
22789 // the date and/or time to be formatted. If a time only is formatted,
22790 // the values in the year, month, and day fields are irrelevant. The
22791 // opposite is true when formatting only dates.
22792
22793 options = options || {};
22794
22795 var locale = dojo.i18n.normalizeLocale(options.locale),
22796 formatLength = options.formatLength || 'short',
22797 bundle = dojo.date.locale._getGregorianBundle(locale),
22798 str = [],
22799 sauce = dojo.hitch(this, formatPattern, dateObject, bundle, options);
22800 if(options.selector == "year"){
22801 return _processPattern(bundle["dateFormatItem-yyyy"] || "yyyy", sauce);
22802 }
22803 var pattern;
22804 if(options.selector != "date"){
22805 pattern = options.timePattern || bundle["timeFormat-"+formatLength];
22806 if(pattern){str.push(_processPattern(pattern, sauce));}
22807 }
22808 if(options.selector != "time"){
22809 pattern = options.datePattern || bundle["dateFormat-"+formatLength];
22810 if(pattern){str.push(_processPattern(pattern, sauce));}
22811 }
22812
22813 return str.length == 1 ? str[0] : bundle["dateTimeFormat-"+formatLength].replace(/\{(\d+)\}/g,
22814 function(match, key){ return str[key]; }); // String
22815};
22816
22817dojo.date.locale.regexp = function(/*dojo.date.locale.__FormatOptions?*/options){
22818 // summary:
22819 // Builds the regular needed to parse a localized date
22820
22821 return dojo.date.locale._parseInfo(options).regexp; // String
22822};
22823
22824dojo.date.locale._parseInfo = function(/*dojo.date.locale.__FormatOptions?*/options){
22825 options = options || {};
22826 var locale = dojo.i18n.normalizeLocale(options.locale),
22827 bundle = dojo.date.locale._getGregorianBundle(locale),
22828 formatLength = options.formatLength || 'short',
22829 datePattern = options.datePattern || bundle["dateFormat-" + formatLength],
22830 timePattern = options.timePattern || bundle["timeFormat-" + formatLength],
22831 pattern;
22832 if(options.selector == 'date'){
22833 pattern = datePattern;
22834 }else if(options.selector == 'time'){
22835 pattern = timePattern;
22836 }else{
22837 pattern = bundle["dateTimeFormat-"+formatLength].replace(/\{(\d+)\}/g,
22838 function(match, key){ return [timePattern, datePattern][key]; });
22839 }
22840
22841 var tokens = [],
22842 re = _processPattern(pattern, dojo.hitch(this, _buildDateTimeRE, tokens, bundle, options));
22843 return {regexp: re, tokens: tokens, bundle: bundle};
22844};
22845
22846dojo.date.locale.parse = function(/*String*/value, /*dojo.date.locale.__FormatOptions?*/options){
22847 // summary:
22848 // Convert a properly formatted string to a primitive Date object,
22849 // using locale-specific settings.
22850 //
22851 // description:
22852 // Create a Date object from a string using a known localized pattern.
22853 // By default, this method parses looking for both date and time in the string.
22854 // Formatting patterns are chosen appropriate to the locale. Different
22855 // formatting lengths may be chosen, with "full" used by default.
22856 // Custom patterns may be used or registered with translations using
22857 // the dojo.date.locale.addCustomFormats method.
22858 //
22859 // Formatting patterns are implemented using [the syntax described at
22860 // unicode.org](http://www.unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns)
22861 // When two digit years are used, a century is chosen according to a sliding
22862 // window of 80 years before and 20 years after present year, for both `yy` and `yyyy` patterns.
22863 // year < 100CE requires strict mode.
22864 //
22865 // value:
22866 // A string representation of a date
22867
22868 var info = dojo.date.locale._parseInfo(options),
22869 tokens = info.tokens, bundle = info.bundle,
22870 re = new RegExp("^" + info.regexp + "$", info.strict ? "" : "i"),
22871 match = re.exec(value);
22872
22873 if(!match){ return null; } // null
22874
22875 var widthList = ['abbr', 'wide', 'narrow'],
22876 result = [1970,0,1,0,0,0,0], // will get converted to a Date at the end
22877 amPm = "",
22878 valid = dojo.every(match, function(v, i){
22879 if(!i){return true;}
22880 var token=tokens[i-1];
22881 var l=token.length;
22882 switch(token.charAt(0)){
22883 case 'y':
22884 if(l != 2 && options.strict){
22885 //interpret year literally, so '5' would be 5 A.D.
22886 result[0] = v;
22887 }else{
22888 if(v<100){
22889 v = Number(v);
22890 //choose century to apply, according to a sliding window
22891 //of 80 years before and 20 years after present year
22892 var year = '' + new Date().getFullYear(),
22893 century = year.substring(0, 2) * 100,
22894 cutoff = Math.min(Number(year.substring(2, 4)) + 20, 99),
22895 num = (v < cutoff) ? century + v : century - 100 + v;
22896 result[0] = num;
22897 }else{
22898 //we expected 2 digits and got more...
22899 if(options.strict){
22900 return false;
22901 }
22902 //interpret literally, so '150' would be 150 A.D.
22903 //also tolerate '1950', if 'yyyy' input passed to 'yy' format
22904 result[0] = v;
22905 }
22906 }
22907 break;
22908 case 'M':
22909 if(l>2){
22910 var months = bundle['months-format-' + widthList[l-3]].concat();
22911 if(!options.strict){
22912 //Tolerate abbreviating period in month part
22913 //Case-insensitive comparison
22914 v = v.replace(".","").toLowerCase();
22915 months = dojo.map(months, function(s){ return s.replace(".","").toLowerCase(); } );
22916 }
22917 v = dojo.indexOf(months, v);
22918 if(v == -1){
22919// console.log("dojo.date.locale.parse: Could not parse month name: '" + v + "'.");
22920 return false;
22921 }
22922 }else{
22923 v--;
22924 }
22925 result[1] = v;
22926 break;
22927 case 'E':
22928 case 'e':
22929 var days = bundle['days-format-' + widthList[l-3]].concat();
22930 if(!options.strict){
22931 //Case-insensitive comparison
22932 v = v.toLowerCase();
22933 days = dojo.map(days, function(d){return d.toLowerCase();});
22934 }
22935 v = dojo.indexOf(days, v);
22936 if(v == -1){
22937// console.log("dojo.date.locale.parse: Could not parse weekday name: '" + v + "'.");
22938 return false;
22939 }
22940
22941 //TODO: not sure what to actually do with this input,
22942 //in terms of setting something on the Date obj...?
22943 //without more context, can't affect the actual date
22944 //TODO: just validate?
22945 break;
22946 case 'D':
22947 result[1] = 0;
22948 // fallthrough...
22949 case 'd':
22950 result[2] = v;
22951 break;
22952 case 'a': //am/pm
22953 var am = options.am || bundle['dayPeriods-format-wide-am'],
22954 pm = options.pm || bundle['dayPeriods-format-wide-pm'];
22955 if(!options.strict){
22956 var period = /\./g;
22957 v = v.replace(period,'').toLowerCase();
22958 am = am.replace(period,'').toLowerCase();
22959 pm = pm.replace(period,'').toLowerCase();
22960 }
22961 if(options.strict && v != am && v != pm){
22962// console.log("dojo.date.locale.parse: Could not parse am/pm part.");
22963 return false;
22964 }
22965
22966 // we might not have seen the hours field yet, so store the state and apply hour change later
22967 amPm = (v == pm) ? 'p' : (v == am) ? 'a' : '';
22968 break;
22969 case 'K': //hour (1-24)
22970 if(v == 24){ v = 0; }
22971 // fallthrough...
22972 case 'h': //hour (1-12)
22973 case 'H': //hour (0-23)
22974 case 'k': //hour (0-11)
22975 //TODO: strict bounds checking, padding
22976 if(v > 23){
22977// console.log("dojo.date.locale.parse: Illegal hours value");
22978 return false;
22979 }
22980
22981 //in the 12-hour case, adjusting for am/pm requires the 'a' part
22982 //which could come before or after the hour, so we will adjust later
22983 result[3] = v;
22984 break;
22985 case 'm': //minutes
22986 result[4] = v;
22987 break;
22988 case 's': //seconds
22989 result[5] = v;
22990 break;
22991 case 'S': //milliseconds
22992 result[6] = v;
22993// break;
22994// case 'w':
22995//TODO var firstDay = 0;
22996// default:
22997//TODO: throw?
22998// console.log("dojo.date.locale.parse: unsupported pattern char=" + token.charAt(0));
22999 }
23000 return true;
23001 });
23002
23003 var hours = +result[3];
23004 if(amPm === 'p' && hours < 12){
23005 result[3] = hours + 12; //e.g., 3pm -> 15
23006 }else if(amPm === 'a' && hours == 12){
23007 result[3] = 0; //12am -> 0
23008 }
23009
23010 //TODO: implement a getWeekday() method in order to test
23011 //validity of input strings containing 'EEE' or 'EEEE'...
23012
23013 var dateObject = new Date(result[0], result[1], result[2], result[3], result[4], result[5], result[6]); // Date
23014 if(options.strict){
23015 dateObject.setFullYear(result[0]);
23016 }
23017
23018 // Check for overflow. The Date() constructor normalizes things like April 32nd...
23019 //TODO: why isn't this done for times as well?
23020 var allTokens = tokens.join(""),
23021 dateToken = allTokens.indexOf('d') != -1,
23022 monthToken = allTokens.indexOf('M') != -1;
23023
23024 if(!valid ||
23025 (monthToken && dateObject.getMonth() > result[1]) ||
23026 (dateToken && dateObject.getDate() > result[2])){
23027 return null;
23028 }
23029
23030 // Check for underflow, due to DST shifts. See #9366
23031 // This assumes a 1 hour dst shift correction at midnight
23032 // We could compare the timezone offset after the shift and add the difference instead.
23033 if((monthToken && dateObject.getMonth() < result[1]) ||
23034 (dateToken && dateObject.getDate() < result[2])){
23035 dateObject = dojo.date.add(dateObject, "hour", 1);
23036 }
23037
23038 return dateObject; // Date
23039};
23040
23041function _processPattern(pattern, applyPattern, applyLiteral, applyAll){
23042 //summary: Process a pattern with literals in it
23043
23044 // Break up on single quotes, treat every other one as a literal, except '' which becomes '
23045 var identity = function(x){return x;};
23046 applyPattern = applyPattern || identity;
23047 applyLiteral = applyLiteral || identity;
23048 applyAll = applyAll || identity;
23049
23050 //split on single quotes (which escape literals in date format strings)
23051 //but preserve escaped single quotes (e.g., o''clock)
23052 var chunks = pattern.match(/(''|[^'])+/g),
23053 literal = pattern.charAt(0) == "'";
23054
23055 dojo.forEach(chunks, function(chunk, i){
23056 if(!chunk){
23057 chunks[i]='';
23058 }else{
23059 chunks[i]=(literal ? applyLiteral : applyPattern)(chunk.replace(/''/g, "'"));
23060 literal = !literal;
23061 }
23062 });
23063 return applyAll(chunks.join(''));
23064}
23065
23066function _buildDateTimeRE(tokens, bundle, options, pattern){
23067 pattern = dojo.regexp.escapeString(pattern);
23068 if(!options.strict){ pattern = pattern.replace(" a", " ?a"); } // kludge to tolerate no space before am/pm
23069 return pattern.replace(/([a-z])\1*/ig, function(match){
23070 // Build a simple regexp. Avoid captures, which would ruin the tokens list
23071 var s,
23072 c = match.charAt(0),
23073 l = match.length,
23074 p2 = '', p3 = '';
23075 if(options.strict){
23076 if(l > 1){ p2 = '0' + '{'+(l-1)+'}'; }
23077 if(l > 2){ p3 = '0' + '{'+(l-2)+'}'; }
23078 }else{
23079 p2 = '0?'; p3 = '0{0,2}';
23080 }
23081 switch(c){
23082 case 'y':
23083 s = '\\d{2,4}';
23084 break;
23085 case 'M':
23086 s = (l>2) ? '\\S+?' : p2+'[1-9]|1[0-2]';
23087 break;
23088 case 'D':
23089 s = p2+'[1-9]|'+p3+'[1-9][0-9]|[12][0-9][0-9]|3[0-5][0-9]|36[0-6]';
23090 break;
23091 case 'd':
23092 s = '3[01]|[12]\\d|'+p2+'[1-9]';
23093 break;
23094 case 'w':
23095 s = p2+'[1-9]|[1-4][0-9]|5[0-3]';
23096 break;
23097 case 'E':
23098 s = '\\S+';
23099 break;
23100 case 'h': //hour (1-12)
23101 s = p2+'[1-9]|1[0-2]';
23102 break;
23103 case 'k': //hour (0-11)
23104 s = p2+'\\d|1[01]';
23105 break;
23106 case 'H': //hour (0-23)
23107 s = p2+'\\d|1\\d|2[0-3]';
23108 break;
23109 case 'K': //hour (1-24)
23110 s = p2+'[1-9]|1\\d|2[0-4]';
23111 break;
23112 case 'm':
23113 case 's':
23114 s = '[0-5]\\d';
23115 break;
23116 case 'S':
23117 s = '\\d{'+l+'}';
23118 break;
23119 case 'a':
23120 var am = options.am || bundle['dayPeriods-format-wide-am'],
23121 pm = options.pm || bundle['dayPeriods-format-wide-pm'];
23122 if(options.strict){
23123 s = am + '|' + pm;
23124 }else{
23125 s = am + '|' + pm;
23126 if(am != am.toLowerCase()){ s += '|' + am.toLowerCase(); }
23127 if(pm != pm.toLowerCase()){ s += '|' + pm.toLowerCase(); }
23128 if(s.indexOf('.') != -1){ s += '|' + s.replace(/\./g, ""); }
23129 }
23130 s = s.replace(/\./g, "\\.");
23131 break;
23132 default:
23133 // case 'v':
23134 // case 'z':
23135 // case 'Z':
23136 s = ".*";
23137// console.log("parse of date format, pattern=" + pattern);
23138 }
23139
23140 if(tokens){ tokens.push(match); }
23141
23142 return "(" + s + ")"; // add capture
23143 }).replace(/[\xa0 ]/g, "[\\s\\xa0]"); // normalize whitespace. Need explicit handling of \xa0 for IE.
23144}
23145})();
23146
23147(function(){
23148var _customFormats = [];
23149dojo.date.locale.addCustomFormats = function(/*String*/packageName, /*String*/bundleName){
23150 // summary:
23151 // Add a reference to a bundle containing localized custom formats to be
23152 // used by date/time formatting and parsing routines.
23153 //
23154 // description:
23155 // The user may add custom localized formats where the bundle has properties following the
23156 // same naming convention used by dojo.cldr: `dateFormat-xxxx` / `timeFormat-xxxx`
23157 // The pattern string should match the format used by the CLDR.
23158 // See dojo.date.locale.format() for details.
23159 // The resources must be loaded by dojo.requireLocalization() prior to use
23160
23161 _customFormats.push({pkg:packageName,name:bundleName});
23162};
23163
23164dojo.date.locale._getGregorianBundle = function(/*String*/locale){
23165 var gregorian = {};
23166 dojo.forEach(_customFormats, function(desc){
23167 var bundle = dojo.i18n.getLocalization(desc.pkg, desc.name, locale);
23168 gregorian = dojo.mixin(gregorian, bundle);
23169 }, this);
23170 return gregorian; /*Object*/
23171};
23172})();
23173
23174dojo.date.locale.addCustomFormats("dojo.cldr","gregorian");
23175
23176dojo.date.locale.getNames = function(/*String*/item, /*String*/type, /*String?*/context, /*String?*/locale){
23177 // summary:
23178 // Used to get localized strings from dojo.cldr for day or month names.
23179 //
23180 // item:
23181 // 'months' || 'days'
23182 // type:
23183 // 'wide' || 'narrow' || 'abbr' (e.g. "Monday", "Mon", or "M" respectively, in English)
23184 // context:
23185 // 'standAlone' || 'format' (default)
23186 // locale:
23187 // override locale used to find the names
23188
23189 var label,
23190 lookup = dojo.date.locale._getGregorianBundle(locale),
23191 props = [item, context, type];
23192 if(context == 'standAlone'){
23193 var key = props.join('-');
23194 label = lookup[key];
23195 // Fall back to 'format' flavor of name
23196 if(label[0] == 1){ label = undefined; } // kludge, in the absence of real aliasing support in dojo.cldr
23197 }
23198 props[1] = 'format';
23199
23200 // return by copy so changes won't be made accidentally to the in-memory model
23201 return (label || lookup[props.join('-')]).concat(); /*Array*/
23202};
23203
23204dojo.date.locale.isWeekend = function(/*Date?*/dateObject, /*String?*/locale){
23205 // summary:
23206 // Determines if the date falls on a weekend, according to local custom.
23207
23208 var weekend = dojo.cldr.supplemental.getWeekend(locale),
23209 day = (dateObject || new Date()).getDay();
23210 if(weekend.end < weekend.start){
23211 weekend.end += 7;
23212 if(day < weekend.start){ day += 7; }
23213 }
23214 return day >= weekend.start && day <= weekend.end; // Boolean
23215};
23216
23217// These are used only by format and strftime. Do they need to be public? Which module should they go in?
23218
23219dojo.date.locale._getDayOfYear = function(/*Date*/dateObject){
23220 // summary: gets the day of the year as represented by dateObject
23221 return dojo.date.difference(new Date(dateObject.getFullYear(), 0, 1, dateObject.getHours()), dateObject) + 1; // Number
23222};
23223
23224dojo.date.locale._getWeekOfYear = function(/*Date*/dateObject, /*Number*/firstDayOfWeek){
23225 if(arguments.length == 1){ firstDayOfWeek = 0; } // Sunday
23226
23227 var firstDayOfYear = new Date(dateObject.getFullYear(), 0, 1).getDay(),
23228 adj = (firstDayOfYear - firstDayOfWeek + 7) % 7,
23229 week = Math.floor((dojo.date.locale._getDayOfYear(dateObject) + adj - 1) / 7);
23230
23231 // if year starts on the specified day, start counting weeks at 1
23232 if(firstDayOfYear == firstDayOfWeek){ week++; }
23233
23234 return week; // Number
23235};
23236
23237}
23238
23239if(!dojo._hasResource["dijit.Calendar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
23240dojo._hasResource["dijit.Calendar"] = true;
23241dojo.provide("dijit.Calendar");
23242
23243
23244
23245
23246
23247
23248
23249
23250
23251dojo.declare(
23252 "dijit.Calendar",
23253 [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
23254 {
23255 // summary:
23256 // A simple GUI for choosing a date in the context of a monthly calendar.
23257 //
23258 // description:
23259 // A simple GUI for choosing a date in the context of a monthly calendar.
23260 // This widget can't be used in a form because it doesn't serialize the date to an
23261 // `<input>` field. For a form element, use dijit.form.DateTextBox instead.
23262 //
23263 // Note that the parser takes all dates attributes passed in the
23264 // [RFC 3339 format](http://www.faqs.org/rfcs/rfc3339.html), e.g. `2005-06-30T08:05:00-07:00`
23265 // so that they are serializable and locale-independent.
23266 //
23267 // example:
23268 // | var calendar = new dijit.Calendar({}, dojo.byId("calendarNode"));
23269 //
23270 // example:
23271 // | <div dojoType="dijit.Calendar"></div>
23272
23273 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"),
23274
23275 // value: Date
23276 // The currently selected Date
23277 value: new Date(),
23278
23279 // datePackage: String
23280 // JavaScript namespace to find Calendar routines. Uses Gregorian Calendar routines
23281 // at dojo.date by default.
23282 datePackage: "dojo.date",
23283
23284 // dayWidth: String
23285 // How to represent the days of the week in the calendar header. See dojo.date.locale
23286 dayWidth: "narrow",
23287
23288 // tabIndex: Integer
23289 // Order fields are traversed when user hits the tab key
23290 tabIndex: "0",
23291
23292 baseClass:"dijitCalendar",
23293
23294 // Set node classes for various mouse events, see dijit._CssStateMixin for more details
23295 cssStateNodes: {
23296 "decrementMonth": "dijitCalendarArrow",
23297 "incrementMonth": "dijitCalendarArrow",
23298 "previousYearLabelNode": "dijitCalendarPreviousYear",
23299 "nextYearLabelNode": "dijitCalendarNextYear"
23300 },
23301
23302 attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
23303 tabIndex: "domNode"
23304 }),
23305
23306 setValue: function(/*Date*/ value){
23307 // summary:
23308 // Deprecated. Used attr('value', ...) instead.
23309 // tags:
23310 // deprecated
23311 dojo.deprecated("dijit.Calendar:setValue() is deprecated. Use set('value', ...) instead.", "", "2.0");
23312 this.set('value', value);
23313 },
23314
23315 _getValueAttr: function(){
23316 // summary:
23317 // Support getter attr('value')
23318 var value = new this.dateClassObj(this.value);
23319 value.setHours(0, 0, 0, 0); // return midnight, local time for back-compat
23320
23321 // If daylight savings pushes midnight to the previous date, fix the Date
23322 // object to point at 1am so it will represent the correct day. See #9366
23323 if(value.getDate() < this.value.getDate()){
23324 value = this.dateFuncObj.add(value, "hour", 1);
23325 }
23326 return value;
23327 },
23328
23329 _setValueAttr: function(/*Date*/ value){
23330 // summary:
23331 // Support setter attr("value", ...)
23332 // description:
23333 // Set the current date and update the UI. If the date is disabled, the value will
23334 // not change, but the display will change to the corresponding month.
23335 // tags:
23336 // protected
23337 if(!this.value || this.dateFuncObj.compare(value, this.value)){
23338 value = new this.dateClassObj(value);
23339 value.setHours(1); // to avoid issues when DST shift occurs at midnight, see #8521, #9366
23340 this.displayMonth = new this.dateClassObj(value);
23341 if(!this.isDisabledDate(value, this.lang)){
23342 this.value = value;
23343 this.onChange(this.get('value'));
23344 }
23345 dojo.attr(this.domNode, "aria-label",
23346 this.dateLocaleModule.format(value,
23347 {selector:"date", formatLength:"full"}));
23348 this._populateGrid();
23349 }
23350 },
23351
23352 _setText: function(node, text){
23353 // summary:
23354 // This just sets the content of node to the specified text.
23355 // Can't do "node.innerHTML=text" because of an IE bug w/tables, see #3434.
23356 // tags:
23357 // private
23358 while(node.firstChild){
23359 node.removeChild(node.firstChild);
23360 }
23361 node.appendChild(dojo.doc.createTextNode(text));
23362 },
23363
23364 _populateGrid: function(){
23365 // summary:
23366 // Fills in the calendar grid with each day (1-31)
23367 // tags:
23368 // private
23369 var month = this.displayMonth;
23370 month.setDate(1);
23371 var firstDay = month.getDay(),
23372 daysInMonth = this.dateFuncObj.getDaysInMonth(month),
23373 daysInPreviousMonth = this.dateFuncObj.getDaysInMonth(this.dateFuncObj.add(month, "month", -1)),
23374 today = new this.dateClassObj(),
23375 dayOffset = dojo.cldr.supplemental.getFirstDayOfWeek(this.lang);
23376 if(dayOffset > firstDay){ dayOffset -= 7; }
23377
23378 // Iterate through dates in the calendar and fill in date numbers and style info
23379 dojo.query(".dijitCalendarDateTemplate", this.domNode).forEach(function(template, i){
23380 i += dayOffset;
23381 var date = new this.dateClassObj(month),
23382 number, clazz = "dijitCalendar", adj = 0;
23383
23384 if(i < firstDay){
23385 number = daysInPreviousMonth - firstDay + i + 1;
23386 adj = -1;
23387 clazz += "Previous";
23388 }else if(i >= (firstDay + daysInMonth)){
23389 number = i - firstDay - daysInMonth + 1;
23390 adj = 1;
23391 clazz += "Next";
23392 }else{
23393 number = i - firstDay + 1;
23394 clazz += "Current";
23395 }
23396
23397 if(adj){
23398 date = this.dateFuncObj.add(date, "month", adj);
23399 }
23400 date.setDate(number);
23401
23402 if(!this.dateFuncObj.compare(date, today, "date")){
23403 clazz = "dijitCalendarCurrentDate " + clazz;
23404 }
23405
23406 if(this._isSelectedDate(date, this.lang)){
23407 clazz = "dijitCalendarSelectedDate " + clazz;
23408 }
23409
23410 if(this.isDisabledDate(date, this.lang)){
23411 clazz = "dijitCalendarDisabledDate " + clazz;
23412 }
23413
23414 var clazz2 = this.getClassForDate(date, this.lang);
23415 if(clazz2){
23416 clazz = clazz2 + " " + clazz;
23417 }
23418
23419 template.className = clazz + "Month dijitCalendarDateTemplate";
23420 template.dijitDateValue = date.valueOf();
23421 var label = dojo.query(".dijitCalendarDateLabel", template)[0],
23422 text = date.getDateLocalized ? date.getDateLocalized(this.lang) : date.getDate();
23423 this._setText(label, text);
23424 }, this);
23425
23426 // Fill in localized month name
23427 var monthNames = this.dateLocaleModule.getNames('months', 'wide', 'standAlone', this.lang, month);
23428 this._setText(this.monthLabelNode, monthNames[month.getMonth()]);
23429 // Repopulate month list based on current year (Hebrew calendar)
23430 dojo.query(".dijitCalendarMonthLabelTemplate", this.domNode).forEach(function(node, i){
23431 dojo.toggleClass(node, "dijitHidden", !(i in monthNames)); // hide leap months (Hebrew)
23432 this._setText(node, monthNames[i]);
23433 }, this);
23434
23435 // Fill in localized prev/current/next years
23436 var y = month.getFullYear() - 1;
23437 var d = new this.dateClassObj();
23438 dojo.forEach(["previous", "current", "next"], function(name){
23439 d.setFullYear(y++);
23440 this._setText(this[name+"YearLabelNode"],
23441 this.dateLocaleModule.format(d, {selector:'year', locale:this.lang}));
23442 }, this);
23443
23444 // Set up repeating mouse behavior
23445 var _this = this;
23446 var typematic = function(nodeProp, dateProp, adj){
23447//FIXME: leaks (collects) listeners if populateGrid is called multiple times. Do this once?
23448 _this._connects.push(
23449 dijit.typematic.addMouseListener(_this[nodeProp], _this, function(count){
23450 if(count >= 0){ _this._adjustDisplay(dateProp, adj); }
23451 }, 0.8, 500)
23452 );
23453 };
23454 typematic("incrementMonth", "month", 1);
23455 typematic("decrementMonth", "month", -1);
23456 typematic("nextYearLabelNode", "year", 1);
23457 typematic("previousYearLabelNode", "year", -1);
23458 },
23459
23460 goToToday: function(){
23461 // summary:
23462 // Sets calendar's value to today's date
23463 this.set('value', new this.dateClassObj());
23464 },
23465
23466 constructor: function(/*Object*/args){
23467 var dateClass = (args.datePackage && (args.datePackage != "dojo.date"))? args.datePackage + ".Date" : "Date";
23468 this.dateClassObj = dojo.getObject(dateClass, false);
23469 this.datePackage = args.datePackage || this.datePackage;
23470 this.dateFuncObj = dojo.getObject(this.datePackage, false);
23471 this.dateLocaleModule = dojo.getObject(this.datePackage + ".locale", false);
23472 },
23473
23474 postMixInProperties: function(){
23475 // parser.instantiate sometimes passes in NaN for IE. Use default value in prototype instead.
23476 if(isNaN(this.value)){ delete this.value; }
23477 this.inherited(arguments);
23478 },
23479
23480 postCreate: function(){
23481 this.inherited(arguments);
23482 dojo.setSelectable(this.domNode, false);
23483
23484 var cloneClass = dojo.hitch(this, function(clazz, n){
23485 var template = dojo.query(clazz, this.domNode)[0];
23486 for(var i=0; i<n; i++){
23487 template.parentNode.appendChild(template.cloneNode(true));
23488 }
23489 });
23490
23491 // clone the day label and calendar day templates 6 times to make 7 columns
23492 cloneClass(".dijitCalendarDayLabelTemplate", 6);
23493 cloneClass(".dijitCalendarDateTemplate", 6);
23494
23495 // now make 6 week rows
23496 cloneClass(".dijitCalendarWeekTemplate", 5);
23497
23498 // insert localized day names in the header
23499 var dayNames = this.dateLocaleModule.getNames('days', this.dayWidth, 'standAlone', this.lang);
23500 var dayOffset = dojo.cldr.supplemental.getFirstDayOfWeek(this.lang);
23501 dojo.query(".dijitCalendarDayLabel", this.domNode).forEach(function(label, i){
23502 this._setText(label, dayNames[(i + dayOffset) % 7]);
23503 }, this);
23504
23505 var dateObj = new this.dateClassObj(this.value);
23506 // Fill in spacer/month dropdown element with all the month names (invisible) so that the maximum width will affect layout
23507 var monthNames = this.dateLocaleModule.getNames('months', 'wide', 'standAlone', this.lang, dateObj);
23508 cloneClass(".dijitCalendarMonthLabelTemplate", monthNames.length-1);
23509 dojo.query(".dijitCalendarMonthLabelTemplate", this.domNode).forEach(function(node, i){
23510 dojo.attr(node, "month", i);
23511 if(i in monthNames){ this._setText(node, monthNames[i]); }
23512 dojo.place(node.cloneNode(true), this.monthLabelSpacer);
23513 }, this);
23514
23515 this.value = null;
23516 this.set('value', dateObj);
23517 },
23518
23519 _onMenuHover: function(e){
23520 dojo.stopEvent(e);
23521 dojo.toggleClass(e.target, "dijitMenuItemHover");
23522 },
23523
23524 _adjustDisplay: function(/*String*/ part, /*int*/ amount){
23525 // summary:
23526 // Moves calendar forwards or backwards by months or years
23527 // part:
23528 // "month" or "year"
23529 // amount:
23530 // Number of months or years
23531 // tags:
23532 // private
23533 this.displayMonth = this.dateFuncObj.add(this.displayMonth, part, amount);
23534 this._populateGrid();
23535 },
23536
23537 _onMonthToggle: function(/*Event*/ evt){
23538 // summary:
23539 // Handler for when user triggers or dismisses the month list
23540 // tags:
23541 // protected
23542 dojo.stopEvent(evt);
23543
23544 if(evt.type == "mousedown"){
23545 var coords = dojo.position(this.monthLabelNode);
23546// coords.y -= dojo.position(this.domNode, true).y;
23547 // Size the dropdown's width to match the label in the widget
23548 // so that they are horizontally aligned
23549 var dim = {
23550 width: coords.w + "px",
23551 top: -this.displayMonth.getMonth() * coords.h + "px"
23552 };
23553 if((dojo.isIE && dojo.isQuirks) || dojo.isIE < 7){
23554 dim.left = -coords.w/2 + "px";
23555 }
23556 dojo.style(this.monthDropDown, dim);
23557 this._popupHandler = this.connect(document, "onmouseup", "_onMonthToggle");
23558 }else{
23559 this.disconnect(this._popupHandler);
23560 delete this._popupHandler;
23561 }
23562
23563 dojo.toggleClass(this.monthDropDown, "dijitHidden");
23564 dojo.toggleClass(this.monthLabelNode, "dijitVisible");
23565 },
23566
23567 _onMonthSelect: function(/*Event*/ evt){
23568 // summary:
23569 // Handler for when user selects a month from a list
23570 // tags:
23571 // protected
23572 this._onMonthToggle(evt);
23573 this.displayMonth.setMonth(dojo.attr(evt.target, "month"));
23574 this._populateGrid();
23575 },
23576
23577 _onDayClick: function(/*Event*/ evt){
23578 // summary:
23579 // Handler for day clicks, selects the date if appropriate
23580 // tags:
23581 // protected
23582 dojo.stopEvent(evt);
23583 for(var node = evt.target; node && !node.dijitDateValue; node = node.parentNode);
23584 if(node && !dojo.hasClass(node, "dijitCalendarDisabledDate")){
23585 this.set('value', node.dijitDateValue);
23586 this.onValueSelected(this.get('value'));
23587 }
23588 },
23589
23590 _onDayMouseOver: function(/*Event*/ evt){
23591 // summary:
23592 // Handler for mouse over events on days, sets hovered style
23593 // tags:
23594 // protected
23595
23596 // event can occur on <td> or the <span> inside the td,
23597 // set node to the <td>.
23598 var node =
23599 dojo.hasClass(evt.target, "dijitCalendarDateLabel") ?
23600 evt.target.parentNode :
23601 evt.target;
23602
23603 if(node && (node.dijitDateValue || node == this.previousYearLabelNode || node == this.nextYearLabelNode) ){
23604 dojo.addClass(node, "dijitCalendarHoveredDate");
23605 this._currentNode = node;
23606 }
23607 },
23608
23609 _onDayMouseOut: function(/*Event*/ evt){
23610 // summary:
23611 // Handler for mouse out events on days, clears hovered style
23612 // tags:
23613 // protected
23614
23615 if(!this._currentNode){ return; }
23616
23617 // if mouse out occurs moving from <td> to <span> inside <td>, ignore it
23618 if(evt.relatedTarget && evt.relatedTarget.parentNode == this._currentNode){ return; }
23619
23620 dojo.removeClass(this._currentNode, "dijitCalendarHoveredDate");
23621 if(dojo.hasClass(this._currentNode, "dijitCalendarActiveDate")) {
23622 dojo.removeClass(this._currentNode, "dijitCalendarActiveDate");
23623 }
23624 this._currentNode = null;
23625 },
23626
23627 _onDayMouseDown: function(/*Event*/ evt){
23628 var node = evt.target.parentNode;
23629 if(node && node.dijitDateValue){
23630 dojo.addClass(node, "dijitCalendarActiveDate");
23631 this._currentNode = node;
23632 }
23633 },
23634
23635 _onDayMouseUp: function(/*Event*/ evt){
23636 var node = evt.target.parentNode;
23637 if(node && node.dijitDateValue){
23638 dojo.removeClass(node, "dijitCalendarActiveDate");
23639 }
23640 },
23641
23642//TODO: use typematic
23643//TODO: skip disabled dates without ending up in a loop
23644//TODO: could optimize by avoiding populate grid when month does not change
23645 _onKeyPress: function(/*Event*/evt){
23646 // summary:
23647 // Provides keyboard navigation of calendar
23648 // tags:
23649 // protected
23650 var dk = dojo.keys,
23651 increment = -1,
23652 interval,
23653 newValue = this.value;
23654 switch(evt.keyCode){
23655 case dk.RIGHT_ARROW:
23656 increment = 1;
23657 //fallthrough...
23658 case dk.LEFT_ARROW:
23659 interval = "day";
23660 if(!this.isLeftToRight()){ increment *= -1; }
23661 break;
23662 case dk.DOWN_ARROW:
23663 increment = 1;
23664 //fallthrough...
23665 case dk.UP_ARROW:
23666 interval = "week";
23667 break;
23668 case dk.PAGE_DOWN:
23669 increment = 1;
23670 //fallthrough...
23671 case dk.PAGE_UP:
23672 interval = evt.ctrlKey ? "year" : "month";
23673 break;
23674 case dk.END:
23675 // go to the next month
23676 newValue = this.dateFuncObj.add(newValue, "month", 1);
23677 // subtract a day from the result when we're done
23678 interval = "day";
23679 //fallthrough...
23680 case dk.HOME:
23681 newValue = new Date(newValue).setDate(1);
23682 break;
23683 case dk.ENTER:
23684 this.onValueSelected(this.get('value'));
23685 break;
23686 case dk.ESCAPE:
23687 //TODO
23688 default:
23689 return;
23690 }
23691 dojo.stopEvent(evt);
23692
23693 if(interval){
23694 newValue = this.dateFuncObj.add(newValue, interval, increment);
23695 }
23696
23697 this.set("value", newValue);
23698 },
23699
23700 onValueSelected: function(/*Date*/ date){
23701 // summary:
23702 // Notification that a date cell was selected. It may be the same as the previous value.
23703 // description:
23704 // Used by `dijit.form._DateTimeTextBox` (and thus `dijit.form.DateTextBox`)
23705 // to get notification when the user has clicked a date.
23706 // tags:
23707 // protected
23708 },
23709
23710 onChange: function(/*Date*/ date){
23711 // summary:
23712 // Called only when the selected date has changed
23713 },
23714
23715 _isSelectedDate: function(/*Date*/ dateObject, /*String?*/ locale){
23716 // summary:
23717 // Extension point so developers can subclass Calendar to
23718 // support multiple (concurrently) selected dates
23719 // tags:
23720 // protected extension
23721 return !this.dateFuncObj.compare(dateObject, this.value, "date")
23722 },
23723
23724 isDisabledDate: function(/*Date*/ dateObject, /*String?*/ locale){
23725 // summary:
23726 // May be overridden to disable certain dates in the calendar e.g. `isDisabledDate=dojo.date.locale.isWeekend`
23727 // tags:
23728 // extension
23729/*=====
23730 return false; // Boolean
23731=====*/
23732 },
23733
23734 getClassForDate: function(/*Date*/ dateObject, /*String?*/ locale){
23735 // summary:
23736 // May be overridden to return CSS classes to associate with the date entry for the given dateObject,
23737 // for example to indicate a holiday in specified locale.
23738 // tags:
23739 // extension
23740
23741/*=====
23742 return ""; // String
23743=====*/
23744 }
23745 }
23746);
23747
23748}
23749
23750if(!dojo._hasResource["dijit.form._DateTimeTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
23751dojo._hasResource["dijit.form._DateTimeTextBox"] = true;
23752dojo.provide("dijit.form._DateTimeTextBox");
23753
23754
23755
23756
23757
23758
23759new Date("X"); // workaround for #11279, new Date("") == NaN
23760
23761/*=====
23762dojo.declare(
23763 "dijit.form._DateTimeTextBox.__Constraints",
23764 [dijit.form.RangeBoundTextBox.__Constraints, dojo.date.locale.__FormatOptions], {
23765 // summary:
23766 // Specifies both the rules on valid/invalid values (first/last date/time allowed),
23767 // and also formatting options for how the date/time is displayed.
23768 // example:
23769 // To restrict to dates within 2004, displayed in a long format like "December 25, 2005":
23770 // | {min:'2004-01-01',max:'2004-12-31', formatLength:'long'}
23771});
23772=====*/
23773
23774dojo.declare(
23775 "dijit.form._DateTimeTextBox",
23776 dijit.form.RangeBoundTextBox,
23777 {
23778 // summary:
23779 // Base class for validating, serializable, range-bound date or time text box.
23780
23781 // constraints: dijit.form._DateTimeTextBox.__Constraints
23782 // Despite the name, this parameter specifies both constraints on the input
23783 // (including starting/ending dates/times allowed) as well as
23784 // formatting options like whether the date is displayed in long (ex: December 25, 2005)
23785 // or short (ex: 12/25/2005) format. See `dijit.form._DateTimeTextBox.__Constraints` for details.
23786 /*=====
23787 constraints: {},
23788 ======*/
23789
23790 // Override ValidationTextBox.regExpGen().... we use a reg-ex generating function rather
23791 // than a straight regexp to deal with locale (plus formatting options too?)
23792 regExpGen: dojo.date.locale.regexp,
23793
23794 // datePackage: String
23795 // JavaScript namespace to find calendar routines. Uses Gregorian calendar routines
23796 // at dojo.date, by default.
23797 datePackage: "dojo.date",
23798
23799 // Override _FormWidget.compare() to work for dates/times
23800 compare: dojo.date.compare,
23801
23802 format: function(/*Date*/ value, /*dojo.date.locale.__FormatOptions*/ constraints){
23803 // summary:
23804 // Formats the value as a Date, according to specified locale (second argument)
23805 // tags:
23806 // protected
23807 if(!value){ return ''; }
23808 return this.dateLocaleModule.format(value, constraints);
23809 },
23810
23811 parse: function(/*String*/ value, /*dojo.date.locale.__FormatOptions*/ constraints){
23812 // summary:
23813 // Parses as string as a Date, according to constraints
23814 // tags:
23815 // protected
23816
23817 return this.dateLocaleModule.parse(value, constraints) || (this._isEmpty(value) ? null : undefined); // Date
23818 },
23819
23820 // Overrides ValidationTextBox.serialize() to serialize a date in canonical ISO format.
23821 serialize: function(/*anything*/val, /*Object?*/options){
23822 if(val.toGregorian){
23823 val = val.toGregorian();
23824 }
23825 return dojo.date.stamp.toISOString(val, options);
23826 },
23827
23828 // value: Date
23829 // The value of this widget as a JavaScript Date object. Use get("value") / set("value", val) to manipulate.
23830 // When passed to the parser in markup, must be specified according to `dojo.date.stamp.fromISOString`
23831 value: new Date(""), // value.toString()="NaN"
23832 _blankValue: null, // used by filter() when the textbox is blank
23833
23834 // popupClass: [protected extension] String
23835 // Name of the popup widget class used to select a date/time.
23836 // Subclasses should specify this.
23837 popupClass: "", // default is no popup = text only
23838
23839
23840 // _selector: [protected extension] String
23841 // Specifies constraints.selector passed to dojo.date functions, should be either
23842 // "date" or "time".
23843 // Subclass must specify this.
23844 _selector: "",
23845
23846 constructor: function(/*Object*/args){
23847 var dateClass = args.datePackage ? args.datePackage + ".Date" : "Date";
23848 this.dateClassObj = dojo.getObject(dateClass, false);
23849 this.value = new this.dateClassObj("");
23850
23851 this.datePackage = args.datePackage || this.datePackage;
23852 this.dateLocaleModule = dojo.getObject(this.datePackage + ".locale", false);
23853 this.regExpGen = this.dateLocaleModule.regexp;
23854 },
23855
23856 _setConstraintsAttr: function(/* Object */ constraints){
23857 constraints.selector = this._selector;
23858 constraints.fullYear = true; // see #5465 - always format with 4-digit years
23859 var fromISO = dojo.date.stamp.fromISOString;
23860 if(typeof constraints.min == "string"){ constraints.min = fromISO(constraints.min); }
23861 if(typeof constraints.max == "string"){ constraints.max = fromISO(constraints.max); }
23862 this.inherited(arguments, [constraints]);
23863 },
23864
23865 _onFocus: function(/*Event*/ evt){
23866 // summary:
23867 // open the popup
23868 this._open();
23869 this.inherited(arguments);
23870 },
23871
23872 _setValueAttr: function(/*Date*/ value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
23873 // summary:
23874 // Sets the date on this textbox. Note that `value` must be like a Javascript Date object.
23875 if(value !== undefined){
23876 if(!value || value.toString() == dijit.form._DateTimeTextBox.prototype.value.toString()){
23877 value = null;
23878 }
23879 if(value instanceof Date && !(this.dateClassObj instanceof Date)){
23880 value = new this.dateClassObj(value);
23881 }
23882 }
23883 this.inherited(arguments, [value, priorityChange, formattedValue]);
23884 if(this._picker){
23885 // #3948: fix blank date on popup only
23886 if(!value){value = new this.dateClassObj();}
23887 this._picker.set('value', value);
23888 }
23889 },
23890
23891 _open: function(){
23892 // summary:
23893 // opens the TimePicker, and sets the onValueSelected value
23894
23895 if(this.disabled || this.readOnly || !this.popupClass){return;}
23896
23897 var textBox = this;
23898
23899 if(!this._picker){
23900 var PopupProto = dojo.getObject(this.popupClass, false);
23901 this._picker = new PopupProto({
23902 onValueSelected: function(value){
23903 if(textBox._tabbingAway){
23904 delete textBox._tabbingAway;
23905 }else{
23906 textBox.focus(); // focus the textbox before the popup closes to avoid reopening the popup
23907 }
23908 setTimeout(dojo.hitch(textBox, "_close"), 1); // allow focus time to take
23909
23910 // this will cause InlineEditBox and other handlers to do stuff so make sure it's last
23911 dijit.form._DateTimeTextBox.superclass._setValueAttr.call(textBox, value, true);
23912 },
23913 id: this.id + "_popup",
23914 dir: textBox.dir,
23915 lang: textBox.lang,
23916 value: this.get('value') || new this.dateClassObj(),
23917 constraints: textBox.constraints,
23918
23919 datePackage: textBox.datePackage,
23920
23921 isDisabledDate: function(/*Date*/ date){
23922 // summary:
23923 // disables dates outside of the min/max of the _DateTimeTextBox
23924 var compare = dojo.date.compare;
23925 var constraints = textBox.constraints;
23926 return constraints && (
23927 (constraints.min && compare(constraints.min, date, textBox._selector) > 0) ||
23928 (constraints.max && compare(constraints.max, date, textBox._selector) < 0)
23929 );
23930 }
23931 });
23932 }
23933 if(!this._opened){
23934 // Open drop down. Align left sides of input box and drop down, even in RTL mode,
23935 // otherwise positioning thrown off when the drop down width is changed in marginBox call below (#10676)
23936 dijit.popup.open({
23937 parent: this,
23938 popup: this._picker,
23939 orient: {'BL':'TL', 'TL':'BL'},
23940 around: this.domNode,
23941 onCancel: dojo.hitch(this, this._close),
23942 onClose: function(){ textBox._opened=false; }
23943 });
23944 this._opened=true;
23945 }
23946
23947 dojo.marginBox(this._picker.domNode,{ w:this.domNode.offsetWidth });
23948 },
23949
23950 _close: function(){
23951 if(this._opened){
23952 dijit.popup.close(this._picker);
23953 this._opened=false;
23954 }
23955 },
23956
23957 _onBlur: function(){
23958 // summary:
23959 // Called magically when focus has shifted away from this widget and it's dropdown
23960 this._close();
23961 if(this._picker){
23962 // teardown so that constraints will be rebuilt next time (redundant reference: #6002)
23963 this._picker.destroy();
23964 delete this._picker;
23965 }
23966 this.inherited(arguments);
23967 // don't focus on <input>. the user has explicitly focused on something else.
23968 },
23969
23970 _getDisplayedValueAttr: function(){
23971 return this.textbox.value;
23972 },
23973
23974 _setDisplayedValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange){
23975 this._setValueAttr(this.parse(value, this.constraints), priorityChange, value);
23976 },
23977
23978 destroy: function(){
23979 if(this._picker){
23980 this._picker.destroy();
23981 delete this._picker;
23982 }
23983 this.inherited(arguments);
23984 },
23985
23986 postCreate: function(){
23987 this.inherited(arguments);
23988 this.connect(this.focusNode, 'onkeypress', this._onKeyPress);
23989 this.connect(this.focusNode, 'onclick', this._open);
23990 },
23991
23992 _onKeyPress: function(/*Event*/ e){
23993 // summary:
23994 // Handler for keypress events
23995
23996 var p = this._picker, dk = dojo.keys;
23997 // Handle the key in the picker, if it has a handler. If the handler
23998 // returns false, then don't handle any other keys.
23999 if(p && this._opened && p.handleKey){
24000 if(p.handleKey(e) === false){ return; }
24001 }
24002 if(this._opened && e.charOrCode == dk.ESCAPE && !(e.shiftKey || e.ctrlKey || e.altKey || e.metaKey)){
24003 this._close();
24004 dojo.stopEvent(e);
24005 }else if(!this._opened && e.charOrCode == dk.DOWN_ARROW){
24006 this._open();
24007 dojo.stopEvent(e);
24008 }else if(e.charOrCode === dk.TAB){
24009 this._tabbingAway = true;
24010 }else if(this._opened && (e.keyChar || e.charOrCode === dk.BACKSPACE || e.charOrCode == dk.DELETE)){
24011 // Replace the element - but do it after a delay to allow for
24012 // filtering to occur
24013 setTimeout(dojo.hitch(this, function(){
24014 if(this._picker && this._opened){
24015 dijit.placeOnScreenAroundElement(p.domNode.parentNode, this.domNode, {'BL':'TL', 'TL':'BL'}, p.orient ? dojo.hitch(p, "orient") : null);
24016 }
24017 }), 1);
24018 }
24019 }
24020 }
24021);
24022
24023}
24024
24025if(!dojo._hasResource["dijit.form.DateTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
24026dojo._hasResource["dijit.form.DateTextBox"] = true;
24027dojo.provide("dijit.form.DateTextBox");
24028
24029
24030
24031
24032dojo.declare(
24033 "dijit.form.DateTextBox",
24034 dijit.form._DateTimeTextBox,
24035 {
24036 // summary:
24037 // A validating, serializable, range-bound date text box with a drop down calendar
24038 //
24039 // Example:
24040 // | new dijit.form.DateTextBox({value: new Date(2009, 0, 20)})
24041 //
24042 // Example:
24043 // | <input dojotype='dijit.form.DateTextBox' value='2009-01-20'>
24044
24045 baseClass: "dijitTextBox dijitDateTextBox",
24046 popupClass: "dijit.Calendar",
24047 _selector: "date",
24048
24049 // value: Date
24050 // The value of this widget as a JavaScript Date object, with only year/month/day specified.
24051 // If specified in markup, use the format specified in `dojo.date.stamp.fromISOString`
24052 value: new Date("") // value.toString()="NaN"
24053 }
24054);
24055
24056}
24057
24058if(!dojo._hasResource["dijit.form._Spinner"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
24059dojo._hasResource["dijit.form._Spinner"] = true;
24060dojo.provide("dijit.form._Spinner");
24061
24062
24063
24064dojo.declare(
24065 "dijit.form._Spinner",
24066 dijit.form.RangeBoundTextBox,
24067 {
24068 // summary:
24069 // Mixin for validation widgets with a spinner.
24070 // description:
24071 // This class basically (conceptually) extends `dijit.form.ValidationTextBox`.
24072 // It modifies the template to have up/down arrows, and provides related handling code.
24073
24074 // defaultTimeout: Number
24075 // Number of milliseconds before a held arrow key or up/down button becomes typematic
24076 defaultTimeout: 500,
24077
24078 // minimumTimeout: Number
24079 // minimum number of milliseconds that typematic event fires when held key or button is held
24080 minimumTimeout: 10,
24081
24082 // timeoutChangeRate: Number
24083 // Fraction of time used to change the typematic timer between events.
24084 // 1.0 means that each typematic event fires at defaultTimeout intervals.
24085 // < 1.0 means that each typematic event fires at an increasing faster rate.
24086 timeoutChangeRate: 0.90,
24087
24088 // smallDelta: Number
24089 // Adjust the value by this much when spinning using the arrow keys/buttons
24090 smallDelta: 1,
24091
24092 // largeDelta: Number
24093 // Adjust the value by this much when spinning using the PgUp/Dn keys
24094 largeDelta: 10,
24095
24096 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=\"&#9650;\" 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=\"&#9660;\" 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=\"&Chi; \" 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"),
24097
24098 baseClass: "dijitTextBox dijitSpinner",
24099
24100 // Set classes like dijitUpArrowButtonHover or dijitDownArrowButtonActive depending on
24101 // mouse action over specified node
24102 cssStateNodes: {
24103 "upArrowNode": "dijitUpArrowButton",
24104 "downArrowNode": "dijitDownArrowButton"
24105 },
24106
24107 adjust: function(/* Object */ val, /*Number*/ delta){
24108 // summary:
24109 // Overridable function used to adjust a primitive value(Number/Date/...) by the delta amount specified.
24110 // The val is adjusted in a way that makes sense to the object type.
24111 // tags:
24112 // protected extension
24113 return val;
24114 },
24115
24116 _arrowPressed: function(/*Node*/ nodePressed, /*Number*/ direction, /*Number*/ increment){
24117 // summary:
24118 // Handler for arrow button or arrow key being pressed
24119 if(this.disabled || this.readOnly){ return; }
24120 this._setValueAttr(this.adjust(this.get('value'), direction*increment), false);
24121 dijit.selectInputText(this.textbox, this.textbox.value.length);
24122 },
24123
24124 _arrowReleased: function(/*Node*/ node){
24125 // summary:
24126 // Handler for arrow button or arrow key being released
24127 this._wheelTimer = null;
24128 if(this.disabled || this.readOnly){ return; }
24129 },
24130
24131 _typematicCallback: function(/*Number*/ count, /*DOMNode*/ node, /*Event*/ evt){
24132 var inc=this.smallDelta;
24133 if(node == this.textbox){
24134 var k=dojo.keys;
24135 var key = evt.charOrCode;
24136 inc = (key == k.PAGE_UP || key == k.PAGE_DOWN) ? this.largeDelta : this.smallDelta;
24137 node = (key == k.UP_ARROW || key == k.PAGE_UP) ? this.upArrowNode : this.downArrowNode;
24138 }
24139 if(count == -1){ this._arrowReleased(node); }
24140 else{ this._arrowPressed(node, (node == this.upArrowNode) ? 1 : -1, inc); }
24141 },
24142
24143 _wheelTimer: null,
24144 _mouseWheeled: function(/*Event*/ evt){
24145 // summary:
24146 // Mouse wheel listener where supported
24147
24148 dojo.stopEvent(evt);
24149 // FIXME: Safari bubbles
24150
24151 // be nice to DOH and scroll as much as the event says to
24152 var scrollAmount = evt.detail ? (evt.detail * -1) : (evt.wheelDelta / 120);
24153 if(scrollAmount !== 0){
24154 var node = this[(scrollAmount > 0 ? "upArrowNode" : "downArrowNode" )];
24155
24156 this._arrowPressed(node, scrollAmount, this.smallDelta);
24157
24158 if(!this._wheelTimer){
24159 clearTimeout(this._wheelTimer);
24160 }
24161 this._wheelTimer = setTimeout(dojo.hitch(this,"_arrowReleased",node), 50);
24162 }
24163
24164 },
24165
24166 postCreate: function(){
24167 this.inherited(arguments);
24168
24169 // extra listeners
24170 this.connect(this.domNode, !dojo.isMozilla ? "onmousewheel" : 'DOMMouseScroll', "_mouseWheeled");
24171 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));
24172 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));
24173 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));
24174 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));
24175 }
24176});
24177
24178}
24179
24180if(!dojo._hasResource["dijit.form.NumberSpinner"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
24181dojo._hasResource["dijit.form.NumberSpinner"] = true;
24182dojo.provide("dijit.form.NumberSpinner");
24183
24184
24185
24186
24187dojo.declare("dijit.form.NumberSpinner",
24188 [dijit.form._Spinner, dijit.form.NumberTextBoxMixin],
24189 {
24190 // summary:
24191 // Extends NumberTextBox to add up/down arrows and pageup/pagedown for incremental change to the value
24192 //
24193 // description:
24194 // A `dijit.form.NumberTextBox` extension to provide keyboard accessible value selection
24195 // as well as icons for spinning direction. When using the keyboard, the typematic rules
24196 // apply, meaning holding the key will gradually increase or decrease the value and
24197 // accelerate.
24198 //
24199 // example:
24200 // | new dijit.form.NumberSpinner({ constraints:{ max:300, min:100 }}, "someInput");
24201
24202 adjust: function(/* Object */val, /* Number*/delta){
24203 // summary:
24204 // Change Number val by the given amount
24205 // tags:
24206 // protected
24207
24208 var tc = this.constraints,
24209 v = isNaN(val),
24210 gotMax = !isNaN(tc.max),
24211 gotMin = !isNaN(tc.min)
24212 ;
24213 if(v && delta != 0){ // blank or invalid value and they want to spin, so create defaults
24214 val = (delta > 0) ?
24215 gotMin ? tc.min : gotMax ? tc.max : 0 :
24216 gotMax ? this.constraints.max : gotMin ? tc.min : 0
24217 ;
24218 }
24219 var newval = val + delta;
24220 if(v || isNaN(newval)){ return val; }
24221 if(gotMax && (newval > tc.max)){
24222 newval = tc.max;
24223 }
24224 if(gotMin && (newval < tc.min)){
24225 newval = tc.min;
24226 }
24227 return newval;
24228 },
24229
24230 _onKeyPress: function(e){
24231 if((e.charOrCode == dojo.keys.HOME || e.charOrCode == dojo.keys.END) && !(e.ctrlKey || e.altKey || e.metaKey)
24232 && typeof this.get('value') != 'undefined' /* gibberish, so HOME and END are default editing keys*/){
24233 var value = this.constraints[(e.charOrCode == dojo.keys.HOME ? "min" : "max")];
24234 if(typeof value == "number"){
24235 this._setValueAttr(value, false);
24236 }
24237 // eat home or end key whether we change the value or not
24238 dojo.stopEvent(e);
24239 }
24240 }
24241});
24242
24243}
24244
24245if(!dojo._hasResource["dijit.form.MultiSelect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
24246dojo._hasResource["dijit.form.MultiSelect"] = true;
24247dojo.provide("dijit.form.MultiSelect");
24248
24249
24250
24251dojo.declare("dijit.form.MultiSelect", dijit.form._FormValueWidget, {
24252 // summary:
24253 // Widget version of a <select multiple=true> element,
24254 // for selecting multiple options.
24255
24256 // size: Number
24257 // Number of elements to display on a page
24258 // NOTE: may be removed in version 2.0, since elements may have variable height;
24259 // set the size via style="..." or CSS class names instead.
24260 size: 7,
24261
24262 templateString: "<select multiple='true' ${!nameAttrSetting} dojoAttachPoint='containerNode,focusNode' dojoAttachEvent='onchange: _onChange'></select>",
24263
24264 attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
24265 size: "focusNode"
24266 }),
24267
24268 reset: function(){
24269 // summary:
24270 // Reset the widget's value to what it was at initialization time
24271
24272 // TODO: once we inherit from FormValueWidget this won't be needed
24273 this._hasBeenBlurred = false;
24274 this._setValueAttr(this._resetValue, true);
24275 },
24276
24277 addSelected: function(/* dijit.form.MultiSelect */ select){
24278 // summary:
24279 // Move the selected nodes of a passed Select widget
24280 // instance to this Select widget.
24281 //
24282 // example:
24283 // | // move all the selected values from "bar" to "foo"
24284 // | dijit.byId("foo").addSelected(dijit.byId("bar"));
24285
24286 select.getSelected().forEach(function(n){
24287 this.containerNode.appendChild(n);
24288 // scroll to bottom to see item
24289 // cannot use scrollIntoView since <option> tags don't support all attributes
24290 // does not work on IE due to a bug where <select> always shows scrollTop = 0
24291 this.domNode.scrollTop = this.domNode.offsetHeight; // overshoot will be ignored
24292 // scrolling the source select is trickier esp. on safari who forgets to change the scrollbar size
24293 var oldscroll = select.domNode.scrollTop;
24294 select.domNode.scrollTop = 0;
24295 select.domNode.scrollTop = oldscroll;
24296 },this);
24297 },
24298
24299 getSelected: function(){
24300 // summary:
24301 // Access the NodeList of the selected options directly
24302 return dojo.query("option",this.containerNode).filter(function(n){
24303 return n.selected; // Boolean
24304 }); // dojo.NodeList
24305 },
24306
24307 _getValueAttr: function(){
24308 // summary:
24309 // Hook so attr('value') works.
24310 // description:
24311 // Returns an array of the selected options' values.
24312 return this.getSelected().map(function(n){
24313 return n.value;
24314 });
24315 },
24316
24317 multiple: true, // for Form
24318
24319 _setValueAttr: function(/* Array */values){
24320 // summary:
24321 // Hook so attr('value', values) works.
24322 // description:
24323 // Set the value(s) of this Select based on passed values
24324 dojo.query("option",this.containerNode).forEach(function(n){
24325 n.selected = (dojo.indexOf(values,n.value) != -1);
24326 });
24327 },
24328
24329 invertSelection: function(onChange){
24330 // summary:
24331 // Invert the selection
24332 // onChange: Boolean
24333 // If null, onChange is not fired.
24334 dojo.query("option",this.containerNode).forEach(function(n){
24335 n.selected = !n.selected;
24336 });
24337 this._handleOnChange(this.get('value'), onChange == true);
24338 },
24339
24340 _onChange: function(/*Event*/ e){
24341 this._handleOnChange(this.get('value'), true);
24342 },
24343
24344 // for layout widgets:
24345 resize: function(/* Object */size){
24346 if(size){
24347 dojo.marginBox(this.domNode, size);
24348 }
24349 },
24350
24351 postCreate: function(){
24352 this._onChange();
24353 }
24354});
24355
24356}
24357
24358if(!dojo._hasResource["dijit.form.HorizontalSlider"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
24359dojo._hasResource["dijit.form.HorizontalSlider"] = true;
24360dojo.provide("dijit.form.HorizontalSlider");
24361
24362
24363
24364
24365
24366
24367
24368
24369dojo.declare(
24370 "dijit.form.HorizontalSlider",
24371 [dijit.form._FormValueWidget, dijit._Container],
24372{
24373 // summary:
24374 // A form widget that allows one to select a value with a horizontally draggable handle
24375
24376 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"),
24377
24378 // Overrides FormValueWidget.value to indicate numeric value
24379 value: 0,
24380
24381 // showButtons: Boolean
24382 // Show increment/decrement buttons at the ends of the slider?
24383 showButtons: true,
24384
24385 // minimum:: Integer
24386 // The minimum value the slider can be set to.
24387 minimum: 0,
24388
24389 // maximum: Integer
24390 // The maximum value the slider can be set to.
24391 maximum: 100,
24392
24393 // discreteValues: Integer
24394 // If specified, indicates that the slider handle has only 'discreteValues' possible positions,
24395 // and that after dragging the handle, it will snap to the nearest possible position.
24396 // Thus, the slider has only 'discreteValues' possible values.
24397 //
24398 // For example, if minimum=10, maxiumum=30, and discreteValues=3, then the slider handle has
24399 // three possible positions, representing values 10, 20, or 30.
24400 //
24401 // If discreteValues is not specified or if it's value is higher than the number of pixels
24402 // in the slider bar, then the slider handle can be moved freely, and the slider's value will be
24403 // computed/reported based on pixel position (in this case it will likely be fractional,
24404 // such as 123.456789).
24405 discreteValues: Infinity,
24406
24407 // pageIncrement: Integer
24408 // If discreteValues is also specified, this indicates the amount of clicks (ie, snap positions)
24409 // that the slider handle is moved via pageup/pagedown keys.
24410 // If discreteValues is not specified, it indicates the number of pixels.
24411 pageIncrement: 2,
24412
24413 // clickSelect: Boolean
24414 // If clicking the slider bar changes the value or not
24415 clickSelect: true,
24416
24417 // slideDuration: Number
24418 // The time in ms to take to animate the slider handle from 0% to 100%,
24419 // when clicking the slider bar to make the handle move.
24420 slideDuration: dijit.defaultDuration,
24421
24422 // Flag to _Templated (TODO: why is this here? I see no widgets in the template.)
24423 widgetsInTemplate: true,
24424
24425 attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
24426 id: ""
24427 }),
24428
24429 baseClass: "dijitSlider",
24430
24431 // Apply CSS classes to up/down arrows and handle per mouse state
24432 cssStateNodes: {
24433 incrementButton: "dijitSliderIncrementButton",
24434 decrementButton: "dijitSliderDecrementButton",
24435 focusNode: "dijitSliderThumb"
24436 },
24437
24438 _mousePixelCoord: "pageX",
24439 _pixelCount: "w",
24440 _startingPixelCoord: "x",
24441 _startingPixelCount: "l",
24442 _handleOffsetCoord: "left",
24443 _progressPixelSize: "width",
24444
24445 _onKeyUp: function(/*Event*/ e){
24446 if(this.disabled || this.readOnly || e.altKey || e.ctrlKey || e.metaKey){ return; }
24447 this._setValueAttr(this.value, true);
24448 },
24449
24450 _onKeyPress: function(/*Event*/ e){
24451 if(this.disabled || this.readOnly || e.altKey || e.ctrlKey || e.metaKey){ return; }
24452 switch(e.charOrCode){
24453 case dojo.keys.HOME:
24454 this._setValueAttr(this.minimum, false);
24455 break;
24456 case dojo.keys.END:
24457 this._setValueAttr(this.maximum, false);
24458 break;
24459 // this._descending === false: if ascending vertical (min on top)
24460 // (this._descending || this.isLeftToRight()): if left-to-right horizontal or descending vertical
24461 case ((this._descending || this.isLeftToRight()) ? dojo.keys.RIGHT_ARROW : dojo.keys.LEFT_ARROW):
24462 case (this._descending === false ? dojo.keys.DOWN_ARROW : dojo.keys.UP_ARROW):
24463 case (this._descending === false ? dojo.keys.PAGE_DOWN : dojo.keys.PAGE_UP):
24464 this.increment(e);
24465 break;
24466 case ((this._descending || this.isLeftToRight()) ? dojo.keys.LEFT_ARROW : dojo.keys.RIGHT_ARROW):
24467 case (this._descending === false ? dojo.keys.UP_ARROW : dojo.keys.DOWN_ARROW):
24468 case (this._descending === false ? dojo.keys.PAGE_UP : dojo.keys.PAGE_DOWN):
24469 this.decrement(e);
24470 break;
24471 default:
24472 return;
24473 }
24474 dojo.stopEvent(e);
24475 },
24476
24477 _onHandleClick: function(e){
24478 if(this.disabled || this.readOnly){ return; }
24479 if(!dojo.isIE){
24480 // make sure you get focus when dragging the handle
24481 // (but don't do on IE because it causes a flicker on mouse up (due to blur then focus)
24482 dijit.focus(this.sliderHandle);
24483 }
24484 dojo.stopEvent(e);
24485 },
24486
24487 _isReversed: function(){
24488 // summary:
24489 // Returns true if direction is from right to left
24490 // tags:
24491 // protected extension
24492 return !this.isLeftToRight();
24493 },
24494
24495 _onBarClick: function(e){
24496 if(this.disabled || this.readOnly || !this.clickSelect){ return; }
24497 dijit.focus(this.sliderHandle);
24498 dojo.stopEvent(e);
24499 var abspos = dojo.position(this.sliderBarContainer, true);
24500 var pixelValue = e[this._mousePixelCoord] - abspos[this._startingPixelCoord];
24501 this._setPixelValue(this._isReversed() ? (abspos[this._pixelCount] - pixelValue) : pixelValue, abspos[this._pixelCount], true);
24502 this._movable.onMouseDown(e);
24503 },
24504
24505 _setPixelValue: function(/*Number*/ pixelValue, /*Number*/ maxPixels, /*Boolean, optional*/ priorityChange){
24506 if(this.disabled || this.readOnly){ return; }
24507 pixelValue = pixelValue < 0 ? 0 : maxPixels < pixelValue ? maxPixels : pixelValue;
24508 var count = this.discreteValues;
24509 if(count <= 1 || count == Infinity){ count = maxPixels; }
24510 count--;
24511 var pixelsPerValue = maxPixels / count;
24512 var wholeIncrements = Math.round(pixelValue / pixelsPerValue);
24513 this._setValueAttr((this.maximum-this.minimum)*wholeIncrements/count + this.minimum, priorityChange);
24514 },
24515
24516 _setValueAttr: function(/*Number*/ value, /*Boolean, optional*/ priorityChange){
24517 // summary:
24518 // Hook so attr('value', value) works.
24519 this.valueNode.value = this.value = value;
24520 dijit.setWaiState(this.focusNode, "valuenow", value);
24521 this.inherited(arguments);
24522 var percent = (value - this.minimum) / (this.maximum - this.minimum);
24523 var progressBar = (this._descending === false) ? this.remainingBar : this.progressBar;
24524 var remainingBar = (this._descending === false) ? this.progressBar : this.remainingBar;
24525 if(this._inProgressAnim && this._inProgressAnim.status != "stopped"){
24526 this._inProgressAnim.stop(true);
24527 }
24528 if(priorityChange && this.slideDuration > 0 && progressBar.style[this._progressPixelSize]){
24529 // animate the slider
24530 var _this = this;
24531 var props = {};
24532 var start = parseFloat(progressBar.style[this._progressPixelSize]);
24533 var duration = this.slideDuration * (percent-start/100);
24534 if(duration == 0){ return; }
24535 if(duration < 0){ duration = 0 - duration; }
24536 props[this._progressPixelSize] = { start: start, end: percent*100, units:"%" };
24537 this._inProgressAnim = dojo.animateProperty({ node: progressBar, duration: duration,
24538 onAnimate: function(v){ remainingBar.style[_this._progressPixelSize] = (100-parseFloat(v[_this._progressPixelSize])) + "%"; },
24539 onEnd: function(){ delete _this._inProgressAnim; },
24540 properties: props
24541 })
24542 this._inProgressAnim.play();
24543 }
24544 else{
24545 progressBar.style[this._progressPixelSize] = (percent*100) + "%";
24546 remainingBar.style[this._progressPixelSize] = ((1-percent)*100) + "%";
24547 }
24548 },
24549
24550 _bumpValue: function(signedChange, /*Boolean, optional*/ priorityChange){
24551 if(this.disabled || this.readOnly){ return; }
24552 var s = dojo.getComputedStyle(this.sliderBarContainer);
24553 var c = dojo._getContentBox(this.sliderBarContainer, s);
24554 var count = this.discreteValues;
24555 if(count <= 1 || count == Infinity){ count = c[this._pixelCount]; }
24556 count--;
24557 var value = (this.value - this.minimum) * count / (this.maximum - this.minimum) + signedChange;
24558 if(value < 0){ value = 0; }
24559 if(value > count){ value = count; }
24560 value = value * (this.maximum - this.minimum) / count + this.minimum;
24561 this._setValueAttr(value, priorityChange);
24562 },
24563
24564 _onClkBumper: function(val){
24565 if(this.disabled || this.readOnly || !this.clickSelect){ return; }
24566 this._setValueAttr(val, true);
24567 },
24568
24569 _onClkIncBumper: function(){
24570 this._onClkBumper(this._descending === false ? this.minimum : this.maximum);
24571 },
24572
24573 _onClkDecBumper: function(){
24574 this._onClkBumper(this._descending === false ? this.maximum : this.minimum);
24575 },
24576
24577 decrement: function(/*Event*/ e){
24578 // summary:
24579 // Decrement slider
24580 // tags:
24581 // private
24582 this._bumpValue(e.charOrCode == dojo.keys.PAGE_DOWN ? -this.pageIncrement : -1);
24583 },
24584
24585 increment: function(/*Event*/ e){
24586 // summary:
24587 // Increment slider
24588 // tags:
24589 // private
24590 this._bumpValue(e.charOrCode == dojo.keys.PAGE_UP ? this.pageIncrement : 1);
24591 },
24592
24593 _mouseWheeled: function(/*Event*/ evt){
24594 // summary:
24595 // Event handler for mousewheel where supported
24596 dojo.stopEvent(evt);
24597 var janky = !dojo.isMozilla;
24598 var scroll = evt[(janky ? "wheelDelta" : "detail")] * (janky ? 1 : -1);
24599 this._bumpValue(scroll < 0 ? -1 : 1, true); // negative scroll acts like a decrement
24600 },
24601
24602 startup: function(){
24603 if(this._started){ return; }
24604
24605 dojo.forEach(this.getChildren(), function(child){
24606 if(this[child.container] != this.containerNode){
24607 this[child.container].appendChild(child.domNode);
24608 }
24609 }, this);
24610
24611 this.inherited(arguments);
24612 },
24613
24614 _typematicCallback: function(/*Number*/ count, /*Object*/ button, /*Event*/ e){
24615 if(count == -1){
24616 this._setValueAttr(this.value, true);
24617 }else{
24618 this[(button == (this._descending? this.incrementButton : this.decrementButton)) ? "decrement" : "increment"](e);
24619 }
24620 },
24621
24622 postCreate: function(){
24623 if(this.showButtons){
24624 this.incrementButton.style.display="";
24625 this.decrementButton.style.display="";
24626 this._connects.push(dijit.typematic.addMouseListener(
24627 this.decrementButton, this, "_typematicCallback", 25, 500));
24628 this._connects.push(dijit.typematic.addMouseListener(
24629 this.incrementButton, this, "_typematicCallback", 25, 500));
24630 }
24631 this.connect(this.domNode, !dojo.isMozilla ? "onmousewheel" : "DOMMouseScroll", "_mouseWheeled");
24632
24633 // define a custom constructor for a SliderMover that points back to me
24634 var mover = dojo.declare(dijit.form._SliderMover, {
24635 widget: this
24636 });
24637
24638 this._movable = new dojo.dnd.Moveable(this.sliderHandle, {mover: mover});
24639 // find any associated label element and add to slider focusnode.
24640 var label=dojo.query('label[for="'+this.id+'"]');
24641 if(label.length){
24642 label[0].id = (this.id+"_label");
24643 dijit.setWaiState(this.focusNode, "labelledby", label[0].id);
24644 }
24645 dijit.setWaiState(this.focusNode, "valuemin", this.minimum);
24646 dijit.setWaiState(this.focusNode, "valuemax", this.maximum);
24647
24648 this.inherited(arguments);
24649 this._layoutHackIE7();
24650 },
24651
24652 destroy: function(){
24653 this._movable.destroy();
24654 if(this._inProgressAnim && this._inProgressAnim.status != "stopped"){
24655 this._inProgressAnim.stop(true);
24656 }
24657 this._supportingWidgets = dijit.findWidgets(this.domNode); // tells destroy about pseudo-child widgets (ruler/labels)
24658 this.inherited(arguments);
24659 }
24660});
24661
24662dojo.declare("dijit.form._SliderMover",
24663 dojo.dnd.Mover,
24664{
24665 onMouseMove: function(e){
24666 var widget = this.widget;
24667 var abspos = widget._abspos;
24668 if(!abspos){
24669 abspos = widget._abspos = dojo.position(widget.sliderBarContainer, true);
24670 widget._setPixelValue_ = dojo.hitch(widget, "_setPixelValue");
24671 widget._isReversed_ = widget._isReversed();
24672 }
24673 var pixelValue = e[widget._mousePixelCoord] - abspos[widget._startingPixelCoord];
24674 widget._setPixelValue_(widget._isReversed_ ? (abspos[widget._pixelCount]-pixelValue) : pixelValue, abspos[widget._pixelCount], false);
24675 },
24676
24677 destroy: function(e){
24678 dojo.dnd.Mover.prototype.destroy.apply(this, arguments);
24679 var widget = this.widget;
24680 widget._abspos = null;
24681 widget._setValueAttr(widget.value, true);
24682 }
24683});
24684
24685
24686
24687}
24688
24689if(!dojo._hasResource["dijit.form.VerticalSlider"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
24690dojo._hasResource["dijit.form.VerticalSlider"] = true;
24691dojo.provide("dijit.form.VerticalSlider");
24692
24693
24694
24695dojo.declare(
24696 "dijit.form.VerticalSlider",
24697 dijit.form.HorizontalSlider,
24698{
24699 // summary:
24700 // A form widget that allows one to select a value with a vertically draggable handle
24701
24702 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"),
24703 _mousePixelCoord: "pageY",
24704 _pixelCount: "h",
24705 _startingPixelCoord: "y",
24706 _startingPixelCount: "t",
24707 _handleOffsetCoord: "top",
24708 _progressPixelSize: "height",
24709
24710 // _descending: Boolean
24711 // Specifies if the slider values go from high-on-top (true), or low-on-top (false)
24712 // TODO: expose this in 1.2 - the css progress/remaining bar classes need to be reversed
24713 _descending: true,
24714
24715 _isReversed: function(){
24716 // summary:
24717 // Overrides HorizontalSlider._isReversed.
24718 // Indicates if values are high on top (with low numbers on the bottom).
24719 return this._descending;
24720 }
24721});
24722
24723
24724}
24725
24726if(!dojo._hasResource["dijit.form.HorizontalRule"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
24727dojo._hasResource["dijit.form.HorizontalRule"] = true;
24728dojo.provide("dijit.form.HorizontalRule");
24729
24730
24731
24732
24733dojo.declare("dijit.form.HorizontalRule", [dijit._Widget, dijit._Templated],
24734{
24735 // summary:
24736 // Hash marks for `dijit.form.HorizontalSlider`
24737
24738 templateString: '<div class="dijitRuleContainer dijitRuleContainerH"></div>',
24739
24740 // count: Integer
24741 // Number of hash marks to generate
24742 count: 3,
24743
24744 // container: String
24745 // For HorizontalSlider, this is either "topDecoration" or "bottomDecoration",
24746 // and indicates whether this rule goes above or below the slider.
24747 container: "containerNode",
24748
24749 // ruleStyle: String
24750 // CSS style to apply to individual hash marks
24751 ruleStyle: "",
24752
24753 _positionPrefix: '<div class="dijitRuleMark dijitRuleMarkH" style="left:',
24754 _positionSuffix: '%;',
24755 _suffix: '"></div>',
24756
24757 _genHTML: function(pos, ndx){
24758 return this._positionPrefix + pos + this._positionSuffix + this.ruleStyle + this._suffix;
24759 },
24760
24761 // _isHorizontal: [protected extension] Boolean
24762 // VerticalRule will override this...
24763 _isHorizontal: true,
24764
24765 postCreate: function(){
24766 var innerHTML;
24767 if(this.count == 1){
24768 innerHTML = this._genHTML(50, 0);
24769 }else{
24770 var i;
24771 var interval = 100 / (this.count-1);
24772 if(!this._isHorizontal || this.isLeftToRight()){
24773 innerHTML = this._genHTML(0, 0);
24774 for(i=1; i < this.count-1; i++){
24775 innerHTML += this._genHTML(interval*i, i);
24776 }
24777 innerHTML += this._genHTML(100, this.count-1);
24778 }else{
24779 innerHTML = this._genHTML(100, 0);
24780 for(i=1; i < this.count-1; i++){
24781 innerHTML += this._genHTML(100-interval*i, i);
24782 }
24783 innerHTML += this._genHTML(0, this.count-1);
24784 }
24785 }
24786 this.domNode.innerHTML = innerHTML;
24787 }
24788});
24789
24790}
24791
24792if(!dojo._hasResource["dijit.form.VerticalRule"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
24793dojo._hasResource["dijit.form.VerticalRule"] = true;
24794dojo.provide("dijit.form.VerticalRule");
24795
24796
24797
24798dojo.declare("dijit.form.VerticalRule", dijit.form.HorizontalRule,
24799{
24800 // summary:
24801 // Hash marks for the `dijit.form.VerticalSlider`
24802
24803 templateString: '<div class="dijitRuleContainer dijitRuleContainerV"></div>',
24804 _positionPrefix: '<div class="dijitRuleMark dijitRuleMarkV" style="top:',
24805
24806/*=====
24807 // container: String
24808 // This is either "leftDecoration" or "rightDecoration",
24809 // to indicate whether this rule goes to the left or to the right of the slider.
24810 // Note that on RTL system, "leftDecoration" would actually go to the right, and vice-versa.
24811 container: "",
24812=====*/
24813
24814 // Overrides HorizontalRule._isHorizontal
24815 _isHorizontal: false
24816
24817});
24818
24819
24820}
24821
24822if(!dojo._hasResource["dijit.form.HorizontalRuleLabels"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
24823dojo._hasResource["dijit.form.HorizontalRuleLabels"] = true;
24824dojo.provide("dijit.form.HorizontalRuleLabels");
24825
24826
24827
24828dojo.declare("dijit.form.HorizontalRuleLabels", dijit.form.HorizontalRule,
24829{
24830 // summary:
24831 // Labels for `dijit.form.HorizontalSlider`
24832
24833 templateString: '<div class="dijitRuleContainer dijitRuleContainerH dijitRuleLabelsContainer dijitRuleLabelsContainerH"></div>',
24834
24835 // labelStyle: String
24836 // CSS style to apply to individual text labels
24837 labelStyle: "",
24838
24839 // labels: String[]?
24840 // Array of text labels to render - evenly spaced from left-to-right or bottom-to-top.
24841 // Alternately, minimum and maximum can be specified, to get numeric labels.
24842 labels: [],
24843
24844 // numericMargin: Integer
24845 // Number of generated numeric labels that should be rendered as '' on the ends when labels[] are not specified
24846 numericMargin: 0,
24847
24848 // numericMinimum: Integer
24849 // Leftmost label value for generated numeric labels when labels[] are not specified
24850 minimum: 0,
24851
24852 // numericMaximum: Integer
24853 // Rightmost label value for generated numeric labels when labels[] are not specified
24854 maximum: 1,
24855
24856 // constraints: Object
24857 // pattern, places, lang, et al (see dojo.number) for generated numeric labels when labels[] are not specified
24858 constraints: {pattern:"#%"},
24859
24860 _positionPrefix: '<div class="dijitRuleLabelContainer dijitRuleLabelContainerH" style="left:',
24861 _labelPrefix: '"><div class="dijitRuleLabel dijitRuleLabelH">',
24862 _suffix: '</div></div>',
24863
24864 _calcPosition: function(pos){
24865 // summary:
24866 // Returns the value to be used in HTML for the label as part of the left: attribute
24867 // tags:
24868 // protected extension
24869 return pos;
24870 },
24871
24872 _genHTML: function(pos, ndx){
24873 return this._positionPrefix + this._calcPosition(pos) + this._positionSuffix + this.labelStyle + this._labelPrefix + this.labels[ndx] + this._suffix;
24874 },
24875
24876 getLabels: function(){
24877 // summary:
24878 // Overridable function to return array of labels to use for this slider.
24879 // Can specify a getLabels() method instead of a labels[] array, or min/max attributes.
24880 // tags:
24881 // protected extension
24882
24883 // if the labels array was not specified directly, then see if <li> children were
24884 var labels = this.labels;
24885 if(!labels.length){
24886 // for markup creation, labels are specified as child elements
24887 labels = dojo.query("> li", this.srcNodeRef).map(function(node){
24888 return String(node.innerHTML);
24889 });
24890 }
24891 this.srcNodeRef.innerHTML = '';
24892 // if the labels were not specified directly and not as <li> children, then calculate numeric labels
24893 if(!labels.length && this.count > 1){
24894 var start = this.minimum;
24895 var inc = (this.maximum - start) / (this.count-1);
24896 for(var i=0; i < this.count; i++){
24897 labels.push((i < this.numericMargin || i >= (this.count-this.numericMargin)) ? '' : dojo.number.format(start, this.constraints));
24898 start += inc;
24899 }
24900 }
24901 return labels;
24902 },
24903
24904 postMixInProperties: function(){
24905 this.inherited(arguments);
24906 this.labels = this.getLabels();
24907 this.count = this.labels.length;
24908 }
24909});
24910
24911
24912
24913}
24914
24915if(!dojo._hasResource["dijit.form.VerticalRuleLabels"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
24916dojo._hasResource["dijit.form.VerticalRuleLabels"] = true;
24917dojo.provide("dijit.form.VerticalRuleLabels");
24918
24919
24920
24921dojo.declare("dijit.form.VerticalRuleLabels", dijit.form.HorizontalRuleLabels,
24922{
24923 // summary:
24924 // Labels for the `dijit.form.VerticalSlider`
24925
24926 templateString: '<div class="dijitRuleContainer dijitRuleContainerV dijitRuleLabelsContainer dijitRuleLabelsContainerV"></div>',
24927
24928 _positionPrefix: '<div class="dijitRuleLabelContainer dijitRuleLabelContainerV" style="top:',
24929 _labelPrefix: '"><span class="dijitRuleLabel dijitRuleLabelV">',
24930
24931 _calcPosition: function(pos){
24932 // Overrides HorizontalRuleLabel._calcPosition()
24933 return 100-pos;
24934 },
24935
24936 // needed to prevent labels from being reversed in RTL mode
24937 _isHorizontal: false
24938});
24939
24940}
24941
24942if(!dojo._hasResource["dijit.form.SimpleTextarea"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
24943dojo._hasResource["dijit.form.SimpleTextarea"] = true;
24944dojo.provide("dijit.form.SimpleTextarea");
24945
24946
24947
24948dojo.declare("dijit.form.SimpleTextarea",
24949 dijit.form.TextBox,
24950 {
24951 // summary:
24952 // A simple textarea that degrades, and responds to
24953 // minimal LayoutContainer usage, and works with dijit.form.Form.
24954 // Doesn't automatically size according to input, like Textarea.
24955 //
24956 // example:
24957 // | <textarea dojoType="dijit.form.SimpleTextarea" name="foo" value="bar" rows=30 cols=40></textarea>
24958 //
24959 // example:
24960 // | new dijit.form.SimpleTextarea({ rows:20, cols:30 }, "foo");
24961
24962 baseClass: "dijitTextBox dijitTextArea",
24963
24964 attributeMap: dojo.delegate(dijit.form._FormValueWidget.prototype.attributeMap, {
24965 rows:"textbox", cols: "textbox"
24966 }),
24967
24968 // rows: Number
24969 // The number of rows of text.
24970 rows: "3",
24971
24972 // rows: Number
24973 // The number of characters per line.
24974 cols: "20",
24975
24976 templateString: "<textarea ${!nameAttrSetting} dojoAttachPoint='focusNode,containerNode,textbox' autocomplete='off'></textarea>",
24977
24978 postMixInProperties: function(){
24979 // Copy value from srcNodeRef, unless user specified a value explicitly (or there is no srcNodeRef)
24980 if(!this.value && this.srcNodeRef){
24981 this.value = this.srcNodeRef.value;
24982 }
24983 this.inherited(arguments);
24984 },
24985
24986 filter: function(/*String*/ value){
24987 // Override TextBox.filter to deal with newlines... specifically (IIRC) this is for IE which writes newlines
24988 // as \r\n instead of just \n
24989 if(value){
24990 value = value.replace(/\r/g,"");
24991 }
24992 return this.inherited(arguments);
24993 },
24994
24995 postCreate: function(){
24996 this.inherited(arguments);
24997 if(dojo.isIE && this.cols){ // attribute selectors is not supported in IE6
24998 dojo.addClass(this.textbox, "dijitTextAreaCols");
24999 }
25000 },
25001
25002 _previousValue: "",
25003 _onInput: function(/*Event?*/ e){
25004 // Override TextBox._onInput() to enforce maxLength restriction
25005 if(this.maxLength){
25006 var maxLength = parseInt(this.maxLength);
25007 var value = this.textbox.value.replace(/\r/g,'');
25008 var overflow = value.length - maxLength;
25009 if(overflow > 0){
25010 if(e){ dojo.stopEvent(e); }
25011 var textarea = this.textbox;
25012 if(textarea.selectionStart){
25013 var pos = textarea.selectionStart;
25014 var cr = 0;
25015 if(dojo.isOpera){
25016 cr = (this.textbox.value.substring(0,pos).match(/\r/g) || []).length;
25017 }
25018 this.textbox.value = value.substring(0,pos-overflow-cr)+value.substring(pos-cr);
25019 textarea.setSelectionRange(pos-overflow, pos-overflow);
25020 }else if(dojo.doc.selection){ //IE
25021 textarea.focus();
25022 var range = dojo.doc.selection.createRange();
25023 // delete overflow characters
25024 range.moveStart("character", -overflow);
25025 range.text = '';
25026 // show cursor
25027 range.select();
25028 }
25029 }
25030 this._previousValue = this.textbox.value;
25031 }
25032 this.inherited(arguments);
25033 }
25034});
25035
25036}
25037
25038if(!dojo._hasResource["dijit.form.Textarea"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
25039dojo._hasResource["dijit.form.Textarea"] = true;
25040dojo.provide("dijit.form.Textarea");
25041
25042
25043
25044dojo.declare(
25045 "dijit.form.Textarea",
25046 dijit.form.SimpleTextarea,
25047 {
25048 // summary:
25049 // A textarea widget that adjusts it's height according to the amount of data.
25050 //
25051 // description:
25052 // A textarea that dynamically expands/contracts (changing it's height) as
25053 // the user types, to display all the text without requiring a scroll bar.
25054 //
25055 // Takes nearly all the parameters (name, value, etc.) that a vanilla textarea takes.
25056 // Rows is not supported since this widget adjusts the height.
25057 //
25058 // example:
25059 // | <textarea dojoType="dijit.form.TextArea">...</textarea>
25060
25061
25062 // Override SimpleTextArea.cols to default to width:100%, for backward compatibility
25063 cols: "",
25064
25065 _previousNewlines: 0,
25066 _strictMode: (dojo.doc.compatMode != 'BackCompat'), // not the same as !dojo.isQuirks
25067
25068 _getHeight: function(textarea){
25069 var newH = textarea.scrollHeight;
25070 if(dojo.isIE){
25071 newH += textarea.offsetHeight - textarea.clientHeight - ((dojo.isIE < 8 && this._strictMode) ? dojo._getPadBorderExtents(textarea).h : 0);
25072 }else if(dojo.isMoz){
25073 newH += textarea.offsetHeight - textarea.clientHeight; // creates room for horizontal scrollbar
25074 }else if(dojo.isWebKit && !(dojo.isSafari < 4)){ // Safari 4.0 && Chrome
25075 newH += dojo._getBorderExtents(textarea).h;
25076 }else{ // Safari 3.x and Opera 9.6
25077 newH += dojo._getPadBorderExtents(textarea).h;
25078 }
25079 return newH;
25080 },
25081
25082 _estimateHeight: function(textarea){
25083 // summary:
25084 // Approximate the height when the textarea is invisible with the number of lines in the text.
25085 // Fails when someone calls setValue with a long wrapping line, but the layout fixes itself when the user clicks inside so . . .
25086 // In IE, the resize event is supposed to fire when the textarea becomes visible again and that will correct the size automatically.
25087 //
25088 textarea.style.maxHeight = "";
25089 textarea.style.height = "auto";
25090 // #rows = #newlines+1
25091 // Note: on Moz, the following #rows appears to be 1 too many.
25092 // Actually, Moz is reserving room for the scrollbar.
25093 // If you increase the font size, this behavior becomes readily apparent as the last line gets cut off without the +1.
25094 textarea.rows = (textarea.value.match(/\n/g) || []).length + 1;
25095 },
25096
25097 _needsHelpShrinking: dojo.isMoz || dojo.isWebKit,
25098
25099 _onInput: function(){
25100 // Override SimpleTextArea._onInput() to deal with height adjustment
25101 this.inherited(arguments);
25102 if(this._busyResizing){ return; }
25103 this._busyResizing = true;
25104 var textarea = this.textbox;
25105 if(textarea.scrollHeight && textarea.offsetHeight && textarea.clientHeight){
25106 var newH = this._getHeight(textarea) + "px";
25107 if(textarea.style.height != newH){
25108 textarea.style.maxHeight = textarea.style.height = newH;
25109 }
25110 if(this._needsHelpShrinking){
25111 if(this._setTimeoutHandle){
25112 clearTimeout(this._setTimeoutHandle);
25113 }
25114 this._setTimeoutHandle = setTimeout(dojo.hitch(this, "_shrink"), 0); // try to collapse multiple shrinks into 1
25115 }
25116 }else{
25117 // hidden content of unknown size
25118 this._estimateHeight(textarea);
25119 }
25120 this._busyResizing = false;
25121 },
25122
25123 _busyResizing: false,
25124 _shrink: function(){
25125 // grow paddingBottom to see if scrollHeight shrinks (when it is unneccesarily big)
25126 this._setTimeoutHandle = null;
25127 if(this._needsHelpShrinking && !this._busyResizing){
25128 this._busyResizing = true;
25129 var textarea = this.textbox;
25130 var empty = false;
25131 if(textarea.value == ''){
25132 textarea.value = ' '; // prevent collapse all the way back to 0
25133 empty = true;
25134 }
25135 var scrollHeight = textarea.scrollHeight;
25136 if(!scrollHeight){
25137 this._estimateHeight(textarea);
25138 }else{
25139 var oldPadding = textarea.style.paddingBottom;
25140 var newPadding = dojo._getPadExtents(textarea);
25141 newPadding = newPadding.h - newPadding.t;
25142 textarea.style.paddingBottom = newPadding + 1 + "px"; // tweak padding to see if height can be reduced
25143 var newH = this._getHeight(textarea) - 1 + "px"; // see if the height changed by the 1px added
25144 if(textarea.style.maxHeight != newH){ // if can be reduced, so now try a big chunk
25145 textarea.style.paddingBottom = newPadding + scrollHeight + "px";
25146 textarea.scrollTop = 0;
25147 textarea.style.maxHeight = this._getHeight(textarea) - scrollHeight + "px"; // scrollHeight is the added padding
25148 }
25149 textarea.style.paddingBottom = oldPadding;
25150 }
25151 if(empty){
25152 textarea.value = '';
25153 }
25154 this._busyResizing = false;
25155 }
25156 },
25157
25158 resize: function(){
25159 // summary:
25160 // Resizes the textarea vertically (should be called after a style/value change)
25161 this._onInput();
25162 },
25163
25164 _setValueAttr: function(){
25165 this.inherited(arguments);
25166 this.resize();
25167 },
25168
25169 postCreate: function(){
25170 this.inherited(arguments);
25171 // tweak textarea style to reduce browser differences
25172 dojo.style(this.textbox, { overflowY: 'hidden', overflowX: 'auto', boxSizing: 'border-box', MsBoxSizing: 'border-box', WebkitBoxSizing: 'border-box', MozBoxSizing: 'border-box' });
25173 this.connect(this.textbox, "onscroll", this._onInput);
25174 this.connect(this.textbox, "onresize", this._onInput);
25175 this.connect(this.textbox, "onfocus", this._onInput); // useful when a previous estimate was off a bit
25176 this._setTimeoutHandle = setTimeout(dojo.hitch(this, "resize"), 0);
25177 },
25178
25179 uninitialize: function(){
25180 if(this._setTimeoutHandle){
25181 clearTimeout(this._setTimeoutHandle);
25182 }
25183 this.inherited(arguments);
25184 }
25185});
25186
25187}
25188
25189if(!dojo._hasResource["dijit.layout.StackController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
25190dojo._hasResource["dijit.layout.StackController"] = true;
25191dojo.provide("dijit.layout.StackController");
25192
25193
25194
25195
25196
25197
25198
25199dojo.declare(
25200 "dijit.layout.StackController",
25201 [dijit._Widget, dijit._Templated, dijit._Container],
25202 {
25203 // summary:
25204 // Set of buttons to select a page in a page list.
25205 // description:
25206 // Monitors the specified StackContainer, and whenever a page is
25207 // added, deleted, or selected, updates itself accordingly.
25208
25209 templateString: "<span wairole='tablist' dojoAttachEvent='onkeypress' class='dijitStackController'></span>",
25210
25211 // containerId: [const] String
25212 // The id of the page container that I point to
25213 containerId: "",
25214
25215 // buttonWidget: [const] String
25216 // The name of the button widget to create to correspond to each page
25217 buttonWidget: "dijit.layout._StackButton",
25218
25219 postCreate: function(){
25220 dijit.setWaiRole(this.domNode, "tablist");
25221
25222 this.pane2button = {}; // mapping from pane id to buttons
25223 this.pane2handles = {}; // mapping from pane id to this.connect() handles
25224
25225 // Listen to notifications from StackContainer
25226 this.subscribe(this.containerId+"-startup", "onStartup");
25227 this.subscribe(this.containerId+"-addChild", "onAddChild");
25228 this.subscribe(this.containerId+"-removeChild", "onRemoveChild");
25229 this.subscribe(this.containerId+"-selectChild", "onSelectChild");
25230 this.subscribe(this.containerId+"-containerKeyPress", "onContainerKeyPress");
25231 },
25232
25233 onStartup: function(/*Object*/ info){
25234 // summary:
25235 // Called after StackContainer has finished initializing
25236 // tags:
25237 // private
25238 dojo.forEach(info.children, this.onAddChild, this);
25239 if(info.selected){
25240 // Show button corresponding to selected pane (unless selected
25241 // is null because there are no panes)
25242 this.onSelectChild(info.selected);
25243 }
25244 },
25245
25246 destroy: function(){
25247 for(var pane in this.pane2button){
25248 this.onRemoveChild(dijit.byId(pane));
25249 }
25250 this.inherited(arguments);
25251 },
25252
25253 onAddChild: function(/*dijit._Widget*/ page, /*Integer?*/ insertIndex){
25254 // summary:
25255 // Called whenever a page is added to the container.
25256 // Create button corresponding to the page.
25257 // tags:
25258 // private
25259
25260 // create an instance of the button widget
25261 var cls = dojo.getObject(this.buttonWidget);
25262 var button = new cls({
25263 id: this.id + "_" + page.id,
25264 label: page.title,
25265 dir: page.dir,
25266 lang: page.lang,
25267 showLabel: page.showTitle,
25268 iconClass: page.iconClass,
25269 closeButton: page.closable,
25270 title: page.tooltip
25271 });
25272 dijit.setWaiState(button.focusNode,"selected", "false");
25273 this.pane2handles[page.id] = [
25274 this.connect(page, 'set', function(name, value){
25275 var buttonAttr = {
25276 title: 'label',
25277 showTitle: 'showLabel',
25278 iconClass: 'iconClass',
25279 closable: 'closeButton',
25280 tooltip: 'title'
25281 }[name];
25282 if(buttonAttr){
25283 button.set(buttonAttr, value);
25284 }
25285 }),
25286 this.connect(button, 'onClick', dojo.hitch(this,"onButtonClick", page)),
25287 this.connect(button, 'onClickCloseButton', dojo.hitch(this,"onCloseButtonClick", page))
25288 ];
25289 this.addChild(button, insertIndex);
25290 this.pane2button[page.id] = button;
25291 page.controlButton = button; // this value might be overwritten if two tabs point to same container
25292 if(!this._currentChild){ // put the first child into the tab order
25293 button.focusNode.setAttribute("tabIndex", "0");
25294 dijit.setWaiState(button.focusNode, "selected", "true");
25295 this._currentChild = page;
25296 }
25297 // make sure all tabs have the same length
25298 if(!this.isLeftToRight() && dojo.isIE && this._rectifyRtlTabList){
25299 this._rectifyRtlTabList();
25300 }
25301 },
25302
25303 onRemoveChild: function(/*dijit._Widget*/ page){
25304 // summary:
25305 // Called whenever a page is removed from the container.
25306 // Remove the button corresponding to the page.
25307 // tags:
25308 // private
25309
25310 if(this._currentChild === page){ this._currentChild = null; }
25311 dojo.forEach(this.pane2handles[page.id], this.disconnect, this);
25312 delete this.pane2handles[page.id];
25313 var button = this.pane2button[page.id];
25314 if(button){
25315 this.removeChild(button);
25316 delete this.pane2button[page.id];
25317 button.destroy();
25318 }
25319 delete page.controlButton;
25320 },
25321
25322 onSelectChild: function(/*dijit._Widget*/ page){
25323 // summary:
25324 // Called when a page has been selected in the StackContainer, either by me or by another StackController
25325 // tags:
25326 // private
25327
25328 if(!page){ return; }
25329
25330 if(this._currentChild){
25331 var oldButton=this.pane2button[this._currentChild.id];
25332 oldButton.set('checked', false);
25333 dijit.setWaiState(oldButton.focusNode, "selected", "false");
25334 oldButton.focusNode.setAttribute("tabIndex", "-1");
25335 }
25336
25337 var newButton=this.pane2button[page.id];
25338 newButton.set('checked', true);
25339 dijit.setWaiState(newButton.focusNode, "selected", "true");
25340 this._currentChild = page;
25341 newButton.focusNode.setAttribute("tabIndex", "0");
25342 var container = dijit.byId(this.containerId);
25343 dijit.setWaiState(container.containerNode, "labelledby", newButton.id);
25344 },
25345
25346 onButtonClick: function(/*dijit._Widget*/ page){
25347 // summary:
25348 // Called whenever one of my child buttons is pressed in an attempt to select a page
25349 // tags:
25350 // private
25351
25352 var container = dijit.byId(this.containerId);
25353 container.selectChild(page);
25354 },
25355
25356 onCloseButtonClick: function(/*dijit._Widget*/ page){
25357 // summary:
25358 // Called whenever one of my child buttons [X] is pressed in an attempt to close a page
25359 // tags:
25360 // private
25361
25362 var container = dijit.byId(this.containerId);
25363 container.closeChild(page);
25364 if(this._currentChild){
25365 var b = this.pane2button[this._currentChild.id];
25366 if(b){
25367 dijit.focus(b.focusNode || b.domNode);
25368 }
25369 }
25370 },
25371
25372 // TODO: this is a bit redundant with forward, back api in StackContainer
25373 adjacent: function(/*Boolean*/ forward){
25374 // summary:
25375 // Helper for onkeypress to find next/previous button
25376 // tags:
25377 // private
25378
25379 if(!this.isLeftToRight() && (!this.tabPosition || /top|bottom/.test(this.tabPosition))){ forward = !forward; }
25380 // find currently focused button in children array
25381 var children = this.getChildren();
25382 var current = dojo.indexOf(children, this.pane2button[this._currentChild.id]);
25383 // pick next button to focus on
25384 var offset = forward ? 1 : children.length - 1;
25385 return children[ (current + offset) % children.length ]; // dijit._Widget
25386 },
25387
25388 onkeypress: function(/*Event*/ e){
25389 // summary:
25390 // Handle keystrokes on the page list, for advancing to next/previous button
25391 // and closing the current page if the page is closable.
25392 // tags:
25393 // private
25394
25395 if(this.disabled || e.altKey ){ return; }
25396 var forward = null;
25397 if(e.ctrlKey || !e._djpage){
25398 var k = dojo.keys;
25399 switch(e.charOrCode){
25400 case k.LEFT_ARROW:
25401 case k.UP_ARROW:
25402 if(!e._djpage){ forward = false; }
25403 break;
25404 case k.PAGE_UP:
25405 if(e.ctrlKey){ forward = false; }
25406 break;
25407 case k.RIGHT_ARROW:
25408 case k.DOWN_ARROW:
25409 if(!e._djpage){ forward = true; }
25410 break;
25411 case k.PAGE_DOWN:
25412 if(e.ctrlKey){ forward = true; }
25413 break;
25414 case k.DELETE:
25415 if(this._currentChild.closable){
25416 this.onCloseButtonClick(this._currentChild);
25417 }
25418 dojo.stopEvent(e);
25419 break;
25420 default:
25421 if(e.ctrlKey){
25422 if(e.charOrCode === k.TAB){
25423 this.adjacent(!e.shiftKey).onClick();
25424 dojo.stopEvent(e);
25425 }else if(e.charOrCode == "w"){
25426 if(this._currentChild.closable){
25427 this.onCloseButtonClick(this._currentChild);
25428 }
25429 dojo.stopEvent(e); // avoid browser tab closing.
25430 }
25431 }
25432 }
25433 // handle page navigation
25434 if(forward !== null){
25435 this.adjacent(forward).onClick();
25436 dojo.stopEvent(e);
25437 }
25438 }
25439 },
25440
25441 onContainerKeyPress: function(/*Object*/ info){
25442 // summary:
25443 // Called when there was a keypress on the container
25444 // tags:
25445 // private
25446 info.e._djpage = info.page;
25447 this.onkeypress(info.e);
25448 }
25449 });
25450
25451
25452dojo.declare("dijit.layout._StackButton",
25453 dijit.form.ToggleButton,
25454 {
25455 // summary:
25456 // Internal widget used by StackContainer.
25457 // description:
25458 // The button-like or tab-like object you click to select or delete a page
25459 // tags:
25460 // private
25461
25462 // Override _FormWidget.tabIndex.
25463 // StackContainer buttons are not in the tab order by default.
25464 // Probably we should be calling this.startupKeyNavChildren() instead.
25465 tabIndex: "-1",
25466
25467 postCreate: function(/*Event*/ evt){
25468 dijit.setWaiRole((this.focusNode || this.domNode), "tab");
25469 this.inherited(arguments);
25470 },
25471
25472 onClick: function(/*Event*/ evt){
25473 // summary:
25474 // This is for TabContainer where the tabs are <span> rather than button,
25475 // so need to set focus explicitly (on some browsers)
25476 // Note that you shouldn't override this method, but you can connect to it.
25477 dijit.focus(this.focusNode);
25478
25479 // ... now let StackController catch the event and tell me what to do
25480 },
25481
25482 onClickCloseButton: function(/*Event*/ evt){
25483 // summary:
25484 // StackContainer connects to this function; if your widget contains a close button
25485 // then clicking it should call this function.
25486 // Note that you shouldn't override this method, but you can connect to it.
25487 evt.stopPropagation();
25488 }
25489 });
25490
25491
25492}
25493
25494if(!dojo._hasResource["dijit.layout.StackContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
25495dojo._hasResource["dijit.layout.StackContainer"] = true;
25496dojo.provide("dijit.layout.StackContainer");
25497
25498
25499
25500
25501
25502
25503dojo.declare(
25504 "dijit.layout.StackContainer",
25505 dijit.layout._LayoutWidget,
25506 {
25507 // summary:
25508 // A container that has multiple children, but shows only
25509 // one child at a time
25510 //
25511 // description:
25512 // A container for widgets (ContentPanes, for example) That displays
25513 // only one Widget at a time.
25514 //
25515 // Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild
25516 //
25517 // Can be base class for container, Wizard, Show, etc.
25518
25519 // doLayout: Boolean
25520 // If true, change the size of my currently displayed child to match my size
25521 doLayout: true,
25522
25523 // persist: Boolean
25524 // Remembers the selected child across sessions
25525 persist: false,
25526
25527 baseClass: "dijitStackContainer",
25528
25529/*=====
25530 // selectedChildWidget: [readonly] dijit._Widget
25531 // References the currently selected child widget, if any.
25532 // Adjust selected child with selectChild() method.
25533 selectedChildWidget: null,
25534=====*/
25535
25536 postCreate: function(){
25537 this.inherited(arguments);
25538 dojo.addClass(this.domNode, "dijitLayoutContainer");
25539 dijit.setWaiRole(this.containerNode, "tabpanel");
25540 this.connect(this.domNode, "onkeypress", this._onKeyPress);
25541 },
25542
25543 startup: function(){
25544 if(this._started){ return; }
25545
25546 var children = this.getChildren();
25547
25548 // Setup each page panel to be initially hidden
25549 dojo.forEach(children, this._setupChild, this);
25550
25551 // Figure out which child to initially display, defaulting to first one
25552 if(this.persist){
25553 this.selectedChildWidget = dijit.byId(dojo.cookie(this.id + "_selectedChild"));
25554 }else{
25555 dojo.some(children, function(child){
25556 if(child.selected){
25557 this.selectedChildWidget = child;
25558 }
25559 return child.selected;
25560 }, this);
25561 }
25562 var selected = this.selectedChildWidget;
25563 if(!selected && children[0]){
25564 selected = this.selectedChildWidget = children[0];
25565 selected.selected = true;
25566 }
25567
25568 // Publish information about myself so any StackControllers can initialize.
25569 // This needs to happen before this.inherited(arguments) so that for
25570 // TabContainer, this._contentBox doesn't include the space for the tab labels.
25571 dojo.publish(this.id+"-startup", [{children: children, selected: selected}]);
25572
25573 // Startup each child widget, and do initial layout like setting this._contentBox,
25574 // then calls this.resize() which does the initial sizing on the selected child.
25575 this.inherited(arguments);
25576 },
25577
25578 resize: function(){
25579 // Resize is called when we are first made visible (it's called from startup()
25580 // if we are initially visible). If this is the first time we've been made
25581 // visible then show our first child.
25582 var selected = this.selectedChildWidget;
25583 if(selected && !this._hasBeenShown){
25584 this._hasBeenShown = true;
25585 this._showChild(selected);
25586 }
25587 this.inherited(arguments);
25588 },
25589
25590 _setupChild: function(/*dijit._Widget*/ child){
25591 // Overrides _LayoutWidget._setupChild()
25592
25593 this.inherited(arguments);
25594
25595 dojo.removeClass(child.domNode, "dijitVisible");
25596 dojo.addClass(child.domNode, "dijitHidden");
25597
25598 // remove the title attribute so it doesn't show up when i hover
25599 // over a node
25600 child.domNode.title = "";
25601 },
25602
25603 addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
25604 // Overrides _Container.addChild() to do layout and publish events
25605
25606 this.inherited(arguments);
25607
25608 if(this._started){
25609 dojo.publish(this.id+"-addChild", [child, insertIndex]);
25610
25611 // in case the tab titles have overflowed from one line to two lines
25612 // (or, if this if first child, from zero lines to one line)
25613 // TODO: w/ScrollingTabController this is no longer necessary, although
25614 // ScrollTabController.resize() does need to get called to show/hide
25615 // the navigation buttons as appropriate, but that's handled in ScrollingTabController.onAddChild()
25616 this.layout();
25617
25618 // if this is the first child, then select it
25619 if(!this.selectedChildWidget){
25620 this.selectChild(child);
25621 }
25622 }
25623 },
25624
25625 removeChild: function(/*dijit._Widget*/ page){
25626 // Overrides _Container.removeChild() to do layout and publish events
25627
25628 this.inherited(arguments);
25629
25630 if(this._started){
25631 // this will notify any tablists to remove a button; do this first because it may affect sizing
25632 dojo.publish(this.id + "-removeChild", [page]);
25633 }
25634
25635 // If we are being destroyed than don't run the code below (to select another page), because we are deleting
25636 // every page one by one
25637 if(this._beingDestroyed){ return; }
25638
25639 // Select new page to display, also updating TabController to show the respective tab.
25640 // Do this before layout call because it can affect the height of the TabController.
25641 if(this.selectedChildWidget === page){
25642 this.selectedChildWidget = undefined;
25643 if(this._started){
25644 var children = this.getChildren();
25645 if(children.length){
25646 this.selectChild(children[0]);
25647 }
25648 }
25649 }
25650
25651 if(this._started){
25652 // In case the tab titles now take up one line instead of two lines
25653 // (note though that ScrollingTabController never overflows to multiple lines),
25654 // or the height has changed slightly because of addition/removal of tab which close icon
25655 this.layout();
25656 }
25657 },
25658
25659 selectChild: function(/*dijit._Widget|String*/ page, /*Boolean*/ animate){
25660 // summary:
25661 // Show the given widget (which must be one of my children)
25662 // page:
25663 // Reference to child widget or id of child widget
25664
25665 page = dijit.byId(page);
25666
25667 if(this.selectedChildWidget != page){
25668 // Deselect old page and select new one
25669 this._transition(page, this.selectedChildWidget, animate);
25670 this.selectedChildWidget = page;
25671 dojo.publish(this.id+"-selectChild", [page]);
25672
25673 if(this.persist){
25674 dojo.cookie(this.id + "_selectedChild", this.selectedChildWidget.id);
25675 }
25676 }
25677 },
25678
25679 _transition: function(/*dijit._Widget*/newWidget, /*dijit._Widget*/oldWidget){
25680 // summary:
25681 // Hide the old widget and display the new widget.
25682 // Subclasses should override this.
25683 // tags:
25684 // protected extension
25685 if(oldWidget){
25686 this._hideChild(oldWidget);
25687 }
25688 this._showChild(newWidget);
25689
25690 // Size the new widget, in case this is the first time it's being shown,
25691 // or I have been resized since the last time it was shown.
25692 // Note that page must be visible for resizing to work.
25693 if(newWidget.resize){
25694 if(this.doLayout){
25695 newWidget.resize(this._containerContentBox || this._contentBox);
25696 }else{
25697 // the child should pick it's own size but we still need to call resize()
25698 // (with no arguments) to let the widget lay itself out
25699 newWidget.resize();
25700 }
25701 }
25702 },
25703
25704 _adjacent: function(/*Boolean*/ forward){
25705 // summary:
25706 // Gets the next/previous child widget in this container from the current selection.
25707 var children = this.getChildren();
25708 var index = dojo.indexOf(children, this.selectedChildWidget);
25709 index += forward ? 1 : children.length - 1;
25710 return children[ index % children.length ]; // dijit._Widget
25711 },
25712
25713 forward: function(){
25714 // summary:
25715 // Advance to next page.
25716 this.selectChild(this._adjacent(true), true);
25717 },
25718
25719 back: function(){
25720 // summary:
25721 // Go back to previous page.
25722 this.selectChild(this._adjacent(false), true);
25723 },
25724
25725 _onKeyPress: function(e){
25726 dojo.publish(this.id+"-containerKeyPress", [{ e: e, page: this}]);
25727 },
25728
25729 layout: function(){
25730 // Implement _LayoutWidget.layout() virtual method.
25731 if(this.doLayout && this.selectedChildWidget && this.selectedChildWidget.resize){
25732 this.selectedChildWidget.resize(this._containerContentBox || this._contentBox);
25733 }
25734 },
25735
25736 _showChild: function(/*dijit._Widget*/ page){
25737 // summary:
25738 // Show the specified child by changing it's CSS, and call _onShow()/onShow() so
25739 // it can do any updates it needs regarding loading href's etc.
25740 var children = this.getChildren();
25741 page.isFirstChild = (page == children[0]);
25742 page.isLastChild = (page == children[children.length-1]);
25743 page.selected = true;
25744
25745 dojo.removeClass(page.domNode, "dijitHidden");
25746 dojo.addClass(page.domNode, "dijitVisible");
25747
25748 page._onShow();
25749 },
25750
25751 _hideChild: function(/*dijit._Widget*/ page){
25752 // summary:
25753 // Hide the specified child by changing it's CSS, and call _onHide() so
25754 // it's notified.
25755 page.selected=false;
25756 dojo.removeClass(page.domNode, "dijitVisible");
25757 dojo.addClass(page.domNode, "dijitHidden");
25758
25759 page.onHide();
25760 },
25761
25762 closeChild: function(/*dijit._Widget*/ page){
25763 // summary:
25764 // Callback when user clicks the [X] to remove a page.
25765 // If onClose() returns true then remove and destroy the child.
25766 // tags:
25767 // private
25768 var remove = page.onClose(this, page);
25769 if(remove){
25770 this.removeChild(page);
25771 // makes sure we can clean up executeScripts in ContentPane onUnLoad
25772 page.destroyRecursive();
25773 }
25774 },
25775
25776 destroyDescendants: function(/*Boolean*/preserveDom){
25777 dojo.forEach(this.getChildren(), function(child){
25778 this.removeChild(child);
25779 child.destroyRecursive(preserveDom);
25780 }, this);
25781 }
25782});
25783
25784// For back-compat, remove for 2.0
25785
25786
25787
25788// These arguments can be specified for the children of a StackContainer.
25789// Since any widget can be specified as a StackContainer child, mix them
25790// into the base widget class. (This is a hack, but it's effective.)
25791dojo.extend(dijit._Widget, {
25792 // selected: Boolean
25793 // Parameter for children of `dijit.layout.StackContainer` or subclasses.
25794 // Specifies that this widget should be the initially displayed pane.
25795 // Note: to change the selected child use `dijit.layout.StackContainer.selectChild`
25796 selected: false,
25797
25798 // closable: Boolean
25799 // Parameter for children of `dijit.layout.StackContainer` or subclasses.
25800 // True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
25801 closable: false,
25802
25803 // iconClass: String
25804 // Parameter for children of `dijit.layout.StackContainer` or subclasses.
25805 // CSS Class specifying icon to use in label associated with this pane.
25806 iconClass: "",
25807
25808 // showTitle: Boolean
25809 // Parameter for children of `dijit.layout.StackContainer` or subclasses.
25810 // When true, display title of this widget as tab label etc., rather than just using
25811 // icon specified in iconClass
25812 showTitle: true
25813});
25814
25815}
25816
25817if(!dojo._hasResource["dijit.layout.AccordionPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
25818dojo._hasResource["dijit.layout.AccordionPane"] = true;
25819dojo.provide("dijit.layout.AccordionPane");
25820
25821
25822
25823dojo.declare("dijit.layout.AccordionPane", dijit.layout.ContentPane, {
25824 // summary:
25825 // Deprecated widget. Use `dijit.layout.ContentPane` instead.
25826 // tags:
25827 // deprecated
25828
25829 constructor: function(){
25830 dojo.deprecated("dijit.layout.AccordionPane deprecated, use ContentPane instead", "", "2.0");
25831 },
25832
25833 onSelected: function(){
25834 // summary:
25835 // called when this pane is selected
25836 }
25837});
25838
25839}
25840
25841if(!dojo._hasResource["dijit.layout.AccordionContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
25842dojo._hasResource["dijit.layout.AccordionContainer"] = true;
25843dojo.provide("dijit.layout.AccordionContainer");
25844
25845
25846
25847
25848
25849
25850
25851
25852
25853 // for back compat, remove for 2.0
25854
25855dojo.declare(
25856 "dijit.layout.AccordionContainer",
25857 dijit.layout.StackContainer,
25858 {
25859 // summary:
25860 // Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time,
25861 // and switching between panes is visualized by sliding the other panes up/down.
25862 // example:
25863 // | <div dojoType="dijit.layout.AccordionContainer">
25864 // | <div dojoType="dijit.layout.ContentPane" title="pane 1">
25865 // | </div>
25866 // | <div dojoType="dijit.layout.ContentPane" title="pane 2">
25867 // | <p>This is some text</p>
25868 // | </div>
25869 // | </div>
25870
25871 // duration: Integer
25872 // Amount of time (in ms) it takes to slide panes
25873 duration: dijit.defaultDuration,
25874
25875 // buttonWidget: [const] String
25876 // The name of the widget used to display the title of each pane
25877 buttonWidget: "dijit.layout._AccordionButton",
25878
25879 // _verticalSpace: Number
25880 // Pixels of space available for the open pane
25881 // (my content box size minus the cumulative size of all the title bars)
25882 _verticalSpace: 0,
25883
25884 baseClass: "dijitAccordionContainer",
25885
25886 postCreate: function(){
25887 this.domNode.style.overflow = "hidden";
25888 this.inherited(arguments);
25889 dijit.setWaiRole(this.domNode, "tablist");
25890 },
25891
25892 startup: function(){
25893 if(this._started){ return; }
25894 this.inherited(arguments);
25895 if(this.selectedChildWidget){
25896 var style = this.selectedChildWidget.containerNode.style;
25897 style.display = "";
25898 style.overflow = "auto";
25899 this.selectedChildWidget._wrapperWidget.set("selected", true);
25900 }
25901 },
25902
25903 _getTargetHeight: function(/* Node */ node){
25904 // summary:
25905 // For the given node, returns the height that should be
25906 // set to achieve our vertical space (subtract any padding
25907 // we may have).
25908 //
25909 // This is used by the animations.
25910 //
25911 // TODO: I don't think this works correctly in IE quirks when an elements
25912 // style.height including padding and borders
25913 var cs = dojo.getComputedStyle(node);
25914 return Math.max(this._verticalSpace - dojo._getPadBorderExtents(node, cs).h - dojo._getMarginExtents(node, cs).h, 0);
25915 },
25916
25917 layout: function(){
25918 // Implement _LayoutWidget.layout() virtual method.
25919 // Set the height of the open pane based on what room remains.
25920
25921 var openPane = this.selectedChildWidget;
25922
25923 if(!openPane){ return;}
25924
25925 var openPaneContainer = openPane._wrapperWidget.domNode,
25926 openPaneContainerMargin = dojo._getMarginExtents(openPaneContainer),
25927 openPaneContainerPadBorder = dojo._getPadBorderExtents(openPaneContainer),
25928 mySize = this._contentBox;
25929
25930 // get cumulative height of all the unselected title bars
25931 var totalCollapsedHeight = 0;
25932 dojo.forEach(this.getChildren(), function(child){
25933 if(child != openPane){
25934 totalCollapsedHeight += dojo.marginBox(child._wrapperWidget.domNode).h;
25935 }
25936 });
25937 this._verticalSpace = mySize.h - totalCollapsedHeight - openPaneContainerMargin.h
25938 - openPaneContainerPadBorder.h - openPane._buttonWidget.getTitleHeight();
25939
25940 // Memo size to make displayed child
25941 this._containerContentBox = {
25942 h: this._verticalSpace,
25943 w: this._contentBox.w - openPaneContainerMargin.w - openPaneContainerPadBorder.w
25944 };
25945
25946 if(openPane){
25947 openPane.resize(this._containerContentBox);
25948 }
25949 },
25950
25951 _setupChild: function(child){
25952 // Overrides _LayoutWidget._setupChild().
25953 // Put wrapper widget around the child widget, showing title
25954
25955 child._wrapperWidget = new dijit.layout._AccordionInnerContainer({
25956 contentWidget: child,
25957 buttonWidget: this.buttonWidget,
25958 id: child.id + "_wrapper",
25959 dir: child.dir,
25960 lang: child.lang,
25961 parent: this
25962 });
25963
25964 this.inherited(arguments);
25965 },
25966
25967 addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
25968 if(this._started){
25969 // Adding a child to a started Accordion is complicated because children have
25970 // wrapper widgets. Default code path (calling this.inherited()) would add
25971 // the new child inside another child's wrapper.
25972
25973 // First add in child as a direct child of this AccordionContainer
25974 dojo.place(child.domNode, this.containerNode, insertIndex);
25975
25976 if(!child._started){
25977 child.startup();
25978 }
25979
25980 // Then stick the wrapper widget around the child widget
25981 this._setupChild(child);
25982
25983 // Code below copied from StackContainer
25984 dojo.publish(this.id+"-addChild", [child, insertIndex]);
25985 this.layout();
25986 if(!this.selectedChildWidget){
25987 this.selectChild(child);
25988 }
25989 }else{
25990 // We haven't been started yet so just add in the child widget directly,
25991 // and the wrapper will be created on startup()
25992 this.inherited(arguments);
25993 }
25994 },
25995
25996 removeChild: function(child){
25997 // Overrides _LayoutWidget.removeChild().
25998
25999 // destroy wrapper widget first, before StackContainer.getChildren() call
26000 child._wrapperWidget.destroy();
26001 delete child._wrapperWidget;
26002 dojo.removeClass(child.domNode, "dijitHidden");
26003
26004 this.inherited(arguments);
26005 },
26006
26007 getChildren: function(){
26008 // Overrides _Container.getChildren() to return content panes rather than internal AccordionInnerContainer panes
26009 return dojo.map(this.inherited(arguments), function(child){
26010 return child.declaredClass == "dijit.layout._AccordionInnerContainer" ? child.contentWidget : child;
26011 }, this);
26012 },
26013
26014 destroy: function(){
26015 dojo.forEach(this.getChildren(), function(child){
26016 child._wrapperWidget.destroy();
26017 });
26018 this.inherited(arguments);
26019 },
26020
26021 _transition: function(/*dijit._Widget?*/newWidget, /*dijit._Widget?*/oldWidget, /*Boolean*/ animate){
26022 // Overrides StackContainer._transition() to provide sliding of title bars etc.
26023
26024//TODO: should be able to replace this with calls to slideIn/slideOut
26025 if(this._inTransition){ return; }
26026 var animations = [];
26027 var paneHeight = this._verticalSpace;
26028 if(newWidget){
26029 newWidget._wrapperWidget.set("selected", true);
26030
26031 this._showChild(newWidget); // prepare widget to be slid in
26032
26033 // Size the new widget, in case this is the first time it's being shown,
26034 // or I have been resized since the last time it was shown.
26035 // Note that page must be visible for resizing to work.
26036 if(this.doLayout && newWidget.resize){
26037 newWidget.resize(this._containerContentBox);
26038 }
26039
26040 var newContents = newWidget.domNode;
26041 dojo.addClass(newContents, "dijitVisible");
26042 dojo.removeClass(newContents, "dijitHidden");
26043
26044 if(animate){
26045 var newContentsOverflow = newContents.style.overflow;
26046 newContents.style.overflow = "hidden";
26047 animations.push(dojo.animateProperty({
26048 node: newContents,
26049 duration: this.duration,
26050 properties: {
26051 height: { start: 1, end: this._getTargetHeight(newContents) }
26052 },
26053 onEnd: function(){
26054 newContents.style.overflow = newContentsOverflow;
26055
26056 // Kick IE to workaround layout bug, see #11415
26057 if(dojo.isIE){
26058 setTimeout(function(){
26059 dojo.removeClass(newContents.parentNode, "dijitAccordionInnerContainerFocused");
26060 setTimeout(function(){
26061 dojo.addClass(newContents.parentNode, "dijitAccordionInnerContainerFocused");
26062 }, 0);
26063 }, 0);
26064 }
26065 }
26066 }));
26067 }
26068 }
26069 if(oldWidget){
26070 oldWidget._wrapperWidget.set("selected", false);
26071 var oldContents = oldWidget.domNode;
26072 if(animate){
26073 var oldContentsOverflow = oldContents.style.overflow;
26074 oldContents.style.overflow = "hidden";
26075 animations.push(dojo.animateProperty({
26076 node: oldContents,
26077 duration: this.duration,
26078 properties: {
26079 height: { start: this._getTargetHeight(oldContents), end: 1 }
26080 },
26081 onEnd: function(){
26082 dojo.addClass(oldContents, "dijitHidden");
26083 dojo.removeClass(oldContents, "dijitVisible");
26084 oldContents.style.overflow = oldContentsOverflow;
26085 if(oldWidget.onHide){
26086 oldWidget.onHide();
26087 }
26088 }
26089 }));
26090 }else{
26091 dojo.addClass(oldContents, "dijitHidden");
26092 dojo.removeClass(oldContents, "dijitVisible");
26093 if(oldWidget.onHide){
26094 oldWidget.onHide();
26095 }
26096 }
26097 }
26098
26099 if(animate){
26100 this._inTransition = true;
26101 var combined = dojo.fx.combine(animations);
26102 combined.onEnd = dojo.hitch(this, function(){
26103 delete this._inTransition;
26104 });
26105 combined.play();
26106 }
26107 },
26108
26109 // note: we are treating the container as controller here
26110 _onKeyPress: function(/*Event*/ e, /*dijit._Widget*/ fromTitle){
26111 // summary:
26112 // Handle keypress events
26113 // description:
26114 // This is called from a handler on AccordionContainer.domNode
26115 // (setup in StackContainer), and is also called directly from
26116 // the click handler for accordion labels
26117 if(this._inTransition || this.disabled || e.altKey || !(fromTitle || e.ctrlKey)){
26118 if(this._inTransition){
26119 dojo.stopEvent(e);
26120 }
26121 return;
26122 }
26123 var k = dojo.keys,
26124 c = e.charOrCode;
26125 if((fromTitle && (c == k.LEFT_ARROW || c == k.UP_ARROW)) ||
26126 (e.ctrlKey && c == k.PAGE_UP)){
26127 this._adjacent(false)._buttonWidget._onTitleClick();
26128 dojo.stopEvent(e);
26129 }else if((fromTitle && (c == k.RIGHT_ARROW || c == k.DOWN_ARROW)) ||
26130 (e.ctrlKey && (c == k.PAGE_DOWN || c == k.TAB))){
26131 this._adjacent(true)._buttonWidget._onTitleClick();
26132 dojo.stopEvent(e);
26133 }
26134 }
26135 }
26136);
26137
26138dojo.declare("dijit.layout._AccordionInnerContainer",
26139 [dijit._Widget, dijit._CssStateMixin], {
26140 // summary:
26141 // Internal widget placed as direct child of AccordionContainer.containerNode.
26142 // When other widgets are added as children to an AccordionContainer they are wrapped in
26143 // this widget.
26144
26145 // buttonWidget: String
26146 // Name of class to use to instantiate title
26147 // (Wish we didn't have a separate widget for just the title but maintaining it
26148 // for backwards compatibility, is it worth it?)
26149/*=====
26150 buttonWidget: null,
26151=====*/
26152 // contentWidget: dijit._Widget
26153 // Pointer to the real child widget
26154/*=====
26155 contentWidget: null,
26156=====*/
26157
26158 baseClass: "dijitAccordionInnerContainer",
26159
26160 // tell nested layout widget that we will take care of sizing
26161 isContainer: true,
26162 isLayoutContainer: true,
26163
26164 buildRendering: function(){
26165 // Create wrapper div, placed where the child is now
26166 this.domNode = dojo.place("<div class='" + this.baseClass + "'>", this.contentWidget.domNode, "after");
26167
26168 // wrapper div's first child is the button widget (ie, the title bar)
26169 var child = this.contentWidget,
26170 cls = dojo.getObject(this.buttonWidget);
26171 this.button = child._buttonWidget = (new cls({
26172 contentWidget: child,
26173 label: child.title,
26174 title: child.tooltip,
26175 dir: child.dir,
26176 lang: child.lang,
26177 iconClass: child.iconClass,
26178 id: child.id + "_button",
26179 parent: this.parent
26180 })).placeAt(this.domNode);
26181
26182 // and then the actual content widget (changing it from prior-sibling to last-child)
26183 dojo.place(this.contentWidget.domNode, this.domNode);
26184 },
26185
26186 postCreate: function(){
26187 this.inherited(arguments);
26188 this.connect(this.contentWidget, 'set', function(name, value){
26189 var mappedName = {title: "label", tooltip: "title", iconClass: "iconClass"}[name];
26190 if(mappedName){
26191 this.button.set(mappedName, value);
26192 }
26193 }, this);
26194 },
26195
26196 _setSelectedAttr: function(/*Boolean*/ isSelected){
26197 this.selected = isSelected;
26198 this.button.set("selected", isSelected);
26199 if(isSelected){
26200 var cw = this.contentWidget;
26201 if(cw.onSelected){ cw.onSelected(); }
26202 }
26203 },
26204
26205 startup: function(){
26206 // Called by _Container.addChild()
26207 this.contentWidget.startup();
26208 },
26209
26210 destroy: function(){
26211 this.button.destroyRecursive();
26212
26213 delete this.contentWidget._buttonWidget;
26214 delete this.contentWidget._wrapperWidget;
26215
26216 this.inherited(arguments);
26217 },
26218
26219 destroyDescendants: function(){
26220 // since getChildren isn't working for me, have to code this manually
26221 this.contentWidget.destroyRecursive();
26222 }
26223});
26224
26225dojo.declare("dijit.layout._AccordionButton",
26226 [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
26227 {
26228 // summary:
26229 // The title bar to click to open up an accordion pane.
26230 // Internal widget used by AccordionContainer.
26231 // tags:
26232 // private
26233
26234 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"),
26235 attributeMap: dojo.mixin(dojo.clone(dijit.layout.ContentPane.prototype.attributeMap), {
26236 label: {node: "titleTextNode", type: "innerHTML" },
26237 title: {node: "titleTextNode", type: "attribute", attribute: "title"},
26238 iconClass: { node: "iconNode", type: "class" }
26239 }),
26240
26241 baseClass: "dijitAccordionTitle",
26242
26243 getParent: function(){
26244 // summary:
26245 // Returns the AccordionContainer parent.
26246 // tags:
26247 // private
26248 return this.parent;
26249 },
26250
26251 postCreate: function(){
26252 this.inherited(arguments);
26253 dojo.setSelectable(this.domNode, false);
26254 var titleTextNodeId = dojo.attr(this.domNode,'id').replace(' ','_');
26255 dojo.attr(this.titleTextNode, "id", titleTextNodeId+"_title");
26256 dijit.setWaiState(this.focusNode, "labelledby", dojo.attr(this.titleTextNode, "id"));
26257 },
26258
26259 getTitleHeight: function(){
26260 // summary:
26261 // Returns the height of the title dom node.
26262 return dojo.marginBox(this.domNode).h; // Integer
26263 },
26264
26265 // TODO: maybe the parent should set these methods directly rather than forcing the code
26266 // into the button widget?
26267 _onTitleClick: function(){
26268 // summary:
26269 // Callback when someone clicks my title.
26270 var parent = this.getParent();
26271 if(!parent._inTransition){
26272 parent.selectChild(this.contentWidget, true);
26273 dijit.focus(this.focusNode);
26274 }
26275 },
26276
26277 _onTitleKeyPress: function(/*Event*/ evt){
26278 return this.getParent()._onKeyPress(evt, this.contentWidget);
26279 },
26280
26281 _setSelectedAttr: function(/*Boolean*/ isSelected){
26282 this.selected = isSelected;
26283 dijit.setWaiState(this.focusNode, "expanded", isSelected);
26284 dijit.setWaiState(this.focusNode, "selected", isSelected);
26285 this.focusNode.setAttribute("tabIndex", isSelected ? "0" : "-1");
26286 }
26287});
26288
26289}
26290
26291if(!dojo._hasResource["dijit.layout.BorderContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
26292dojo._hasResource["dijit.layout.BorderContainer"] = true;
26293dojo.provide("dijit.layout.BorderContainer");
26294
26295
26296
26297
26298dojo.declare(
26299 "dijit.layout.BorderContainer",
26300 dijit.layout._LayoutWidget,
26301{
26302 // summary:
26303 // Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides.
26304 //
26305 // description:
26306 // A BorderContainer is a box with a specified size, such as style="width: 500px; height: 500px;",
26307 // that contains a child widget marked region="center" and optionally children widgets marked
26308 // region equal to "top", "bottom", "leading", "trailing", "left" or "right".
26309 // Children along the edges will be laid out according to width or height dimensions and may
26310 // include optional splitters (splitter="true") to make them resizable by the user. The remaining
26311 // space is designated for the center region.
26312 //
26313 // NOTE: Splitters must not be more than 50 pixels in width.
26314 //
26315 // The outer size must be specified on the BorderContainer node. Width must be specified for the sides
26316 // and height for the top and bottom, respectively. No dimensions should be specified on the center;
26317 // it will fill the remaining space. Regions named "leading" and "trailing" may be used just like
26318 // "left" and "right" except that they will be reversed in right-to-left environments.
26319 //
26320 // example:
26321 // | <div dojoType="dijit.layout.BorderContainer" design="sidebar" gutters="false"
26322 // | style="width: 400px; height: 300px;">
26323 // | <div dojoType="ContentPane" region="top">header text</div>
26324 // | <div dojoType="ContentPane" region="right" splitter="true" style="width: 200px;">table of contents</div>
26325 // | <div dojoType="ContentPane" region="center">client area</div>
26326 // | </div>
26327
26328 // design: String
26329 // Which design is used for the layout:
26330 // - "headline" (default) where the top and bottom extend
26331 // the full width of the container
26332 // - "sidebar" where the left and right sides extend from top to bottom.
26333 design: "headline",
26334
26335 // gutters: Boolean
26336 // Give each pane a border and margin.
26337 // Margin determined by domNode.paddingLeft.
26338 // When false, only resizable panes have a gutter (i.e. draggable splitter) for resizing.
26339 gutters: true,
26340
26341 // liveSplitters: Boolean
26342 // Specifies whether splitters resize as you drag (true) or only upon mouseup (false)
26343 liveSplitters: true,
26344
26345 // persist: Boolean
26346 // Save splitter positions in a cookie.
26347 persist: false,
26348
26349 baseClass: "dijitBorderContainer",
26350
26351 // _splitterClass: String
26352 // Optional hook to override the default Splitter widget used by BorderContainer
26353 _splitterClass: "dijit.layout._Splitter",
26354
26355 postMixInProperties: function(){
26356 // change class name to indicate that BorderContainer is being used purely for
26357 // layout (like LayoutContainer) rather than for pretty formatting.
26358 if(!this.gutters){
26359 this.baseClass += "NoGutter";
26360 }
26361 this.inherited(arguments);
26362 },
26363
26364 postCreate: function(){
26365 this.inherited(arguments);
26366
26367 this._splitters = {};
26368 this._splitterThickness = {};
26369 },
26370
26371 startup: function(){
26372 if(this._started){ return; }
26373 dojo.forEach(this.getChildren(), this._setupChild, this);
26374 this.inherited(arguments);
26375 },
26376
26377 _setupChild: function(/*dijit._Widget*/ child){
26378 // Override _LayoutWidget._setupChild().
26379
26380 var region = child.region;
26381 if(region){
26382 this.inherited(arguments);
26383
26384 dojo.addClass(child.domNode, this.baseClass+"Pane");
26385
26386 var ltr = this.isLeftToRight();
26387 if(region == "leading"){ region = ltr ? "left" : "right"; }
26388 if(region == "trailing"){ region = ltr ? "right" : "left"; }
26389
26390 //FIXME: redundant?
26391 this["_"+region] = child.domNode;
26392 this["_"+region+"Widget"] = child;
26393
26394 // Create draggable splitter for resizing pane,
26395 // or alternately if splitter=false but BorderContainer.gutters=true then
26396 // insert dummy div just for spacing
26397 if((child.splitter || this.gutters) && !this._splitters[region]){
26398 var _Splitter = dojo.getObject(child.splitter ? this._splitterClass : "dijit.layout._Gutter");
26399 var splitter = new _Splitter({
26400 id: child.id + "_splitter",
26401 container: this,
26402 child: child,
26403 region: region,
26404 live: this.liveSplitters
26405 });
26406 splitter.isSplitter = true;
26407 this._splitters[region] = splitter.domNode;
26408 dojo.place(this._splitters[region], child.domNode, "after");
26409
26410 // Splitters arent added as Contained children, so we need to call startup explicitly
26411 splitter.startup();
26412 }
26413 child.region = region;
26414 }
26415 },
26416
26417 _computeSplitterThickness: function(region){
26418 this._splitterThickness[region] = this._splitterThickness[region] ||
26419 dojo.marginBox(this._splitters[region])[(/top|bottom/.test(region) ? 'h' : 'w')];
26420 },
26421
26422 layout: function(){
26423 // Implement _LayoutWidget.layout() virtual method.
26424 for(var region in this._splitters){ this._computeSplitterThickness(region); }
26425 this._layoutChildren();
26426 },
26427
26428 addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
26429 // Override _LayoutWidget.addChild().
26430 this.inherited(arguments);
26431 if(this._started){
26432 this.layout(); //OPT
26433 }
26434 },
26435
26436 removeChild: function(/*dijit._Widget*/ child){
26437 // Override _LayoutWidget.removeChild().
26438 var region = child.region;
26439 var splitter = this._splitters[region];
26440 if(splitter){
26441 dijit.byNode(splitter).destroy();
26442 delete this._splitters[region];
26443 delete this._splitterThickness[region];
26444 }
26445 this.inherited(arguments);
26446 delete this["_"+region];
26447 delete this["_" +region+"Widget"];
26448 if(this._started){
26449 this._layoutChildren();
26450 }
26451 dojo.removeClass(child.domNode, this.baseClass+"Pane");
26452 },
26453
26454 getChildren: function(){
26455 // Override _LayoutWidget.getChildren() to only return real children, not the splitters.
26456 return dojo.filter(this.inherited(arguments), function(widget){
26457 return !widget.isSplitter;
26458 });
26459 },
26460
26461 getSplitter: function(/*String*/region){
26462 // summary:
26463 // Returns the widget responsible for rendering the splitter associated with region
26464 var splitter = this._splitters[region];
26465 return splitter ? dijit.byNode(splitter) : null;
26466 },
26467
26468 resize: function(newSize, currentSize){
26469 // Overrides _LayoutWidget.resize().
26470
26471 // resetting potential padding to 0px to provide support for 100% width/height + padding
26472 // TODO: this hack doesn't respect the box model and is a temporary fix
26473 if(!this.cs || !this.pe){
26474 var node = this.domNode;
26475 this.cs = dojo.getComputedStyle(node);
26476 this.pe = dojo._getPadExtents(node, this.cs);
26477 this.pe.r = dojo._toPixelValue(node, this.cs.paddingRight);
26478 this.pe.b = dojo._toPixelValue(node, this.cs.paddingBottom);
26479
26480 dojo.style(node, "padding", "0px");
26481 }
26482
26483 this.inherited(arguments);
26484 },
26485
26486 _layoutChildren: function(/*String?*/changedRegion, /*Number?*/ changedRegionSize){
26487 // summary:
26488 // This is the main routine for setting size/position of each child.
26489 // description:
26490 // With no arguments, measures the height of top/bottom panes, the width
26491 // of left/right panes, and then sizes all panes accordingly.
26492 //
26493 // With changedRegion specified (as "left", "top", "bottom", or "right"),
26494 // it changes that region's width/height to changedRegionSize and
26495 // then resizes other regions that were affected.
26496 // changedRegion:
26497 // The region should be changed because splitter was dragged.
26498 // "left", "right", "top", or "bottom".
26499 // changedRegionSize:
26500 // The new width/height (in pixels) to make changedRegion
26501
26502 if(!this._borderBox || !this._borderBox.h){
26503 // We are currently hidden, or we haven't been sized by our parent yet.
26504 // Abort. Someone will resize us later.
26505 return;
26506 }
26507
26508 var sidebarLayout = (this.design == "sidebar");
26509 var topHeight = 0, bottomHeight = 0, leftWidth = 0, rightWidth = 0;
26510 var topStyle = {}, leftStyle = {}, rightStyle = {}, bottomStyle = {},
26511 centerStyle = (this._center && this._center.style) || {};
26512
26513 var changedSide = /left|right/.test(changedRegion);
26514
26515 var layoutSides = !changedRegion || (!changedSide && !sidebarLayout);
26516 var layoutTopBottom = !changedRegion || (changedSide && sidebarLayout);
26517
26518 // Ask browser for width/height of side panes.
26519 // Would be nice to cache this but height can change according to width
26520 // (because words wrap around). I don't think width will ever change though
26521 // (except when the user drags a splitter).
26522 if(this._top){
26523 topStyle = (changedRegion == "top" || layoutTopBottom) && this._top.style;
26524 topHeight = changedRegion == "top" ? changedRegionSize : dojo.marginBox(this._top).h;
26525 }
26526 if(this._left){
26527 leftStyle = (changedRegion == "left" || layoutSides) && this._left.style;
26528 leftWidth = changedRegion == "left" ? changedRegionSize : dojo.marginBox(this._left).w;
26529 }
26530 if(this._right){
26531 rightStyle = (changedRegion == "right" || layoutSides) && this._right.style;
26532 rightWidth = changedRegion == "right" ? changedRegionSize : dojo.marginBox(this._right).w;
26533 }
26534 if(this._bottom){
26535 bottomStyle = (changedRegion == "bottom" || layoutTopBottom) && this._bottom.style;
26536 bottomHeight = changedRegion == "bottom" ? changedRegionSize : dojo.marginBox(this._bottom).h;
26537 }
26538
26539 var splitters = this._splitters;
26540 var topSplitter = splitters.top, bottomSplitter = splitters.bottom,
26541 leftSplitter = splitters.left, rightSplitter = splitters.right;
26542 var splitterThickness = this._splitterThickness;
26543 var topSplitterThickness = splitterThickness.top || 0,
26544 leftSplitterThickness = splitterThickness.left || 0,
26545 rightSplitterThickness = splitterThickness.right || 0,
26546 bottomSplitterThickness = splitterThickness.bottom || 0;
26547
26548 // Check for race condition where CSS hasn't finished loading, so
26549 // the splitter width == the viewport width (#5824)
26550 if(leftSplitterThickness > 50 || rightSplitterThickness > 50){
26551 setTimeout(dojo.hitch(this, function(){
26552 // Results are invalid. Clear them out.
26553 this._splitterThickness = {};
26554
26555 for(var region in this._splitters){
26556 this._computeSplitterThickness(region);
26557 }
26558 this._layoutChildren();
26559 }), 50);
26560 return false;
26561 }
26562
26563 var pe = this.pe;
26564
26565 var splitterBounds = {
26566 left: (sidebarLayout ? leftWidth + leftSplitterThickness: 0) + pe.l + "px",
26567 right: (sidebarLayout ? rightWidth + rightSplitterThickness: 0) + pe.r + "px"
26568 };
26569
26570 if(topSplitter){
26571 dojo.mixin(topSplitter.style, splitterBounds);
26572 topSplitter.style.top = topHeight + pe.t + "px";
26573 }
26574
26575 if(bottomSplitter){
26576 dojo.mixin(bottomSplitter.style, splitterBounds);
26577 bottomSplitter.style.bottom = bottomHeight + pe.b + "px";
26578 }
26579
26580 splitterBounds = {
26581 top: (sidebarLayout ? 0 : topHeight + topSplitterThickness) + pe.t + "px",
26582 bottom: (sidebarLayout ? 0 : bottomHeight + bottomSplitterThickness) + pe.b + "px"
26583 };
26584
26585 if(leftSplitter){
26586 dojo.mixin(leftSplitter.style, splitterBounds);
26587 leftSplitter.style.left = leftWidth + pe.l + "px";
26588 }
26589
26590 if(rightSplitter){
26591 dojo.mixin(rightSplitter.style, splitterBounds);
26592 rightSplitter.style.right = rightWidth + pe.r + "px";
26593 }
26594
26595 dojo.mixin(centerStyle, {
26596 top: pe.t + topHeight + topSplitterThickness + "px",
26597 left: pe.l + leftWidth + leftSplitterThickness + "px",
26598 right: pe.r + rightWidth + rightSplitterThickness + "px",
26599 bottom: pe.b + bottomHeight + bottomSplitterThickness + "px"
26600 });
26601
26602 var bounds = {
26603 top: sidebarLayout ? pe.t + "px" : centerStyle.top,
26604 bottom: sidebarLayout ? pe.b + "px" : centerStyle.bottom
26605 };
26606 dojo.mixin(leftStyle, bounds);
26607 dojo.mixin(rightStyle, bounds);
26608 leftStyle.left = pe.l + "px"; rightStyle.right = pe.r + "px"; topStyle.top = pe.t + "px"; bottomStyle.bottom = pe.b + "px";
26609 if(sidebarLayout){
26610 topStyle.left = bottomStyle.left = leftWidth + leftSplitterThickness + pe.l + "px";
26611 topStyle.right = bottomStyle.right = rightWidth + rightSplitterThickness + pe.r + "px";
26612 }else{
26613 topStyle.left = bottomStyle.left = pe.l + "px";
26614 topStyle.right = bottomStyle.right = pe.r + "px";
26615 }
26616
26617 // More calculations about sizes of panes
26618 var containerHeight = this._borderBox.h - pe.t - pe.b,
26619 middleHeight = containerHeight - ( topHeight + topSplitterThickness + bottomHeight + bottomSplitterThickness),
26620 sidebarHeight = sidebarLayout ? containerHeight : middleHeight;
26621
26622 var containerWidth = this._borderBox.w - pe.l - pe.r,
26623 middleWidth = containerWidth - (leftWidth + leftSplitterThickness + rightWidth + rightSplitterThickness),
26624 sidebarWidth = sidebarLayout ? middleWidth : containerWidth;
26625
26626 // New margin-box size of each pane
26627 var dim = {
26628 top: { w: sidebarWidth, h: topHeight },
26629 bottom: { w: sidebarWidth, h: bottomHeight },
26630 left: { w: leftWidth, h: sidebarHeight },
26631 right: { w: rightWidth, h: sidebarHeight },
26632 center: { h: middleHeight, w: middleWidth }
26633 };
26634
26635 if(changedRegion){
26636 // Respond to splitter drag event by changing changedRegion's width or height
26637 var child = this["_" + changedRegion + "Widget"],
26638 mb = {};
26639 mb[ /top|bottom/.test(changedRegion) ? "h" : "w"] = changedRegionSize;
26640 child.resize ? child.resize(mb, dim[child.region]) : dojo.marginBox(child.domNode, mb);
26641 }
26642
26643 // Nodes in IE<8 don't respond to t/l/b/r, and TEXTAREA doesn't respond in any browser
26644 var janky = dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.some(this.getChildren(), function(child){
26645 return child.domNode.tagName == "TEXTAREA" || child.domNode.tagName == "INPUT";
26646 });
26647 if(janky){
26648 // Set the size of the children the old fashioned way, by setting
26649 // CSS width and height
26650
26651 var resizeWidget = function(widget, changes, result){
26652 if(widget){
26653 (widget.resize ? widget.resize(changes, result) : dojo.marginBox(widget.domNode, changes));
26654 }
26655 };
26656
26657 if(leftSplitter){ leftSplitter.style.height = sidebarHeight; }
26658 if(rightSplitter){ rightSplitter.style.height = sidebarHeight; }
26659 resizeWidget(this._leftWidget, {h: sidebarHeight}, dim.left);
26660 resizeWidget(this._rightWidget, {h: sidebarHeight}, dim.right);
26661
26662 if(topSplitter){ topSplitter.style.width = sidebarWidth; }
26663 if(bottomSplitter){ bottomSplitter.style.width = sidebarWidth; }
26664 resizeWidget(this._topWidget, {w: sidebarWidth}, dim.top);
26665 resizeWidget(this._bottomWidget, {w: sidebarWidth}, dim.bottom);
26666
26667 resizeWidget(this._centerWidget, dim.center);
26668 }else{
26669 // Calculate which panes need a notification that their size has been changed
26670 // (we've already set style.top/bottom/left/right on those other panes).
26671 var notifySides = !changedRegion || (/top|bottom/.test(changedRegion) && this.design != "sidebar"),
26672 notifyTopBottom = !changedRegion || (/left|right/.test(changedRegion) && this.design == "sidebar"),
26673 notifyList = {
26674 center: true,
26675 left: notifySides,
26676 right: notifySides,
26677 top: notifyTopBottom,
26678 bottom: notifyTopBottom
26679 };
26680
26681 // Send notification to those panes that have changed size
26682 dojo.forEach(this.getChildren(), function(child){
26683 if(child.resize && notifyList[child.region]){
26684 child.resize(null, dim[child.region]);
26685 }
26686 }, this);
26687 }
26688 },
26689
26690 destroy: function(){
26691 for(var region in this._splitters){
26692 var splitter = this._splitters[region];
26693 dijit.byNode(splitter).destroy();
26694 dojo.destroy(splitter);
26695 }
26696 delete this._splitters;
26697 delete this._splitterThickness;
26698 this.inherited(arguments);
26699 }
26700});
26701
26702// This argument can be specified for the children of a BorderContainer.
26703// Since any widget can be specified as a LayoutContainer child, mix it
26704// into the base widget class. (This is a hack, but it's effective.)
26705dojo.extend(dijit._Widget, {
26706 // region: [const] String
26707 // Parameter for children of `dijit.layout.BorderContainer`.
26708 // Values: "top", "bottom", "leading", "trailing", "left", "right", "center".
26709 // See the `dijit.layout.BorderContainer` description for details.
26710 region: '',
26711
26712 // splitter: [const] Boolean
26713 // Parameter for child of `dijit.layout.BorderContainer` where region != "center".
26714 // If true, enables user to resize the widget by putting a draggable splitter between
26715 // this widget and the region=center widget.
26716 splitter: false,
26717
26718 // minSize: [const] Number
26719 // Parameter for children of `dijit.layout.BorderContainer`.
26720 // Specifies a minimum size (in pixels) for this widget when resized by a splitter.
26721 minSize: 0,
26722
26723 // maxSize: [const] Number
26724 // Parameter for children of `dijit.layout.BorderContainer`.
26725 // Specifies a maximum size (in pixels) for this widget when resized by a splitter.
26726 maxSize: Infinity
26727});
26728
26729
26730
26731dojo.declare("dijit.layout._Splitter", [ dijit._Widget, dijit._Templated ],
26732{
26733 // summary:
26734 // A draggable spacer between two items in a `dijit.layout.BorderContainer`.
26735 // description:
26736 // This is instantiated by `dijit.layout.BorderContainer`. Users should not
26737 // create it directly.
26738 // tags:
26739 // private
26740
26741/*=====
26742 // container: [const] dijit.layout.BorderContainer
26743 // Pointer to the parent BorderContainer
26744 container: null,
26745
26746 // child: [const] dijit.layout._LayoutWidget
26747 // Pointer to the pane associated with this splitter
26748 child: null,
26749
26750 // region: String
26751 // Region of pane associated with this splitter.
26752 // "top", "bottom", "left", "right".
26753 region: null,
26754=====*/
26755
26756 // live: [const] Boolean
26757 // If true, the child's size changes and the child widget is redrawn as you drag the splitter;
26758 // otherwise, the size doesn't change until you drop the splitter (by mouse-up)
26759 live: true,
26760
26761 templateString: '<div class="dijitSplitter" dojoAttachEvent="onkeypress:_onKeyPress,onmousedown:_startDrag,onmouseenter:_onMouse,onmouseleave:_onMouse" tabIndex="0" waiRole="separator"><div class="dijitSplitterThumb"></div></div>',
26762
26763 postCreate: function(){
26764 this.inherited(arguments);
26765 this.horizontal = /top|bottom/.test(this.region);
26766 dojo.addClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V"));
26767// dojo.addClass(this.child.domNode, "dijitSplitterPane");
26768// dojo.setSelectable(this.domNode, false); //TODO is this necessary?
26769
26770 this._factor = /top|left/.test(this.region) ? 1 : -1;
26771
26772 this._cookieName = this.container.id + "_" + this.region;
26773 if(this.container.persist){
26774 // restore old size
26775 var persistSize = dojo.cookie(this._cookieName);
26776 if(persistSize){
26777 this.child.domNode.style[this.horizontal ? "height" : "width"] = persistSize;
26778 }
26779 }
26780 },
26781
26782 _computeMaxSize: function(){
26783 // summary:
26784 // Compute the maximum size that my corresponding pane can be set to
26785
26786 var dim = this.horizontal ? 'h' : 'w',
26787 thickness = this.container._splitterThickness[this.region];
26788
26789 // Get DOMNode of opposite pane, if an opposite pane exists.
26790 // Ex: if I am the _Splitter for the left pane, then get the right pane.
26791 var flip = {left:'right', right:'left', top:'bottom', bottom:'top', leading:'trailing', trailing:'leading'},
26792 oppNode = this.container["_" + flip[this.region]];
26793
26794 // I can expand up to the edge of the opposite pane, or if there's no opposite pane, then to
26795 // edge of BorderContainer
26796 var available = dojo.contentBox(this.container.domNode)[dim] -
26797 (oppNode ? dojo.marginBox(oppNode)[dim] : 0) -
26798 20 - thickness * 2;
26799
26800 return Math.min(this.child.maxSize, available);
26801 },
26802
26803 _startDrag: function(e){
26804 if(!this.cover){
26805 this.cover = dojo.doc.createElement('div');
26806 dojo.addClass(this.cover, "dijitSplitterCover");
26807 dojo.place(this.cover, this.child.domNode, "after");
26808 }
26809 dojo.addClass(this.cover, "dijitSplitterCoverActive");
26810
26811 // Safeguard in case the stop event was missed. Shouldn't be necessary if we always get the mouse up.
26812 if(this.fake){ dojo.destroy(this.fake); }
26813 if(!(this._resize = this.live)){ //TODO: disable live for IE6?
26814 // create fake splitter to display at old position while we drag
26815 (this.fake = this.domNode.cloneNode(true)).removeAttribute("id");
26816 dojo.addClass(this.domNode, "dijitSplitterShadow");
26817 dojo.place(this.fake, this.domNode, "after");
26818 }
26819 dojo.addClass(this.domNode, "dijitSplitterActive");
26820 dojo.addClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Active");
26821 if(this.fake){
26822 dojo.removeClass(this.fake, "dijitSplitterHover");
26823 dojo.removeClass(this.fake, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover");
26824 }
26825
26826 //Performance: load data info local vars for onmousevent function closure
26827 var factor = this._factor,
26828 max = this._computeMaxSize(),
26829 min = this.child.minSize || 20,
26830 isHorizontal = this.horizontal,
26831 axis = isHorizontal ? "pageY" : "pageX",
26832 pageStart = e[axis],
26833 splitterStyle = this.domNode.style,
26834 dim = isHorizontal ? 'h' : 'w',
26835 childStart = dojo.marginBox(this.child.domNode)[dim],
26836 region = this.region,
26837 splitterStart = parseInt(this.domNode.style[region], 10),
26838 resize = this._resize,
26839 childNode = this.child.domNode,
26840 layoutFunc = dojo.hitch(this.container, this.container._layoutChildren),
26841 de = dojo.doc;
26842
26843 this._handlers = (this._handlers || []).concat([
26844 dojo.connect(de, "onmousemove", this._drag = function(e, forceResize){
26845 var delta = e[axis] - pageStart,
26846 childSize = factor * delta + childStart,
26847 boundChildSize = Math.max(Math.min(childSize, max), min);
26848
26849 if(resize || forceResize){
26850 layoutFunc(region, boundChildSize);
26851 }
26852 splitterStyle[region] = factor * delta + splitterStart + (boundChildSize - childSize) + "px";
26853 }),
26854 dojo.connect(de, "ondragstart", dojo.stopEvent),
26855 dojo.connect(dojo.body(), "onselectstart", dojo.stopEvent),
26856 dojo.connect(de, "onmouseup", this, "_stopDrag")
26857 ]);
26858 dojo.stopEvent(e);
26859 },
26860
26861 _onMouse: function(e){
26862 var o = (e.type == "mouseover" || e.type == "mouseenter");
26863 dojo.toggleClass(this.domNode, "dijitSplitterHover", o);
26864 dojo.toggleClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover", o);
26865 },
26866
26867 _stopDrag: function(e){
26868 try{
26869 if(this.cover){
26870 dojo.removeClass(this.cover, "dijitSplitterCoverActive");
26871 }
26872 if(this.fake){ dojo.destroy(this.fake); }
26873 dojo.removeClass(this.domNode, "dijitSplitterActive");
26874 dojo.removeClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Active");
26875 dojo.removeClass(this.domNode, "dijitSplitterShadow");
26876 this._drag(e); //TODO: redundant with onmousemove?
26877 this._drag(e, true);
26878 }finally{
26879 this._cleanupHandlers();
26880 delete this._drag;
26881 }
26882
26883 if(this.container.persist){
26884 dojo.cookie(this._cookieName, this.child.domNode.style[this.horizontal ? "height" : "width"], {expires:365});
26885 }
26886 },
26887
26888 _cleanupHandlers: function(){
26889 dojo.forEach(this._handlers, dojo.disconnect);
26890 delete this._handlers;
26891 },
26892
26893 _onKeyPress: function(/*Event*/ e){
26894 // should we apply typematic to this?
26895 this._resize = true;
26896 var horizontal = this.horizontal;
26897 var tick = 1;
26898 var dk = dojo.keys;
26899 switch(e.charOrCode){
26900 case horizontal ? dk.UP_ARROW : dk.LEFT_ARROW:
26901 tick *= -1;
26902// break;
26903 case horizontal ? dk.DOWN_ARROW : dk.RIGHT_ARROW:
26904 break;
26905 default:
26906// this.inherited(arguments);
26907 return;
26908 }
26909 var childSize = dojo.marginBox(this.child.domNode)[ horizontal ? 'h' : 'w' ] + this._factor * tick;
26910 this.container._layoutChildren(this.region, Math.max(Math.min(childSize, this._computeMaxSize()), this.child.minSize));
26911 dojo.stopEvent(e);
26912 },
26913
26914 destroy: function(){
26915 this._cleanupHandlers();
26916 delete this.child;
26917 delete this.container;
26918 delete this.cover;
26919 delete this.fake;
26920 this.inherited(arguments);
26921 }
26922});
26923
26924dojo.declare("dijit.layout._Gutter", [dijit._Widget, dijit._Templated ],
26925{
26926 // summary:
26927 // Just a spacer div to separate side pane from center pane.
26928 // Basically a trick to lookup the gutter/splitter width from the theme.
26929 // description:
26930 // Instantiated by `dijit.layout.BorderContainer`. Users should not
26931 // create directly.
26932 // tags:
26933 // private
26934
26935 templateString: '<div class="dijitGutter" waiRole="presentation"></div>',
26936
26937 postCreate: function(){
26938 this.horizontal = /top|bottom/.test(this.region);
26939 dojo.addClass(this.domNode, "dijitGutter" + (this.horizontal ? "H" : "V"));
26940 }
26941});
26942
26943}
26944
26945if(!dojo._hasResource["dijit.layout.LayoutContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
26946dojo._hasResource["dijit.layout.LayoutContainer"] = true;
26947dojo.provide("dijit.layout.LayoutContainer");
26948
26949
26950
26951dojo.declare("dijit.layout.LayoutContainer",
26952 dijit.layout._LayoutWidget,
26953 {
26954 // summary:
26955 // Deprecated. Use `dijit.layout.BorderContainer` instead.
26956 //
26957 // description:
26958 // Provides Delphi-style panel layout semantics.
26959 //
26960 // A LayoutContainer is a box with a specified size (like style="width: 500px; height: 500px;"),
26961 // that contains children widgets marked with "layoutAlign" of "left", "right", "bottom", "top", and "client".
26962 // It takes it's children marked as left/top/bottom/right, and lays them out along the edges of the box,
26963 // and then it takes the child marked "client" and puts it into the remaining space in the middle.
26964 //
26965 // Left/right positioning is similar to CSS's "float: left" and "float: right",
26966 // and top/bottom positioning would be similar to "float: top" and "float: bottom", if there were such
26967 // CSS.
26968 //
26969 // Note that there can only be one client element, but there can be multiple left, right, top,
26970 // or bottom elements.
26971 //
26972 // example:
26973 // | <style>
26974 // | html, body{ height: 100%; width: 100%; }
26975 // | </style>
26976 // | <div dojoType="dijit.layout.LayoutContainer" style="width: 100%; height: 100%">
26977 // | <div dojoType="dijit.layout.ContentPane" layoutAlign="top">header text</div>
26978 // | <div dojoType="dijit.layout.ContentPane" layoutAlign="left" style="width: 200px;">table of contents</div>
26979 // | <div dojoType="dijit.layout.ContentPane" layoutAlign="client">client area</div>
26980 // | </div>
26981 //
26982 // Lays out each child in the natural order the children occur in.
26983 // Basically each child is laid out into the "remaining space", where "remaining space" is initially
26984 // the content area of this widget, but is reduced to a smaller rectangle each time a child is added.
26985 // tags:
26986 // deprecated
26987
26988 baseClass: "dijitLayoutContainer",
26989
26990 constructor: function(){
26991 dojo.deprecated("dijit.layout.LayoutContainer is deprecated", "use BorderContainer instead", 2.0);
26992 },
26993
26994 layout: function(){
26995 dijit.layout.layoutChildren(this.domNode, this._contentBox, this.getChildren());
26996 },
26997
26998 addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
26999 this.inherited(arguments);
27000 if(this._started){
27001 dijit.layout.layoutChildren(this.domNode, this._contentBox, this.getChildren());
27002 }
27003 },
27004
27005 removeChild: function(/*dijit._Widget*/ widget){
27006 this.inherited(arguments);
27007 if(this._started){
27008 dijit.layout.layoutChildren(this.domNode, this._contentBox, this.getChildren());
27009 }
27010 }
27011});
27012
27013// This argument can be specified for the children of a LayoutContainer.
27014// Since any widget can be specified as a LayoutContainer child, mix it
27015// into the base widget class. (This is a hack, but it's effective.)
27016dojo.extend(dijit._Widget, {
27017 // layoutAlign: String
27018 // "none", "left", "right", "bottom", "top", and "client".
27019 // See the LayoutContainer description for details on this parameter.
27020 layoutAlign: 'none'
27021});
27022
27023}
27024
27025if(!dojo._hasResource["dijit.layout.LinkPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
27026dojo._hasResource["dijit.layout.LinkPane"] = true;
27027dojo.provide("dijit.layout.LinkPane");
27028
27029
27030
27031
27032dojo.declare("dijit.layout.LinkPane",
27033 [dijit.layout.ContentPane, dijit._Templated],
27034 {
27035 // summary:
27036 // A ContentPane with an href where (when declared in markup)
27037 // the title is specified as innerHTML rather than as a title attribute.
27038 // description:
27039 // LinkPane is just a ContentPane that is declared in markup similarly
27040 // to an anchor. The anchor's body (the words between `<a>` and `</a>`)
27041 // become the title of the widget (used for TabContainer, AccordionContainer, etc.)
27042 // example:
27043 // | <a href="foo.html">my title</a>
27044
27045 // I'm using a template because the user may specify the input as
27046 // <a href="foo.html">title</a>, in which case we need to get rid of the
27047 // <a> because we don't want a link.
27048 templateString: '<div class="dijitLinkPane" dojoAttachPoint="containerNode"></div>',
27049
27050 postMixInProperties: function(){
27051 // If user has specified node contents, they become the title
27052 // (the link must be plain text)
27053 if(this.srcNodeRef){
27054 this.title += this.srcNodeRef.innerHTML;
27055 }
27056 this.inherited(arguments);
27057 },
27058
27059 _fillContent: function(/*DomNode*/ source){
27060 // Overrides _Templated._fillContent().
27061
27062 // _Templated._fillContent() relocates srcNodeRef innerHTML to templated container node,
27063 // but in our case the srcNodeRef innerHTML is the title, so shouldn't be
27064 // copied
27065 }
27066});
27067
27068}
27069
27070if(!dojo._hasResource["dijit.layout.SplitContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
27071dojo._hasResource["dijit.layout.SplitContainer"] = true;
27072dojo.provide("dijit.layout.SplitContainer");
27073
27074//
27075// FIXME: make it prettier
27076// FIXME: active dragging upwards doesn't always shift other bars (direction calculation is wrong in this case)
27077//
27078
27079
27080
27081
27082dojo.declare("dijit.layout.SplitContainer",
27083 dijit.layout._LayoutWidget,
27084 {
27085 // summary:
27086 // Deprecated. Use `dijit.layout.BorderContainer` instead.
27087 // description:
27088 // A Container widget with sizing handles in-between each child.
27089 // Contains multiple children widgets, all of which are displayed side by side
27090 // (either horizontally or vertically); there's a bar between each of the children,
27091 // and you can adjust the relative size of each child by dragging the bars.
27092 //
27093 // You must specify a size (width and height) for the SplitContainer.
27094 // tags:
27095 // deprecated
27096
27097 constructor: function(){
27098 dojo.deprecated("dijit.layout.SplitContainer is deprecated", "use BorderContainer with splitter instead", 2.0);
27099 },
27100
27101 // activeSizing: Boolean
27102 // If true, the children's size changes as you drag the bar;
27103 // otherwise, the sizes don't change until you drop the bar (by mouse-up)
27104 activeSizing: false,
27105
27106 // sizerWidth: Integer
27107 // Size in pixels of the bar between each child
27108 sizerWidth: 7, // FIXME: this should be a CSS attribute (at 7 because css wants it to be 7 until we fix to css)
27109
27110 // orientation: String
27111 // either 'horizontal' or vertical; indicates whether the children are
27112 // arranged side-by-side or up/down.
27113 orientation: 'horizontal',
27114
27115 // persist: Boolean
27116 // Save splitter positions in a cookie
27117 persist: true,
27118
27119 baseClass: "dijitSplitContainer",
27120
27121 postMixInProperties: function(){
27122 this.inherited("postMixInProperties",arguments);
27123 this.isHorizontal = (this.orientation == 'horizontal');
27124 },
27125
27126 postCreate: function(){
27127 this.inherited(arguments);
27128 this.sizers = [];
27129
27130 // overflow has to be explicitly hidden for splitContainers using gekko (trac #1435)
27131 // to keep other combined css classes from inadvertantly making the overflow visible
27132 if(dojo.isMozilla){
27133 this.domNode.style.overflow = '-moz-scrollbars-none'; // hidden doesn't work
27134 }
27135
27136 // create the fake dragger
27137 if(typeof this.sizerWidth == "object"){
27138 try{ //FIXME: do this without a try/catch
27139 this.sizerWidth = parseInt(this.sizerWidth.toString());
27140 }catch(e){ this.sizerWidth = 7; }
27141 }
27142 var sizer = dojo.doc.createElement('div');
27143 this.virtualSizer = sizer;
27144 sizer.style.position = 'relative';
27145
27146 // #1681: work around the dreaded 'quirky percentages in IE' layout bug
27147 // If the splitcontainer's dimensions are specified in percentages, it
27148 // will be resized when the virtualsizer is displayed in _showSizingLine
27149 // (typically expanding its bounds unnecessarily). This happens because
27150 // we use position: relative for .dijitSplitContainer.
27151 // The workaround: instead of changing the display style attribute,
27152 // switch to changing the zIndex (bring to front/move to back)
27153
27154 sizer.style.zIndex = 10;
27155 sizer.className = this.isHorizontal ? 'dijitSplitContainerVirtualSizerH' : 'dijitSplitContainerVirtualSizerV';
27156 this.domNode.appendChild(sizer);
27157 dojo.setSelectable(sizer, false);
27158 },
27159
27160 destroy: function(){
27161 delete this.virtualSizer;
27162 dojo.forEach(this._ownconnects, dojo.disconnect);
27163 this.inherited(arguments);
27164 },
27165 startup: function(){
27166 if(this._started){ return; }
27167
27168 dojo.forEach(this.getChildren(), function(child, i, children){
27169 // attach the children and create the draggers
27170 this._setupChild(child);
27171
27172 if(i < children.length-1){
27173 this._addSizer();
27174 }
27175 }, this);
27176
27177 if(this.persist){
27178 this._restoreState();
27179 }
27180
27181 this.inherited(arguments);
27182 },
27183
27184 _setupChild: function(/*dijit._Widget*/ child){
27185 this.inherited(arguments);
27186 child.domNode.style.position = "absolute";
27187 dojo.addClass(child.domNode, "dijitSplitPane");
27188 },
27189
27190 _onSizerMouseDown: function(e){
27191 if(e.target.id){
27192 for(var i=0;i<this.sizers.length;i++){
27193 if(this.sizers[i].id == e.target.id){
27194 break;
27195 }
27196 }
27197 if(i<this.sizers.length){
27198 this.beginSizing(e,i);
27199 }
27200 }
27201 },
27202 _addSizer: function(index){
27203 index = index === undefined ? this.sizers.length : index;
27204
27205 // TODO: use a template for this!!!
27206 var sizer = dojo.doc.createElement('div');
27207 sizer.id=dijit.getUniqueId('dijit_layout_SplitterContainer_Splitter');
27208 this.sizers.splice(index,0,sizer);
27209 this.domNode.appendChild(sizer);
27210
27211 sizer.className = this.isHorizontal ? 'dijitSplitContainerSizerH' : 'dijitSplitContainerSizerV';
27212
27213 // add the thumb div
27214 var thumb = dojo.doc.createElement('div');
27215 thumb.className = 'thumb';
27216 sizer.appendChild(thumb);
27217
27218 // FIXME: are you serious? why aren't we using mover start/stop combo?
27219 this.connect(sizer, "onmousedown", '_onSizerMouseDown');
27220
27221 dojo.setSelectable(sizer, false);
27222 },
27223
27224 removeChild: function(widget){
27225 // summary:
27226 // Remove sizer, but only if widget is really our child and
27227 // we have at least one sizer to throw away
27228 if(this.sizers.length){
27229 var i=dojo.indexOf(this.getChildren(), widget)
27230 if(i != -1){
27231 if(i == this.sizers.length){
27232 i--;
27233 }
27234 dojo.destroy(this.sizers[i]);
27235 this.sizers.splice(i,1);
27236 }
27237 }
27238
27239 // Remove widget and repaint
27240 this.inherited(arguments);
27241 if(this._started){
27242 this.layout();
27243 }
27244 },
27245
27246 addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
27247 // summary:
27248 // Add a child widget to the container
27249 // child:
27250 // a widget to add
27251 // insertIndex:
27252 // postion in the "stack" to add the child widget
27253
27254 this.inherited(arguments);
27255
27256 if(this._started){
27257 // Do the stuff that startup() does for each widget
27258 var children = this.getChildren();
27259 if(children.length > 1){
27260 this._addSizer(insertIndex);
27261 }
27262
27263 // and then reposition (ie, shrink) every pane to make room for the new guy
27264 this.layout();
27265 }
27266 },
27267
27268 layout: function(){
27269 // summary:
27270 // Do layout of panels
27271
27272 // base class defines this._contentBox on initial creation and also
27273 // on resize
27274 this.paneWidth = this._contentBox.w;
27275 this.paneHeight = this._contentBox.h;
27276
27277 var children = this.getChildren();
27278 if(!children.length){ return; }
27279
27280 //
27281 // calculate space
27282 //
27283
27284 var space = this.isHorizontal ? this.paneWidth : this.paneHeight;
27285 if(children.length > 1){
27286 space -= this.sizerWidth * (children.length - 1);
27287 }
27288
27289 //
27290 // calculate total of SizeShare values
27291 //
27292 var outOf = 0;
27293 dojo.forEach(children, function(child){
27294 outOf += child.sizeShare;
27295 });
27296
27297 //
27298 // work out actual pixels per sizeshare unit
27299 //
27300 var pixPerUnit = space / outOf;
27301
27302 //
27303 // set the SizeActual member of each pane
27304 //
27305 var totalSize = 0;
27306 dojo.forEach(children.slice(0, children.length - 1), function(child){
27307 var size = Math.round(pixPerUnit * child.sizeShare);
27308 child.sizeActual = size;
27309 totalSize += size;
27310 });
27311
27312 children[children.length-1].sizeActual = space - totalSize;
27313
27314 //
27315 // make sure the sizes are ok
27316 //
27317 this._checkSizes();
27318
27319 //
27320 // now loop, positioning each pane and letting children resize themselves
27321 //
27322
27323 var pos = 0;
27324 var size = children[0].sizeActual;
27325 this._movePanel(children[0], pos, size);
27326 children[0].position = pos;
27327 pos += size;
27328
27329 // if we don't have any sizers, our layout method hasn't been called yet
27330 // so bail until we are called..TODO: REVISIT: need to change the startup
27331 // algorithm to guaranteed the ordering of calls to layout method
27332 if(!this.sizers){
27333 return;
27334 }
27335
27336 dojo.some(children.slice(1), function(child, i){
27337 // error-checking
27338 if(!this.sizers[i]){
27339 return true;
27340 }
27341 // first we position the sizing handle before this pane
27342 this._moveSlider(this.sizers[i], pos, this.sizerWidth);
27343 this.sizers[i].position = pos;
27344 pos += this.sizerWidth;
27345
27346 size = child.sizeActual;
27347 this._movePanel(child, pos, size);
27348 child.position = pos;
27349 pos += size;
27350 }, this);
27351 },
27352
27353 _movePanel: function(panel, pos, size){
27354 if(this.isHorizontal){
27355 panel.domNode.style.left = pos + 'px'; // TODO: resize() takes l and t parameters too, don't need to set manually
27356 panel.domNode.style.top = 0;
27357 var box = {w: size, h: this.paneHeight};
27358 if(panel.resize){
27359 panel.resize(box);
27360 }else{
27361 dojo.marginBox(panel.domNode, box);
27362 }
27363 }else{
27364 panel.domNode.style.left = 0; // TODO: resize() takes l and t parameters too, don't need to set manually
27365 panel.domNode.style.top = pos + 'px';
27366 var box = {w: this.paneWidth, h: size};
27367 if(panel.resize){
27368 panel.resize(box);
27369 }else{
27370 dojo.marginBox(panel.domNode, box);
27371 }
27372 }
27373 },
27374
27375 _moveSlider: function(slider, pos, size){
27376 if(this.isHorizontal){
27377 slider.style.left = pos + 'px';
27378 slider.style.top = 0;
27379 dojo.marginBox(slider, { w: size, h: this.paneHeight });
27380 }else{
27381 slider.style.left = 0;
27382 slider.style.top = pos + 'px';
27383 dojo.marginBox(slider, { w: this.paneWidth, h: size });
27384 }
27385 },
27386
27387 _growPane: function(growth, pane){
27388 if(growth > 0){
27389 if(pane.sizeActual > pane.sizeMin){
27390 if((pane.sizeActual - pane.sizeMin) > growth){
27391
27392 // stick all the growth in this pane
27393 pane.sizeActual = pane.sizeActual - growth;
27394 growth = 0;
27395 }else{
27396 // put as much growth in here as we can
27397 growth -= pane.sizeActual - pane.sizeMin;
27398 pane.sizeActual = pane.sizeMin;
27399 }
27400 }
27401 }
27402 return growth;
27403 },
27404
27405 _checkSizes: function(){
27406
27407 var totalMinSize = 0;
27408 var totalSize = 0;
27409 var children = this.getChildren();
27410
27411 dojo.forEach(children, function(child){
27412 totalSize += child.sizeActual;
27413 totalMinSize += child.sizeMin;
27414 });
27415
27416 // only make adjustments if we have enough space for all the minimums
27417
27418 if(totalMinSize <= totalSize){
27419
27420 var growth = 0;
27421
27422 dojo.forEach(children, function(child){
27423 if(child.sizeActual < child.sizeMin){
27424 growth += child.sizeMin - child.sizeActual;
27425 child.sizeActual = child.sizeMin;
27426 }
27427 });
27428
27429 if(growth > 0){
27430 var list = this.isDraggingLeft ? children.reverse() : children;
27431 dojo.forEach(list, function(child){
27432 growth = this._growPane(growth, child);
27433 }, this);
27434 }
27435 }else{
27436 dojo.forEach(children, function(child){
27437 child.sizeActual = Math.round(totalSize * (child.sizeMin / totalMinSize));
27438 });
27439 }
27440 },
27441
27442 beginSizing: function(e, i){
27443 var children = this.getChildren();
27444 this.paneBefore = children[i];
27445 this.paneAfter = children[i+1];
27446
27447 this.isSizing = true;
27448 this.sizingSplitter = this.sizers[i];
27449
27450 if(!this.cover){
27451 this.cover = dojo.create('div', {
27452 style: {
27453 position:'absolute',
27454 zIndex:5,
27455 top: 0,
27456 left: 0,
27457 width: "100%",
27458 height: "100%"
27459 }
27460 }, this.domNode);
27461 }else{
27462 this.cover.style.zIndex = 5;
27463 }
27464 this.sizingSplitter.style.zIndex = 6;
27465
27466 // 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.)
27467 this.originPos = dojo.position(children[0].domNode, true);
27468 if(this.isHorizontal){
27469 var client = e.layerX || e.offsetX || 0;
27470 var screen = e.pageX;
27471 this.originPos = this.originPos.x;
27472 }else{
27473 var client = e.layerY || e.offsetY || 0;
27474 var screen = e.pageY;
27475 this.originPos = this.originPos.y;
27476 }
27477 this.startPoint = this.lastPoint = screen;
27478 this.screenToClientOffset = screen - client;
27479 this.dragOffset = this.lastPoint - this.paneBefore.sizeActual - this.originPos - this.paneBefore.position;
27480
27481 if(!this.activeSizing){
27482 this._showSizingLine();
27483 }
27484
27485 //
27486 // attach mouse events
27487 //
27488 this._ownconnects = [];
27489 this._ownconnects.push(dojo.connect(dojo.doc.documentElement, "onmousemove", this, "changeSizing"));
27490 this._ownconnects.push(dojo.connect(dojo.doc.documentElement, "onmouseup", this, "endSizing"));
27491
27492 dojo.stopEvent(e);
27493 },
27494
27495 changeSizing: function(e){
27496 if(!this.isSizing){ return; }
27497 this.lastPoint = this.isHorizontal ? e.pageX : e.pageY;
27498 this.movePoint();
27499 if(this.activeSizing){
27500 this._updateSize();
27501 }else{
27502 this._moveSizingLine();
27503 }
27504 dojo.stopEvent(e);
27505 },
27506
27507 endSizing: function(e){
27508 if(!this.isSizing){ return; }
27509 if(this.cover){
27510 this.cover.style.zIndex = -1;
27511 }
27512 if(!this.activeSizing){
27513 this._hideSizingLine();
27514 }
27515
27516 this._updateSize();
27517
27518 this.isSizing = false;
27519
27520 if(this.persist){
27521 this._saveState(this);
27522 }
27523
27524 dojo.forEach(this._ownconnects, dojo.disconnect);
27525 },
27526
27527 movePoint: function(){
27528
27529 // make sure lastPoint is a legal point to drag to
27530 var p = this.lastPoint - this.screenToClientOffset;
27531
27532 var a = p - this.dragOffset;
27533 a = this.legaliseSplitPoint(a);
27534 p = a + this.dragOffset;
27535
27536 this.lastPoint = p + this.screenToClientOffset;
27537 },
27538
27539 legaliseSplitPoint: function(a){
27540
27541 a += this.sizingSplitter.position;
27542
27543 this.isDraggingLeft = !!(a > 0);
27544
27545 if(!this.activeSizing){
27546 var min = this.paneBefore.position + this.paneBefore.sizeMin;
27547 if(a < min){
27548 a = min;
27549 }
27550
27551 var max = this.paneAfter.position + (this.paneAfter.sizeActual - (this.sizerWidth + this.paneAfter.sizeMin));
27552 if(a > max){
27553 a = max;
27554 }
27555 }
27556
27557 a -= this.sizingSplitter.position;
27558
27559 this._checkSizes();
27560
27561 return a;
27562 },
27563
27564 _updateSize: function(){
27565 //FIXME: sometimes this.lastPoint is NaN
27566 var pos = this.lastPoint - this.dragOffset - this.originPos;
27567
27568 var start_region = this.paneBefore.position;
27569 var end_region = this.paneAfter.position + this.paneAfter.sizeActual;
27570
27571 this.paneBefore.sizeActual = pos - start_region;
27572 this.paneAfter.position = pos + this.sizerWidth;
27573 this.paneAfter.sizeActual = end_region - this.paneAfter.position;
27574
27575 dojo.forEach(this.getChildren(), function(child){
27576 child.sizeShare = child.sizeActual;
27577 });
27578
27579 if(this._started){
27580 this.layout();
27581 }
27582 },
27583
27584 _showSizingLine: function(){
27585
27586 this._moveSizingLine();
27587
27588 dojo.marginBox(this.virtualSizer,
27589 this.isHorizontal ? { w: this.sizerWidth, h: this.paneHeight } : { w: this.paneWidth, h: this.sizerWidth });
27590
27591 this.virtualSizer.style.display = 'block';
27592 },
27593
27594 _hideSizingLine: function(){
27595 this.virtualSizer.style.display = 'none';
27596 },
27597
27598 _moveSizingLine: function(){
27599 var pos = (this.lastPoint - this.startPoint) + this.sizingSplitter.position;
27600 dojo.style(this.virtualSizer,(this.isHorizontal ? "left" : "top"),pos+"px");
27601 // this.virtualSizer.style[ this.isHorizontal ? "left" : "top" ] = pos + 'px'; // FIXME: remove this line if the previous is better
27602 },
27603
27604 _getCookieName: function(i){
27605 return this.id + "_" + i;
27606 },
27607
27608 _restoreState: function(){
27609 dojo.forEach(this.getChildren(), function(child, i){
27610 var cookieName = this._getCookieName(i);
27611 var cookieValue = dojo.cookie(cookieName);
27612 if(cookieValue){
27613 var pos = parseInt(cookieValue);
27614 if(typeof pos == "number"){
27615 child.sizeShare = pos;
27616 }
27617 }
27618 }, this);
27619 },
27620
27621 _saveState: function(){
27622 if(!this.persist){
27623 return;
27624 }
27625 dojo.forEach(this.getChildren(), function(child, i){
27626 dojo.cookie(this._getCookieName(i), child.sizeShare, {expires:365});
27627 }, this);
27628 }
27629});
27630
27631// These arguments can be specified for the children of a SplitContainer.
27632// Since any widget can be specified as a SplitContainer child, mix them
27633// into the base widget class. (This is a hack, but it's effective.)
27634dojo.extend(dijit._Widget, {
27635 // sizeMin: [deprecated] Integer
27636 // Deprecated. Parameter for children of `dijit.layout.SplitContainer`.
27637 // Minimum size (width or height) of a child of a SplitContainer.
27638 // The value is relative to other children's sizeShare properties.
27639 sizeMin: 10,
27640
27641 // sizeShare: [deprecated] Integer
27642 // Deprecated. Parameter for children of `dijit.layout.SplitContainer`.
27643 // Size (width or height) of a child of a SplitContainer.
27644 // The value is relative to other children's sizeShare properties.
27645 // For example, if there are two children and each has sizeShare=10, then
27646 // each takes up 50% of the available space.
27647 sizeShare: 10
27648});
27649
27650}
27651
27652if(!dojo._hasResource["dijit.layout._TabContainerBase"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
27653dojo._hasResource["dijit.layout._TabContainerBase"] = true;
27654dojo.provide("dijit.layout._TabContainerBase");
27655
27656
27657
27658
27659dojo.declare("dijit.layout._TabContainerBase",
27660 [dijit.layout.StackContainer, dijit._Templated],
27661 {
27662 // summary:
27663 // Abstract base class for TabContainer. Must define _makeController() to instantiate
27664 // and return the widget that displays the tab labels
27665 // description:
27666 // A TabContainer is a container that has multiple panes, but shows only
27667 // one pane at a time. There are a set of tabs corresponding to each pane,
27668 // where each tab has the name (aka title) of the pane, and optionally a close button.
27669
27670 // tabPosition: String
27671 // Defines where tabs go relative to tab content.
27672 // "top", "bottom", "left-h", "right-h"
27673 tabPosition: "top",
27674
27675 baseClass: "dijitTabContainer",
27676
27677 // tabStrip: Boolean
27678 // Defines whether the tablist gets an extra class for layouting, putting a border/shading
27679 // around the set of tabs.
27680 tabStrip: false,
27681
27682 // nested: Boolean
27683 // If true, use styling for a TabContainer nested inside another TabContainer.
27684 // For tundra etc., makes tabs look like links, and hides the outer
27685 // border since the outer TabContainer already has a border.
27686 nested: false,
27687
27688 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"),
27689
27690 postMixInProperties: function(){
27691 // set class name according to tab position, ex: dijitTabContainerTop
27692 this.baseClass += this.tabPosition.charAt(0).toUpperCase() + this.tabPosition.substr(1).replace(/-.*/, "");
27693
27694 this.srcNodeRef && dojo.style(this.srcNodeRef, "visibility", "hidden");
27695
27696 this.inherited(arguments);
27697 },
27698
27699 postCreate: function(){
27700 this.inherited(arguments);
27701
27702 // Create the tab list that will have a tab (a.k.a. tab button) for each tab panel
27703 this.tablist = this._makeController(this.tablistNode);
27704
27705 if(!this.doLayout){ dojo.addClass(this.domNode, "dijitTabContainerNoLayout"); }
27706
27707 if(this.nested){
27708 /* workaround IE's lack of support for "a > b" selectors by
27709 * tagging each node in the template.
27710 */
27711 dojo.addClass(this.domNode, "dijitTabContainerNested");
27712 dojo.addClass(this.tablist.containerNode, "dijitTabContainerTabListNested");
27713 dojo.addClass(this.tablistSpacer, "dijitTabContainerSpacerNested");
27714 dojo.addClass(this.containerNode, "dijitTabPaneWrapperNested");
27715 }else{
27716 dojo.addClass(this.domNode, "tabStrip-" + (this.tabStrip ? "enabled" : "disabled"));
27717 }
27718 },
27719
27720 _setupChild: function(/*dijit._Widget*/ tab){
27721 // Overrides StackContainer._setupChild().
27722 dojo.addClass(tab.domNode, "dijitTabPane");
27723 this.inherited(arguments);
27724 },
27725
27726 startup: function(){
27727 if(this._started){ return; }
27728
27729 // wire up the tablist and its tabs
27730 this.tablist.startup();
27731
27732 this.inherited(arguments);
27733 },
27734
27735 layout: function(){
27736 // Overrides StackContainer.layout().
27737 // Configure the content pane to take up all the space except for where the tabs are
27738
27739 if(!this._contentBox || typeof(this._contentBox.l) == "undefined"){return;}
27740
27741 var sc = this.selectedChildWidget;
27742
27743 if(this.doLayout){
27744 // position and size the titles and the container node
27745 var titleAlign = this.tabPosition.replace(/-h/, "");
27746 this.tablist.layoutAlign = titleAlign;
27747 var children = [this.tablist, {
27748 domNode: this.tablistSpacer,
27749 layoutAlign: titleAlign
27750 }, {
27751 domNode: this.containerNode,
27752 layoutAlign: "client"
27753 }];
27754 dijit.layout.layoutChildren(this.domNode, this._contentBox, children);
27755
27756 // Compute size to make each of my children.
27757 // children[2] is the margin-box size of this.containerNode, set by layoutChildren() call above
27758 this._containerContentBox = dijit.layout.marginBox2contentBox(this.containerNode, children[2]);
27759
27760 if(sc && sc.resize){
27761 sc.resize(this._containerContentBox);
27762 }
27763 }else{
27764 // just layout the tab controller, so it can position left/right buttons etc.
27765 if(this.tablist.resize){
27766 this.tablist.resize({w: dojo.contentBox(this.domNode).w});
27767 }
27768
27769 // and call resize() on the selected pane just to tell it that it's been made visible
27770 if(sc && sc.resize){
27771 sc.resize();
27772 }
27773 }
27774 },
27775
27776 destroy: function(){
27777 if(this.tablist){
27778 this.tablist.destroy();
27779 }
27780 this.inherited(arguments);
27781 }
27782});
27783
27784
27785}
27786
27787if(!dojo._hasResource["dijit.layout.TabController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
27788dojo._hasResource["dijit.layout.TabController"] = true;
27789dojo.provide("dijit.layout.TabController");
27790
27791
27792
27793// Menu is used for an accessible close button, would be nice to have a lighter-weight solution
27794
27795
27796
27797
27798
27799dojo.declare("dijit.layout.TabController",
27800 dijit.layout.StackController,
27801{
27802 // summary:
27803 // Set of tabs (the things with titles and a close button, that you click to show a tab panel).
27804 // Used internally by `dijit.layout.TabContainer`.
27805 // description:
27806 // Lets the user select the currently shown pane in a TabContainer or StackContainer.
27807 // TabController also monitors the TabContainer, and whenever a pane is
27808 // added or deleted updates itself accordingly.
27809 // tags:
27810 // private
27811
27812 templateString: "<div wairole='tablist' dojoAttachEvent='onkeypress:onkeypress'></div>",
27813
27814 // tabPosition: String
27815 // Defines where tabs go relative to the content.
27816 // "top", "bottom", "left-h", "right-h"
27817 tabPosition: "top",
27818
27819 // buttonWidget: String
27820 // The name of the tab widget to create to correspond to each page
27821 buttonWidget: "dijit.layout._TabButton",
27822
27823 _rectifyRtlTabList: function(){
27824 // summary:
27825 // 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
27826
27827 if(0 >= this.tabPosition.indexOf('-h')){ return; }
27828 if(!this.pane2button){ return; }
27829
27830 var maxWidth = 0;
27831 for(var pane in this.pane2button){
27832 var ow = this.pane2button[pane].innerDiv.scrollWidth;
27833 maxWidth = Math.max(maxWidth, ow);
27834 }
27835 //unify the length of all the tabs
27836 for(pane in this.pane2button){
27837 this.pane2button[pane].innerDiv.style.width = maxWidth + 'px';
27838 }
27839 }
27840});
27841
27842dojo.declare("dijit.layout._TabButton",
27843 dijit.layout._StackButton,
27844 {
27845 // summary:
27846 // A tab (the thing you click to select a pane).
27847 // description:
27848 // Contains the title of the pane, and optionally a close-button to destroy the pane.
27849 // This is an internal widget and should not be instantiated directly.
27850 // tags:
27851 // private
27852
27853 // baseClass: String
27854 // The CSS class applied to the domNode.
27855 baseClass: "dijitTab",
27856
27857 // Apply dijitTabCloseButtonHover when close button is hovered
27858 cssStateNodes: {
27859 closeNode: "dijitTabCloseButton"
27860 },
27861
27862 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"),
27863
27864 // Override _FormWidget.scrollOnFocus.
27865 // Don't scroll the whole tab container into view when the button is focused.
27866 scrollOnFocus: false,
27867
27868 postMixInProperties: function(){
27869 // Override blank iconClass from Button to do tab height adjustment on IE6,
27870 // to make sure that tabs with and w/out close icons are same height
27871 if(!this.iconClass){
27872 this.iconClass = "dijitTabButtonIcon";
27873 }
27874 },
27875
27876 postCreate: function(){
27877 this.inherited(arguments);
27878 dojo.setSelectable(this.containerNode, false);
27879
27880 // If a custom icon class has not been set for the
27881 // tab icon, set its width to one pixel. This ensures
27882 // that the height styling of the tab is maintained,
27883 // as it is based on the height of the icon.
27884 // TODO: I still think we can just set dijitTabButtonIcon to 1px in CSS <Bill>
27885 if(this.iconNode.className == "dijitTabButtonIcon"){
27886 dojo.style(this.iconNode, "width", "1px");
27887 }
27888 },
27889
27890 startup: function(){
27891 this.inherited(arguments);
27892 var n = this.domNode;
27893
27894 // Required to give IE6 a kick, as it initially hides the
27895 // tabs until they are focused on.
27896 setTimeout(function(){
27897 n.className = n.className;
27898 }, 1);
27899 },
27900
27901 _setCloseButtonAttr: function(disp){
27902 this.closeButton = disp;
27903 dojo.toggleClass(this.innerDiv, "dijitClosable", disp);
27904 this.closeNode.style.display = disp ? "" : "none";
27905 if(disp){
27906 var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
27907 if(this.closeNode){
27908 dojo.attr(this.closeNode,"title", _nlsResources.itemClose);
27909 }
27910 // add context menu onto title button
27911 var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
27912 this._closeMenu = new dijit.Menu({
27913 id: this.id+"_Menu",
27914 dir: this.dir,
27915 lang: this.lang,
27916 targetNodeIds: [this.domNode]
27917 });
27918
27919 this._closeMenu.addChild(new dijit.MenuItem({
27920 label: _nlsResources.itemClose,
27921 dir: this.dir,
27922 lang: this.lang,
27923 onClick: dojo.hitch(this, "onClickCloseButton")
27924 }));
27925 }else{
27926 if(this._closeMenu){
27927 this._closeMenu.destroyRecursive();
27928 delete this._closeMenu;
27929 }
27930 }
27931 },
27932 _setLabelAttr: function(/*String*/ content){
27933 // summary:
27934 // Hook for attr('label', ...) to work.
27935 // description:
27936 // takes an HTML string.
27937 // Inherited ToggleButton implementation will Set the label (text) of the button;
27938 // Need to set the alt attribute of icon on tab buttons if no label displayed
27939 this.inherited(arguments);
27940 if(this.showLabel == false && !this.params.title){
27941 this.iconNode.alt = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
27942 }
27943 },
27944
27945 destroy: function(){
27946 if(this._closeMenu){
27947 this._closeMenu.destroyRecursive();
27948 delete this._closeMenu;
27949 }
27950 this.inherited(arguments);
27951 }
27952});
27953
27954}
27955
27956if(!dojo._hasResource["dijit.layout.ScrollingTabController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
27957dojo._hasResource["dijit.layout.ScrollingTabController"] = true;
27958dojo.provide("dijit.layout.ScrollingTabController");
27959
27960
27961
27962
27963dojo.declare("dijit.layout.ScrollingTabController",
27964 dijit.layout.TabController,
27965 {
27966 // summary:
27967 // Set of tabs with left/right arrow keys and a menu to switch between tabs not
27968 // all fitting on a single row.
27969 // Works only for horizontal tabs (either above or below the content, not to the left
27970 // or right).
27971 // tags:
27972 // private
27973
27974 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>&#9660;</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>&#9664;</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>&#9654;</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"),
27975
27976 // useMenu:[const] Boolean
27977 // True if a menu should be used to select tabs when they are too
27978 // wide to fit the TabContainer, false otherwise.
27979 useMenu: true,
27980
27981 // useSlider: [const] Boolean
27982 // True if a slider should be used to select tabs when they are too
27983 // wide to fit the TabContainer, false otherwise.
27984 useSlider: true,
27985
27986 // tabStripClass: String
27987 // The css class to apply to the tab strip, if it is visible.
27988 tabStripClass: "",
27989
27990 widgetsInTemplate: true,
27991
27992 // _minScroll: Number
27993 // The distance in pixels from the edge of the tab strip which,
27994 // if a scroll animation is less than, forces the scroll to
27995 // go all the way to the left/right.
27996 _minScroll: 5,
27997
27998 attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
27999 "class": "containerNode"
28000 }),
28001
28002 postCreate: function(){
28003 this.inherited(arguments);
28004 var n = this.domNode;
28005
28006 this.scrollNode = this.tablistWrapper;
28007 this._initButtons();
28008
28009 if(!this.tabStripClass){
28010 this.tabStripClass = "dijitTabContainer" +
28011 this.tabPosition.charAt(0).toUpperCase() +
28012 this.tabPosition.substr(1).replace(/-.*/, "") +
28013 "None";
28014 dojo.addClass(n, "tabStrip-disabled")
28015 }
28016
28017 dojo.addClass(this.tablistWrapper, this.tabStripClass);
28018 },
28019
28020 onStartup: function(){
28021 this.inherited(arguments);
28022
28023 // Do not show the TabController until the related
28024 // StackController has added it's children. This gives
28025 // a less visually jumpy instantiation.
28026 dojo.style(this.domNode, "visibility", "visible");
28027 this._postStartup = true;
28028 },
28029
28030 onAddChild: function(page, insertIndex){
28031 this.inherited(arguments);
28032 var menuItem;
28033 if(this.useMenu){
28034 var containerId = this.containerId;
28035 menuItem = new dijit.MenuItem({
28036 id: page.id + "_stcMi",
28037 label: page.title,
28038 dir: page.dir,
28039 lang: page.lang,
28040 onClick: dojo.hitch(this, function(){
28041 var container = dijit.byId(containerId);
28042 container.selectChild(page);
28043 })
28044 });
28045 this._menuChildren[page.id] = menuItem;
28046 this._menu.addChild(menuItem, insertIndex);
28047 }
28048
28049 // update the menuItem label when the button label is updated
28050 this.pane2handles[page.id].push(
28051 this.connect(this.pane2button[page.id], "set", function(name, value){
28052 if(this._postStartup){
28053 if(name == "label"){
28054 if(menuItem){
28055 menuItem.set(name, value);
28056 }
28057
28058 // The changed label will have changed the width of the
28059 // buttons, so do a resize
28060 if(this._dim){
28061 this.resize(this._dim);
28062 }
28063 }
28064 }
28065 })
28066 );
28067
28068 // Increment the width of the wrapper when a tab is added
28069 // This makes sure that the buttons never wrap.
28070 // The value 200 is chosen as it should be bigger than most
28071 // Tab button widths.
28072 dojo.style(this.containerNode, "width",
28073 (dojo.style(this.containerNode, "width") + 200) + "px");
28074 },
28075
28076 onRemoveChild: function(page, insertIndex){
28077 // null out _selectedTab because we are about to delete that dom node
28078 var button = this.pane2button[page.id];
28079 if(this._selectedTab === button.domNode){
28080 this._selectedTab = null;
28081 }
28082
28083 // delete menu entry corresponding to pane that was removed from TabContainer
28084 if(this.useMenu && page && page.id && this._menuChildren[page.id]){
28085 this._menu.removeChild(this._menuChildren[page.id]);
28086 this._menuChildren[page.id].destroy();
28087 delete this._menuChildren[page.id];
28088 }
28089
28090 this.inherited(arguments);
28091 },
28092
28093 _initButtons: function(){
28094 // summary:
28095 // Creates the buttons used to scroll to view tabs that
28096 // may not be visible if the TabContainer is too narrow.
28097 this._menuChildren = {};
28098
28099 // Make a list of the buttons to display when the tab labels become
28100 // wider than the TabContainer, and hide the other buttons.
28101 // Also gets the total width of the displayed buttons.
28102 this._btnWidth = 0;
28103 this._buttons = dojo.query("> .tabStripButton", this.domNode).filter(function(btn){
28104 if((this.useMenu && btn == this._menuBtn.domNode) ||
28105 (this.useSlider && (btn == this._rightBtn.domNode || btn == this._leftBtn.domNode))){
28106 this._btnWidth += dojo.marginBox(btn).w;
28107 return true;
28108 }else{
28109 dojo.style(btn, "display", "none");
28110 return false;
28111 }
28112 }, this);
28113
28114 if(this.useMenu){
28115 // Create the menu that is used to select tabs.
28116 this._menu = new dijit.Menu({
28117 id: this.id + "_menu",
28118 dir: this.dir,
28119 lang: this.lang,
28120 targetNodeIds: [this._menuBtn.domNode],
28121 leftClickToOpen: true,
28122 refocus: false // selecting a menu item sets focus to a TabButton
28123 });
28124 this._supportingWidgets.push(this._menu);
28125 }
28126 },
28127
28128 _getTabsWidth: function(){
28129 var children = this.getChildren();
28130 if(children.length){
28131 var leftTab = children[this.isLeftToRight() ? 0 : children.length - 1].domNode,
28132 rightTab = children[this.isLeftToRight() ? children.length - 1 : 0].domNode;
28133 return rightTab.offsetLeft + dojo.style(rightTab, "width") - leftTab.offsetLeft;
28134 }else{
28135 return 0;
28136 }
28137 },
28138
28139 _enableBtn: function(width){
28140 // summary:
28141 // Determines if the tabs are wider than the width of the TabContainer, and
28142 // thus that we need to display left/right/menu navigation buttons.
28143 var tabsWidth = this._getTabsWidth();
28144 width = width || dojo.style(this.scrollNode, "width");
28145 return tabsWidth > 0 && width < tabsWidth;
28146 },
28147
28148 resize: function(dim){
28149 // summary:
28150 // Hides or displays the buttons used to scroll the tab list and launch the menu
28151 // that selects tabs.
28152
28153 if(this.domNode.offsetWidth == 0){
28154 return;
28155 }
28156
28157 // Save the dimensions to be used when a child is renamed.
28158 this._dim = dim;
28159
28160 // Set my height to be my natural height (tall enough for one row of tab labels),
28161 // and my content-box width based on margin-box width specified in dim parameter.
28162 // But first reset scrollNode.height in case it was set by layoutChildren() call
28163 // in a previous run of this method.
28164 this.scrollNode.style.height = "auto";
28165 this._contentBox = dijit.layout.marginBox2contentBox(this.domNode, {h: 0, w: dim.w});
28166 this._contentBox.h = this.scrollNode.offsetHeight;
28167 dojo.contentBox(this.domNode, this._contentBox);
28168
28169 // Show/hide the left/right/menu navigation buttons depending on whether or not they
28170 // are needed.
28171 var enable = this._enableBtn(this._contentBox.w);
28172 this._buttons.style("display", enable ? "" : "none");
28173
28174 // Position and size the navigation buttons and the tablist
28175 this._leftBtn.layoutAlign = "left";
28176 this._rightBtn.layoutAlign = "right";
28177 this._menuBtn.layoutAlign = this.isLeftToRight() ? "right" : "left";
28178 dijit.layout.layoutChildren(this.domNode, this._contentBox,
28179 [this._menuBtn, this._leftBtn, this._rightBtn, {domNode: this.scrollNode, layoutAlign: "client"}]);
28180
28181 // set proper scroll so that selected tab is visible
28182 if(this._selectedTab){
28183 if(this._anim && this._anim.status() == "playing"){
28184 this._anim.stop();
28185 }
28186 var w = this.scrollNode,
28187 sl = this._convertToScrollLeft(this._getScrollForSelectedTab());
28188 w.scrollLeft = sl;
28189 }
28190
28191 // Enable/disabled left right buttons depending on whether or not user can scroll to left or right
28192 this._setButtonClass(this._getScroll());
28193
28194 this._postResize = true;
28195 },
28196
28197 _getScroll: function(){
28198 // summary:
28199 // Returns the current scroll of the tabs where 0 means
28200 // "scrolled all the way to the left" and some positive number, based on #
28201 // of pixels of possible scroll (ex: 1000) means "scrolled all the way to the right"
28202 var sl = (this.isLeftToRight() || dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.isWebKit) ? this.scrollNode.scrollLeft :
28203 dojo.style(this.containerNode, "width") - dojo.style(this.scrollNode, "width")
28204 + (dojo.isIE == 8 ? -1 : 1) * this.scrollNode.scrollLeft;
28205 return sl;
28206 },
28207
28208 _convertToScrollLeft: function(val){
28209 // summary:
28210 // Given a scroll value where 0 means "scrolled all the way to the left"
28211 // and some positive number, based on # of pixels of possible scroll (ex: 1000)
28212 // means "scrolled all the way to the right", return value to set this.scrollNode.scrollLeft
28213 // to achieve that scroll.
28214 //
28215 // This method is to adjust for RTL funniness in various browsers and versions.
28216 if(this.isLeftToRight() || dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.isWebKit){
28217 return val;
28218 }else{
28219 var maxScroll = dojo.style(this.containerNode, "width") - dojo.style(this.scrollNode, "width");
28220 return (dojo.isIE == 8 ? -1 : 1) * (val - maxScroll);
28221 }
28222 },
28223
28224 onSelectChild: function(/*dijit._Widget*/ page){
28225 // summary:
28226 // Smoothly scrolls to a tab when it is selected.
28227
28228 var tab = this.pane2button[page.id];
28229 if(!tab || !page){return;}
28230
28231 // Scroll to the selected tab, except on startup, when scrolling is handled in resize()
28232 var node = tab.domNode;
28233 if(this._postResize && node != this._selectedTab){
28234 this._selectedTab = node;
28235
28236 var sl = this._getScroll();
28237
28238 if(sl > node.offsetLeft ||
28239 sl + dojo.style(this.scrollNode, "width") <
28240 node.offsetLeft + dojo.style(node, "width")){
28241 this.createSmoothScroll().play();
28242 }
28243 }
28244
28245 this.inherited(arguments);
28246 },
28247
28248 _getScrollBounds: function(){
28249 // summary:
28250 // Returns the minimum and maximum scroll setting to show the leftmost and rightmost
28251 // tabs (respectively)
28252 var children = this.getChildren(),
28253 scrollNodeWidth = dojo.style(this.scrollNode, "width"), // about 500px
28254 containerWidth = dojo.style(this.containerNode, "width"), // 50,000px
28255 maxPossibleScroll = containerWidth - scrollNodeWidth, // scrolling until right edge of containerNode visible
28256 tabsWidth = this._getTabsWidth();
28257
28258 if(children.length && tabsWidth > scrollNodeWidth){
28259 // Scrolling should happen
28260 return {
28261 min: this.isLeftToRight() ? 0 : children[children.length-1].domNode.offsetLeft,
28262 max: this.isLeftToRight() ?
28263 (children[children.length-1].domNode.offsetLeft + dojo.style(children[children.length-1].domNode, "width")) - scrollNodeWidth :
28264 maxPossibleScroll
28265 };
28266 }else{
28267 // No scrolling needed, all tabs visible, we stay either scrolled to far left or far right (depending on dir)
28268 var onlyScrollPosition = this.isLeftToRight() ? 0 : maxPossibleScroll;
28269 return {
28270 min: onlyScrollPosition,
28271 max: onlyScrollPosition
28272 };
28273 }
28274 },
28275
28276 _getScrollForSelectedTab: function(){
28277 // summary:
28278 // Returns the scroll value setting so that the selected tab
28279 // will appear in the center
28280 var w = this.scrollNode,
28281 n = this._selectedTab,
28282 scrollNodeWidth = dojo.style(this.scrollNode, "width"),
28283 scrollBounds = this._getScrollBounds();
28284
28285 // TODO: scroll minimal amount (to either right or left) so that
28286 // selected tab is fully visible, and just return if it's already visible?
28287 var pos = (n.offsetLeft + dojo.style(n, "width")/2) - scrollNodeWidth/2;
28288 pos = Math.min(Math.max(pos, scrollBounds.min), scrollBounds.max);
28289
28290 // TODO:
28291 // If scrolling close to the left side or right side, scroll
28292 // all the way to the left or right. See this._minScroll.
28293 // (But need to make sure that doesn't scroll the tab out of view...)
28294 return pos;
28295 },
28296
28297 createSmoothScroll : function(x){
28298 // summary:
28299 // Creates a dojo._Animation object that smoothly scrolls the tab list
28300 // either to a fixed horizontal pixel value, or to the selected tab.
28301 // description:
28302 // If an number argument is passed to the function, that horizontal
28303 // pixel position is scrolled to. Otherwise the currently selected
28304 // tab is scrolled to.
28305 // x: Integer?
28306 // An optional pixel value to scroll to, indicating distance from left.
28307
28308 // Calculate position to scroll to
28309 if(arguments.length > 0){
28310 // position specified by caller, just make sure it's within bounds
28311 var scrollBounds = this._getScrollBounds();
28312 x = Math.min(Math.max(x, scrollBounds.min), scrollBounds.max);
28313 }else{
28314 // scroll to center the current tab
28315 x = this._getScrollForSelectedTab();
28316 }
28317
28318 if(this._anim && this._anim.status() == "playing"){
28319 this._anim.stop();
28320 }
28321
28322 var self = this,
28323 w = this.scrollNode,
28324 anim = new dojo._Animation({
28325 beforeBegin: function(){
28326 if(this.curve){ delete this.curve; }
28327 var oldS = w.scrollLeft,
28328 newS = self._convertToScrollLeft(x);
28329 anim.curve = new dojo._Line(oldS, newS);
28330 },
28331 onAnimate: function(val){
28332 w.scrollLeft = val;
28333 }
28334 });
28335 this._anim = anim;
28336
28337 // Disable/enable left/right buttons according to new scroll position
28338 this._setButtonClass(x);
28339
28340 return anim; // dojo._Animation
28341 },
28342
28343 _getBtnNode: function(e){
28344 // summary:
28345 // Gets a button DOM node from a mouse click event.
28346 // e:
28347 // The mouse click event.
28348 var n = e.target;
28349 while(n && !dojo.hasClass(n, "tabStripButton")){
28350 n = n.parentNode;
28351 }
28352 return n;
28353 },
28354
28355 doSlideRight: function(e){
28356 // summary:
28357 // Scrolls the menu to the right.
28358 // e:
28359 // The mouse click event.
28360 this.doSlide(1, this._getBtnNode(e));
28361 },
28362
28363 doSlideLeft: function(e){
28364 // summary:
28365 // Scrolls the menu to the left.
28366 // e:
28367 // The mouse click event.
28368 this.doSlide(-1,this._getBtnNode(e));
28369 },
28370
28371 doSlide: function(direction, node){
28372 // summary:
28373 // Scrolls the tab list to the left or right by 75% of the widget width.
28374 // direction:
28375 // If the direction is 1, the widget scrolls to the right, if it is
28376 // -1, it scrolls to the left.
28377
28378 if(node && dojo.hasClass(node, "dijitTabDisabled")){return;}
28379
28380 var sWidth = dojo.style(this.scrollNode, "width");
28381 var d = (sWidth * 0.75) * direction;
28382
28383 var to = this._getScroll() + d;
28384
28385 this._setButtonClass(to);
28386
28387 this.createSmoothScroll(to).play();
28388 },
28389
28390 _setButtonClass: function(scroll){
28391 // summary:
28392 // Disables the left scroll button if the tabs are scrolled all the way to the left,
28393 // or the right scroll button in the opposite case.
28394 // scroll: Integer
28395 // amount of horizontal scroll
28396
28397 var scrollBounds = this._getScrollBounds();
28398 this._leftBtn.set("disabled", scroll <= scrollBounds.min);
28399 this._rightBtn.set("disabled", scroll >= scrollBounds.max);
28400 }
28401});
28402
28403dojo.declare("dijit.layout._ScrollingTabControllerButton",
28404 dijit.form.Button,
28405 {
28406 baseClass: "dijitTab tabStripButton",
28407
28408 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"),
28409
28410 // Override inherited tabIndex: 0 from dijit.form.Button, because user shouldn't be
28411 // able to tab to the left/right/menu buttons
28412 tabIndex: "-1"
28413 }
28414);
28415
28416}
28417
28418if(!dojo._hasResource["dijit.layout.TabContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
28419dojo._hasResource["dijit.layout.TabContainer"] = true;
28420dojo.provide("dijit.layout.TabContainer");
28421
28422
28423
28424
28425
28426dojo.declare("dijit.layout.TabContainer",
28427 dijit.layout._TabContainerBase,
28428 {
28429 // summary:
28430 // A Container with tabs to select each child (only one of which is displayed at a time).
28431 // description:
28432 // A TabContainer is a container that has multiple panes, but shows only
28433 // one pane at a time. There are a set of tabs corresponding to each pane,
28434 // where each tab has the name (aka title) of the pane, and optionally a close button.
28435
28436 // useMenu: [const] Boolean
28437 // True if a menu should be used to select tabs when they are too
28438 // wide to fit the TabContainer, false otherwise.
28439 useMenu: true,
28440
28441 // useSlider: [const] Boolean
28442 // True if a slider should be used to select tabs when they are too
28443 // wide to fit the TabContainer, false otherwise.
28444 useSlider: true,
28445
28446 // controllerWidget: String
28447 // An optional parameter to override the widget used to display the tab labels
28448 controllerWidget: "",
28449
28450 _makeController: function(/*DomNode*/ srcNode){
28451 // summary:
28452 // Instantiate tablist controller widget and return reference to it.
28453 // Callback from _TabContainerBase.postCreate().
28454 // tags:
28455 // protected extension
28456
28457 var cls = this.baseClass + "-tabs" + (this.doLayout ? "" : " dijitTabNoLayout"),
28458 TabController = dojo.getObject(this.controllerWidget);
28459
28460 return new TabController({
28461 id: this.id + "_tablist",
28462 dir: this.dir,
28463 lang: this.lang,
28464 tabPosition: this.tabPosition,
28465 doLayout: this.doLayout,
28466 containerId: this.id,
28467 "class": cls,
28468 nested: this.nested,
28469 useMenu: this.useMenu,
28470 useSlider: this.useSlider,
28471 tabStripClass: this.tabStrip ? this.baseClass + (this.tabStrip ? "":"No") + "Strip": null
28472 }, srcNode);
28473 },
28474
28475 postMixInProperties: function(){
28476 this.inherited(arguments);
28477
28478 // Scrolling controller only works for horizontal non-nested tabs
28479 if(!this.controllerWidget){
28480 this.controllerWidget = (this.tabPosition == "top" || this.tabPosition == "bottom") && !this.nested ?
28481 "dijit.layout.ScrollingTabController" : "dijit.layout.TabController";
28482 }
28483 }
28484});
28485
28486
28487}
28488
28489if(!dojo._hasResource["dijit.dijit-all"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
28490dojo._hasResource["dijit.dijit-all"] = true;
28491console.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");
28492dojo.provide("dijit.dijit-all");
28493
28494/*=====
28495dijit["dijit-all"] = {
28496 // summary:
28497 // A rollup that includes every dijit. You probably don't need this.
28498};
28499=====*/
28500
28501
28502
28503
28504
28505
28506
28507
28508
28509
28510// Editor
28511
28512
28513
28514
28515
28516
28517
28518
28519
28520
28521
28522
28523
28524
28525
28526
28527
28528
28529
28530// Form widgets
28531
28532
28533// Button widgets
28534
28535
28536
28537
28538
28539
28540
28541// TextBox widgets
28542
28543
28544
28545
28546
28547
28548
28549// Select widgets
28550
28551
28552
28553
28554
28555// Slider widgets
28556
28557
28558
28559
28560
28561
28562
28563// Textarea widgets
28564
28565
28566
28567// Layout widgets
28568
28569
28570
28571 //deprecated
28572
28573 //deprecated
28574
28575
28576
28577}
28578
28579
28580dojo.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"]);