[Pkg-javascript-commits] [dojo] 12/20: Imported Upstream version 1.10.2+dfsg
David Prévot
taffit at moszumanska.debian.org
Mon Oct 20 17:39:42 UTC 2014
This is an automated email from the git hooks/post-receive script.
taffit pushed a commit to branch master
in repository dojo.
commit 87c6269733cad33aef770bef949280f05dca49c2
Merge: 5c20664 5f298fe
Author: David Prévot <taffit at debian.org>
Date: Mon Oct 20 09:54:59 2014 -0400
Imported Upstream version 1.10.2+dfsg
dijit/_editor/RichText.js | 24 +--
dijit/package.json | 4 +-
dijit/tests/form/robot/Select.html | 98 +++++++--
dijit/tests/form/test_Select.html | 220 +++++++--------------
dojo/CONTRIBUTING.md | 10 +-
dojo/_base/kernel.js | 4 +-
dojo/debounce.js | 3 +-
dojo/dom-attr.js | 2 +-
dojo/mouse.js | 2 +-
dojo/on/asyncEventListener.js | 2 +-
dojo/on/debounce.js | 2 +-
dojo/on/throttle.js | 2 +-
dojo/package.json | 2 +-
dojo/promise/instrumentation.js | 25 ++-
dojo/tests/dom-attr.html | 41 ++++
dojo/tests/dom-attr.js | 3 +
dojo/tests/module.js | 1 +
dojox/app/ViewBase.js | 3 +-
dojox/app/controllers/HistoryHash.js | 0
dojox/app/controllers/Layout.js | 9 +-
dojox/app/main.js | 4 +-
dojox/app/tests/domOrderByConstraint/main/main.js | 2 +-
dojox/app/tests/domOrderByConstraint/main/main2.js | 2 +-
dojox/app/tests/images/a-icon-1-41x41.png | Bin 1237 -> 3886 bytes
dojox/app/utils/hash.js | 0
dojox/calendar/CONTRIBUTING.md | 0
dojox/calendar/ColumnViewSecondarySheet.js | 5 +-
dojox/calendar/LICENSE | 0
dojox/calendar/nls/bg/buttons.js | 0
dojox/calendar/nls/he/buttons.js | 0
dojox/calendar/nls/hr/buttons.js | 0
dojox/calendar/nls/uk/buttons.js | 0
dojox/calendar/tests/hcalendar.html | 0
dojox/date/islamic/Date.js | 2 +-
dojox/date/tests/islamic/Date.js | 9 +-
dojox/dgauges/CONTRIBUTING.md | 0
dojox/form/BusyButton.js | 23 ++-
dojox/form/FileInputAuto.js | 1 +
dojox/form/tests/UploadFile.php.disabled | 14 +-
dojox/layout/ContentPane.js | 2 +-
dojox/layout/FloatingPane.js | 2 +-
dojox/mobile/IconItem.js | 2 +-
dojox/package.json | 6 +-
dojox/widget/Toaster.js | 6 +-
util/build/buildControl.js | 14 +-
util/build/version.js | 4 +-
util/doh/_nodeRunner.js | 2 +-
util/doh/_rhinoRunner.js | 2 +-
util/doh/mobileRunner.html | 2 +-
util/doh/package.json | 2 +-
util/doh/runner.html | 2 +-
util/package.json | 2 +-
52 files changed, 326 insertions(+), 241 deletions(-)
diff --cc dijit/_editor/RichText.js
index 364a23f,0000000..f33da82
mode 100644,000000..100644
--- a/dijit/_editor/RichText.js
+++ b/dijit/_editor/RichText.js
@@@ -1,3010 -1,0 +1,3010 @@@
+define([
+ "dojo/_base/array", // array.forEach array.indexOf array.some
+ "dojo/_base/config", // config
+ "dojo/_base/declare", // declare
+ "dojo/_base/Deferred", // Deferred
+ "dojo/dom", // dom.byId
+ "dojo/dom-attr", // domAttr.set or get
+ "dojo/dom-class", // domClass.add domClass.remove
+ "dojo/dom-construct", // domConstruct.create domConstruct.destroy domConstruct.place
+ "dojo/dom-geometry", // domGeometry.position
+ "dojo/dom-style", // domStyle.getComputedStyle domStyle.set
+ "dojo/_base/kernel", // kernel.deprecated
+ "dojo/keys", // keys.BACKSPACE keys.TAB
+ "dojo/_base/lang", // lang.clone lang.hitch lang.isArray lang.isFunction lang.isString lang.trim
+ "dojo/on", // on()
+ "dojo/query", // query
+ "dojo/domReady",
+ "dojo/sniff", // has("ie") has("mozilla") has("opera") has("safari") has("webkit")
+ "dojo/topic", // topic.publish() (publish)
+ "dojo/_base/unload", // unload
+ "dojo/_base/url", // url
+ "dojo/window", // winUtils.get()
+ "../_Widget",
+ "../_CssStateMixin",
+ "../selection",
+ "./range",
+ "./html",
+ "../focus",
+ "../main" // dijit._scopeName
+], function(array, config, declare, Deferred, dom, domAttr, domClass, domConstruct, domGeometry, domStyle,
+ kernel, keys, lang, on, query, domReady, has, topic, unload, _Url, winUtils,
+ _Widget, _CssStateMixin, selectionapi, rangeapi, htmlapi, focus, dijit){
+
+ // module:
+ // dijit/_editor/RichText
+
+ // If you want to allow for rich text saving with back/forward actions, you must add a text area to your page with
+ // the id==dijit._scopeName + "._editor.RichText.value" (typically "dijit/_editor/RichText.value). For example,
+ // something like this will work:
+ //
+ // <textarea id="dijit._editor.RichText.value" style="display:none;position:absolute;top:-100px;left:-100px;height:3px;width:3px;overflow:hidden;"></textarea>
+
+ var RichText = declare("dijit._editor.RichText", [_Widget, _CssStateMixin], {
+ // summary:
+ // dijit/_editor/RichText is the core of dijit.Editor, which provides basic
+ // WYSIWYG editing features.
+ //
+ // description:
+ // dijit/_editor/RichText is the core of dijit.Editor, which provides basic
+ // WYSIWYG editing features. It also encapsulates the differences
+ // of different js engines for various browsers. Do not use this widget
+ // with an HTML <TEXTAREA> tag, since the browser unescapes XML escape characters,
+ // like <. This can have unexpected behavior and lead to security issues
+ // such as scripting attacks.
+ //
+ // tags:
+ // private
+
+ constructor: function(params /*===== , srcNodeRef =====*/){
+ // summary:
+ // Create the widget.
+ // params: Object|null
+ // Initial settings for any of the widget attributes, except readonly attributes.
+ // srcNodeRef: DOMNode
+ // The widget replaces the specified DOMNode.
+
+ // contentPreFilters: Function(String)[]
+ // Pre content filter function register array.
+ // these filters will be executed before the actual
+ // editing area gets the html content.
+ this.contentPreFilters = [];
+
+ // contentPostFilters: Function(String)[]
+ // post content filter function register array.
+ // These will be used on the resulting html
+ // from contentDomPostFilters. The resulting
+ // content is the final html (returned by getValue()).
+ this.contentPostFilters = [];
+
+ // contentDomPreFilters: Function(DomNode)[]
+ // Pre content dom filter function register array.
+ // These filters are applied after the result from
+ // contentPreFilters are set to the editing area.
+ this.contentDomPreFilters = [];
+
+ // contentDomPostFilters: Function(DomNode)[]
+ // Post content dom filter function register array.
+ // These filters are executed on the editing area dom.
+ // The result from these will be passed to contentPostFilters.
+ this.contentDomPostFilters = [];
+
+ // editingAreaStyleSheets: dojo._URL[]
+ // array to store all the stylesheets applied to the editing area
+ this.editingAreaStyleSheets = [];
+
+ // Make a copy of this.events before we start writing into it, otherwise we
+ // will modify the prototype which leads to bad things on pages w/multiple editors
+ this.events = [].concat(this.events);
+
+ this._keyHandlers = {};
+
+ if(params && lang.isString(params.value)){
+ this.value = params.value;
+ }
+
+ this.onLoadDeferred = new Deferred();
+ },
+
+ baseClass: "dijitEditor",
+
+ // inheritWidth: Boolean
+ // whether to inherit the parent's width or simply use 100%
+ inheritWidth: false,
+
+ // focusOnLoad: [deprecated] Boolean
+ // Focus into this widget when the page is loaded
+ focusOnLoad: false,
+
+ // name: String?
+ // Specifies the name of a (hidden) `<textarea>` node on the page that's used to save
+ // the editor content on page leave. Used to restore editor contents after navigating
+ // to a new page and then hitting the back button.
+ name: "",
+
+ // styleSheets: [const] String
+ // semicolon (";") separated list of css files for the editing area
+ styleSheets: "",
+
+ // height: String
+ // Set height to fix the editor at a specific height, with scrolling.
+ // By default, this is 300px. If you want to have the editor always
+ // resizes to accommodate the content, use AlwaysShowToolbar plugin
+ // and set height="". If this editor is used within a layout widget,
+ // set height="100%".
+ height: "300px",
+
+ // minHeight: String
+ // The minimum height that the editor should have.
+ minHeight: "1em",
+
+ // isClosed: [private] Boolean
+ isClosed: true,
+
+ // isLoaded: [private] Boolean
+ isLoaded: false,
+
+ // _SEPARATOR: [private] String
+ // Used to concat contents from multiple editors into a single string,
+ // so they can be saved into a single `<textarea>` node. See "name" attribute.
+ _SEPARATOR: "@@**%%__RICHTEXTBOUNDRY__%%**@@",
+
+ // _NAME_CONTENT_SEP: [private] String
+ // USed to separate name from content. Just a colon isn't safe.
+ _NAME_CONTENT_SEP: "@@**%%:%%**@@",
+
+ // onLoadDeferred: [readonly] dojo/promise/Promise
+ // Deferred which is fired when the editor finishes loading.
+ // Call myEditor.onLoadDeferred.then(callback) it to be informed
+ // when the rich-text area initialization is finalized.
+ onLoadDeferred: null,
+
+ // isTabIndent: Boolean
+ // Make tab key and shift-tab indent and outdent rather than navigating.
+ // Caution: sing this makes web pages inaccessible to users unable to use a mouse.
+ isTabIndent: false,
+
+ // disableSpellCheck: [const] Boolean
+ // When true, disables the browser's native spell checking, if supported.
+ // Works only in Firefox.
+ disableSpellCheck: false,
+
+ postCreate: function(){
+ if("textarea" === this.domNode.tagName.toLowerCase()){
+ console.warn("RichText should not be used with the TEXTAREA tag. See dijit._editor.RichText docs.");
+ }
+
+ // Push in the builtin filters now, making them the first executed, but not over-riding anything
+ // users passed in. See: #6062
+ this.contentPreFilters = [
+ lang.trim, // avoid IE10 problem hitting ENTER on last line when there's a trailing \n.
+ lang.hitch(this, "_preFixUrlAttributes")
+ ].concat(this.contentPreFilters);
+ if(has("mozilla")){
+ this.contentPreFilters = [this._normalizeFontStyle].concat(this.contentPreFilters);
+ this.contentPostFilters = [this._removeMozBogus].concat(this.contentPostFilters);
+ }
+ if(has("webkit")){
+ // Try to clean up WebKit bogus artifacts. The inserted classes
+ // made by WebKit sometimes messes things up.
+ this.contentPreFilters = [this._removeWebkitBogus].concat(this.contentPreFilters);
+ this.contentPostFilters = [this._removeWebkitBogus].concat(this.contentPostFilters);
+ }
+ if(has("ie") || has("trident")){
+ // IE generates <strong> and <em> but we want to normalize to <b> and <i>
+ // Still happens in IE11!
+ this.contentPostFilters = [this._normalizeFontStyle].concat(this.contentPostFilters);
+ this.contentDomPostFilters = [lang.hitch(this, "_stripBreakerNodes")].concat(this.contentDomPostFilters);
+ }
+ this.contentDomPostFilters = [lang.hitch(this, "_stripTrailingEmptyNodes")].concat(this.contentDomPostFilters);
+ this.inherited(arguments);
+
+ topic.publish(dijit._scopeName + "._editor.RichText::init", this);
+ },
+
+ startup: function(){
+ this.inherited(arguments);
+
+ // Don't call open() until startup() because we need to be attached to the DOM, and also if we are the
+ // child of a StackContainer, let StackContainer._setupChild() do DOM manipulations before iframe is
+ // created, to avoid duplicate onload call.
+ this.open();
+ this.setupDefaultShortcuts();
+ },
+
+ setupDefaultShortcuts: function(){
+ // summary:
+ // Add some default key handlers
+ // description:
+ // Overwrite this to setup your own handlers. The default
+ // implementation does not use Editor commands, but directly
+ // executes the builtin commands within the underlying browser
+ // support.
+ // tags:
+ // protected
+ var exec = lang.hitch(this, function(cmd, arg){
+ return function(){
+ return !this.execCommand(cmd, arg);
+ };
+ });
+
+ var ctrlKeyHandlers = {
+ b: exec("bold"),
+ i: exec("italic"),
+ u: exec("underline"),
+ a: exec("selectall"),
+ s: function(){
+ this.save(true);
+ },
+ m: function(){
+ this.isTabIndent = !this.isTabIndent;
+ },
+
+ "1": exec("formatblock", "h1"),
+ "2": exec("formatblock", "h2"),
+ "3": exec("formatblock", "h3"),
+ "4": exec("formatblock", "h4"),
+
+ "\\": exec("insertunorderedlist")
+ };
+
+ if(!has("ie")){
+ ctrlKeyHandlers.Z = exec("redo"); //FIXME: undo?
+ }
+
+ var key;
+ for(key in ctrlKeyHandlers){
+ this.addKeyHandler(key, true, false, ctrlKeyHandlers[key]);
+ }
+ },
+
+ // events: [private] String[]
+ // events which should be connected to the underlying editing area
+ events: ["onKeyDown", "onKeyUp"], // onClick handled specially
+
+ // captureEvents: [deprecated] String[]
+ // Events which should be connected to the underlying editing
+ // area, events in this array will be addListener with
+ // capture=true.
+ // TODO: looking at the code I don't see any distinction between events and captureEvents,
+ // so get rid of this for 2.0 if not sooner
+ captureEvents: [],
+
+ _editorCommandsLocalized: false,
+ _localizeEditorCommands: function(){
+ // summary:
+ // When IE is running in a non-English locale, the API actually changes,
+ // so that we have to say (for example) danraku instead of p (for paragraph).
+ // Handle that here.
+ // tags:
+ // private
+ if(RichText._editorCommandsLocalized){
+ // Use the already generate cache of mappings.
+ this._local2NativeFormatNames = RichText._local2NativeFormatNames;
+ this._native2LocalFormatNames = RichText._native2LocalFormatNames;
+ return;
+ }
+ RichText._editorCommandsLocalized = true;
+ RichText._local2NativeFormatNames = {};
+ RichText._native2LocalFormatNames = {};
+ this._local2NativeFormatNames = RichText._local2NativeFormatNames;
+ this._native2LocalFormatNames = RichText._native2LocalFormatNames;
+ //in IE, names for blockformat is locale dependent, so we cache the values here
+
+ //put p after div, so if IE returns Normal, we show it as paragraph
+ //We can distinguish p and div if IE returns Normal, however, in order to detect that,
+ //we have to call this.document.selection.createRange().parentElement() or such, which
+ //could slow things down. Leave it as it is for now
+ var formats = ['div', 'p', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul', 'address'];
+ var localhtml = "", format, i = 0;
+ while((format = formats[i++])){
+ //append a <br> after each element to separate the elements more reliably
+ if(format.charAt(1) !== 'l'){
+ localhtml += "<" + format + "><span>content</span></" + format + "><br/>";
+ }else{
+ localhtml += "<" + format + "><li>content</li></" + format + "><br/>";
+ }
+ }
+ // queryCommandValue returns empty if we hide editNode, so move it out of screen temporary
+ // Also, IE9 does weird stuff unless we do it inside the editor iframe.
+ var style = { position: "absolute", top: "0px", zIndex: 10, opacity: 0.01 };
+ var div = domConstruct.create('div', {style: style, innerHTML: localhtml});
+ this.ownerDocumentBody.appendChild(div);
+
+ // IE9 has a timing issue with doing this right after setting
+ // the inner HTML, so put a delay in.
+ var inject = lang.hitch(this, function(){
+ var node = div.firstChild;
+ while(node){
+ try{
+ this.selection.selectElement(node.firstChild);
+ var nativename = node.tagName.toLowerCase();
+ this._local2NativeFormatNames[nativename] = document.queryCommandValue("formatblock");
+ this._native2LocalFormatNames[this._local2NativeFormatNames[nativename]] = nativename;
+ node = node.nextSibling.nextSibling;
+ //console.log("Mapped: ", nativename, " to: ", this._local2NativeFormatNames[nativename]);
+ }catch(e){ /*Sqelch the occasional IE9 error */
+ }
+ }
+ domConstruct.destroy(div);
+ });
+ this.defer(inject);
+ },
+
+ open: function(/*DomNode?*/ element){
+ // summary:
+ // Transforms the node referenced in this.domNode into a rich text editing
+ // node.
+ // description:
+ // Sets up the editing area asynchronously. This will result in
+ // the creation and replacement with an iframe.
+ // tags:
+ // private
+
+ if(!this.onLoadDeferred || this.onLoadDeferred.fired >= 0){
+ this.onLoadDeferred = new Deferred();
+ }
+
+ if(!this.isClosed){
+ this.close();
+ }
+ topic.publish(dijit._scopeName + "._editor.RichText::open", this);
+
+ if(arguments.length === 1 && element.nodeName){ // else unchanged
+ this.domNode = element;
+ }
+
+ var dn = this.domNode;
+
+ // Compute initial value of the editor
+ var html;
+ if(lang.isString(this.value)){
+ // Allow setting the editor content programmatically instead of
+ // relying on the initial content being contained within the target
+ // domNode.
+ html = this.value;
+ dn.innerHTML = "";
+ }else if(dn.nodeName && dn.nodeName.toLowerCase() == "textarea"){
+ // if we were created from a textarea, then we need to create a
+ // new editing harness node.
+ var ta = (this.textarea = dn);
+ this.name = ta.name;
+ html = ta.value;
+ dn = this.domNode = this.ownerDocument.createElement("div");
+ dn.setAttribute('widgetId', this.id);
+ ta.removeAttribute('widgetId');
+ dn.cssText = ta.cssText;
+ dn.className += " " + ta.className;
+ domConstruct.place(dn, ta, "before");
+ var tmpFunc = lang.hitch(this, function(){
+ //some browsers refuse to submit display=none textarea, so
+ //move the textarea off screen instead
+ domStyle.set(ta, {
+ display: "block",
+ position: "absolute",
+ top: "-1000px"
+ });
+
+ if(has("ie")){ //nasty IE bug: abnormal formatting if overflow is not hidden
+ var s = ta.style;
+ this.__overflow = s.overflow;
+ s.overflow = "hidden";
+ }
+ });
+ if(has("ie")){
+ this.defer(tmpFunc, 10);
+ }else{
+ tmpFunc();
+ }
+
+ if(ta.form){
+ var resetValue = ta.value;
+ this.reset = function(){
+ var current = this.getValue();
+ if(current !== resetValue){
+ this.replaceValue(resetValue);
+ }
+ };
+ on(ta.form, "submit", lang.hitch(this, function(){
+ // Copy value to the <textarea> so it gets submitted along with form.
+ // FIXME: should we be calling close() here instead?
+ domAttr.set(ta, 'disabled', this.disabled); // don't submit the value if disabled
+ ta.value = this.getValue();
+ }));
+ }
+ }else{
+ html = htmlapi.getChildrenHtml(dn);
+ dn.innerHTML = "";
+ }
+ this.value = html;
+
+ // If we're a list item we have to put in a blank line to force the
+ // bullet to nicely align at the top of text
+ if(dn.nodeName && dn.nodeName === "LI"){
+ dn.innerHTML = " <br>";
+ }
+
+ // Construct the editor div structure.
+ this.header = dn.ownerDocument.createElement("div");
+ dn.appendChild(this.header);
+ this.editingArea = dn.ownerDocument.createElement("div");
+ dn.appendChild(this.editingArea);
+ this.footer = dn.ownerDocument.createElement("div");
+ dn.appendChild(this.footer);
+
+ if(!this.name){
+ this.name = this.id + "_AUTOGEN";
+ }
+
+ // User has pressed back/forward button so we lost the text in the editor, but it's saved
+ // in a hidden <textarea> (which contains the data for all the editors on this page),
+ // so get editor value from there
+ if(this.name !== "" && (!config["useXDomain"] || config["allowXdRichTextSave"])){
+ var saveTextarea = dom.byId(dijit._scopeName + "._editor.RichText.value");
+ if(saveTextarea && saveTextarea.value !== ""){
+ var datas = saveTextarea.value.split(this._SEPARATOR), i = 0, dat;
+ while((dat = datas[i++])){
+ var data = dat.split(this._NAME_CONTENT_SEP);
+ if(data[0] === this.name){
+ this.value = data[1];
+ datas = datas.splice(i, 1);
+ saveTextarea.value = datas.join(this._SEPARATOR);
+ break;
+ }
+ }
+ }
+
+ if(!RichText._globalSaveHandler){
+ RichText._globalSaveHandler = {};
+ unload.addOnUnload(function(){
+ var id;
+ for(id in RichText._globalSaveHandler){
+ var f = RichText._globalSaveHandler[id];
+ if(lang.isFunction(f)){
+ f();
+ }
+ }
+ });
+ }
+ RichText._globalSaveHandler[this.id] = lang.hitch(this, "_saveContent");
+ }
+
+ this.isClosed = false;
+
+ var ifr = (this.editorObject = this.iframe = this.ownerDocument.createElement('iframe'));
+ ifr.id = this.id + "_iframe";
+ ifr.style.border = "none";
+ ifr.style.width = "100%";
+ if(this._layoutMode){
+ // iframe should be 100% height, thus getting it's height from surrounding
+ // <div> (which has the correct height set by Editor)
+ ifr.style.height = "100%";
+ }else{
+ if(has("ie") >= 7){
+ if(this.height){
+ ifr.style.height = this.height;
+ }
+ if(this.minHeight){
+ ifr.style.minHeight = this.minHeight;
+ }
+ }else{
+ ifr.style.height = this.height ? this.height : this.minHeight;
+ }
+ }
+ ifr.frameBorder = 0;
+ ifr._loadFunc = lang.hitch(this, function(w){
+ // This method is called when the editor is first loaded and also if the Editor's
+ // dom node is repositioned. Unfortunately repositioning the Editor tends to
+ // clear the iframe's contents, so we can't just no-op in that case.
+
+ this.window = w;
+ this.document = w.document;
+
+ // instantiate class to access selected text in editor's iframe
+ this.selection = new selectionapi.SelectionManager(w);
+
+ if(has("ie")){
+ this._localizeEditorCommands();
+ }
+
+ // Do final setup and set contents of editor.
+ // Use get("value") rather than html in case _loadFunc() is being called for a second time
+ // because editor's DOMNode was repositioned.
+ this.onLoad(this.get("value"));
+ });
+
+ // Attach iframe to document, and set the initial (blank) content.
+ var src = this._getIframeDocTxt().replace(/\\/g, "\\\\").replace(/'/g, "\\'"),
+ s;
+
+ // IE10 and earlier will throw an "Access is denied" error when attempting to access the parent frame if
+ // document.domain has been set, unless the child frame also has the same document.domain set. In some
+ // cases, we can only set document.domain while the document is being constructed using open/write/close;
+ // attempting to set it later results in a different "This method can't be used in this context" error.
+ // However, in at least IE9-10, sometimes the parent.window check will succeed and the access failure will
+ // only happen later when trying to access frameElement, so there is an additional check and fix there
+ // as well. See #17529
+ if (has("ie") < 11) {
+ s = 'javascript:document.open();try{parent.window;}catch(e){document.domain="' + document.domain + '";}' +
+ 'document.write(\'' + src + '\');document.close()';
+ }
+ else {
+ s = "javascript: '" + src + "'";
+ }
+
+ if(has("ie") == 9){
+ // On IE9, attach to document before setting the content, to avoid problem w/iframe running in
+ // wrong security context, see #16633.
+ this.editingArea.appendChild(ifr);
+ ifr.src = s;
+ }else{
+ // For other browsers, set src first, especially for IE6/7 where attaching first gives a warning on
+ // https:// about "this page contains secure and insecure items, do you want to view both?"
+ ifr.setAttribute('src', s);
+ this.editingArea.appendChild(ifr);
+ }
+
+ // TODO: this is a guess at the default line-height, kinda works
+ if(dn.nodeName === "LI"){
+ dn.lastChild.style.marginTop = "-1.2em";
+ }
+
+ domClass.add(this.domNode, this.baseClass);
+ },
+
+ //static cache variables shared among all instance of this class
+ _local2NativeFormatNames: {},
+ _native2LocalFormatNames: {},
+
+ _getIframeDocTxt: function(){
+ // summary:
+ // Generates the boilerplate text of the document inside the iframe (ie, `<html><head>...</head><body/></html>`).
+ // Editor content (if not blank) should be added afterwards.
+ // tags:
+ // private
+ var _cs = domStyle.getComputedStyle(this.domNode);
+
+ // The contents inside of <body>. The real contents are set later via a call to setValue().
+ // In auto-expand mode, need a wrapper div for AlwaysShowToolbar plugin to correctly
+ // expand/contract the editor as the content changes.
+ var html = "<div id='dijitEditorBody'></div>";
+
+ var font = [ _cs.fontWeight, _cs.fontSize, _cs.fontFamily ].join(" ");
+
+ // line height is tricky - applying a units value will mess things up.
+ // if we can't get a non-units value, bail out.
+ var lineHeight = _cs.lineHeight;
+ if(lineHeight.indexOf("px") >= 0){
+ lineHeight = parseFloat(lineHeight) / parseFloat(_cs.fontSize);
+ // console.debug(lineHeight);
+ }else if(lineHeight.indexOf("em") >= 0){
+ lineHeight = parseFloat(lineHeight);
+ }else{
+ // If we can't get a non-units value, just default
+ // it to the CSS spec default of 'normal'. Seems to
+ // work better, esp on IE, than '1.0'
+ lineHeight = "normal";
+ }
+ var userStyle = "";
+ var self = this;
+ this.style.replace(/(^|;)\s*(line-|font-?)[^;]+/ig, function(match){
+ match = match.replace(/^;/ig, "") + ';';
+ var s = match.split(":")[0];
+ if(s){
+ s = lang.trim(s);
+ s = s.toLowerCase();
+ var i;
+ var sC = "";
+ for(i = 0; i < s.length; i++){
+ var c = s.charAt(i);
+ switch(c){
+ case "-":
+ i++;
+ c = s.charAt(i).toUpperCase();
+ default:
+ sC += c;
+ }
+ }
+ domStyle.set(self.domNode, sC, "");
+ }
+ userStyle += match + ';';
+ });
+
+
+ // need to find any associated label element, aria-label, or aria-labelledby and update iframe document title
+ var label = query('label[for="' + this.id + '"]');
+ var title = "";
+ if(label.length){
+ title = label[0].innerHTML;
+ }else if(this["aria-label"]){
+ title = this["aria-label"];
+ }else if(this["aria-labelledby"]){
+ title = dom.byId(this["aria-labelledby"]).innerHTML;
+ }
+
+ // Now that we have the title, also set it as the title attribute on the iframe
+ this.iframe.setAttribute("title", title);
+
+ return [
+ "<!DOCTYPE html>",
+ this.isLeftToRight() ? "<html lang='" + this.lang + "'>\n<head>\n" : "<html dir='rtl' lang='" + this.lang + "'>\n<head>\n",
+ title ? "<title>" + title + "</title>" : "",
+ "<meta http-equiv='Content-Type' content='text/html'>\n",
+ "<style>\n",
+ "\tbody,html {\n",
+ "\t\tbackground:transparent;\n",
+ "\t\tpadding: 1px 0 0 0;\n",
+ "\t\tmargin: -1px 0 0 0;\n", // remove extraneous vertical scrollbar on safari and firefox
+ "\t}\n",
+ "\tbody,html,#dijitEditorBody { outline: none; }",
+
+ // Set <body> to expand to full size of editor, so clicking anywhere will work.
+ // Except in auto-expand mode, in which case the editor expands to the size of <body>.
+ // Also determine how scrollers should be applied. In autoexpand mode (height = "") no scrollers on y at all.
+ // But in fixed height mode we want both x/y scrollers.
+ // Scrollers go on <body> since it's been set to height: 100%.
+ "html { height: 100%; width: 100%; overflow: hidden; }\n", // scroll bar is on #dijitEditorBody, shouldn't be on <html>
+ this.height ? "\tbody,#dijitEditorBody { height: 100%; width: 100%; overflow: auto; }\n" :
+ "\tbody,#dijitEditorBody { min-height: " + this.minHeight + "; width: 100%; overflow-x: auto; overflow-y: hidden; }\n",
+
+ // TODO: left positioning will cause contents to disappear out of view
+ // if it gets too wide for the visible area
+ "\tbody{\n",
+ "\t\ttop:0px;\n",
+ "\t\tleft:0px;\n",
+ "\t\tright:0px;\n",
+ "\t\tfont:", font, ";\n",
+ ((this.height || has("opera")) ? "" : "\t\tposition: fixed;\n"),
+ "\t\tline-height:", lineHeight, ";\n",
+ "\t}\n",
+ "\tp{ margin: 1em 0; }\n",
+
+ "\tli > ul:-moz-first-node, li > ol:-moz-first-node{ padding-top: 1.2em; }\n",
- // Can't set min-height in IE9, it puts layout on li, which puts move/resize handles.
- (!has("ie") ? "\tli{ min-height:1.2em; }\n" : ""),
++ // Can't set min-height in IE>=9, it puts layout on li, which puts move/resize handles.
++ (has("ie") || has("trident") ? "" : "\tli{ min-height:1.2em; }\n"),
+ "</style>\n",
+ this._applyEditingAreaStyleSheets(), "\n",
+ "</head>\n<body role='main' ",
+
+ // Onload handler fills in real editor content.
+ // On IE9, sometimes onload is called twice, and the first time frameElement is null (test_FullScreen.html)
+ // On IE9-10, it is also possible that accessing window.parent in the initial creation of the
+ // iframe DOM will succeed, but trying to access window.frameElement will fail, in which case we
+ // *can* set the domain without a "This method can't be used in this context" error. See #17529
+ "onload='try{frameElement && frameElement._loadFunc(window,document)}catch(e){document.domain=\"" + document.domain + "\";frameElement._loadFunc(window,document)}' ",
+ "style='" + userStyle + "'>", html, "</body>\n</html>"
+ ].join(""); // String
+ },
+
+ _applyEditingAreaStyleSheets: function(){
+ // summary:
+ // apply the specified css files in styleSheets
+ // tags:
+ // private
+ var files = [];
+ if(this.styleSheets){
+ files = this.styleSheets.split(';');
+ this.styleSheets = '';
+ }
+
+ //empty this.editingAreaStyleSheets here, as it will be filled in addStyleSheet
+ files = files.concat(this.editingAreaStyleSheets);
+ this.editingAreaStyleSheets = [];
+
+ var text = '', i = 0, url, ownerWindow = winUtils.get(this.ownerDocument);
+ while((url = files[i++])){
+ var abstring = (new _Url(ownerWindow.location, url)).toString();
+ this.editingAreaStyleSheets.push(abstring);
+ text += '<link rel="stylesheet" type="text/css" href="' + abstring + '"/>';
+ }
+ return text;
+ },
+
+ addStyleSheet: function(/*dojo/_base/url*/ uri){
+ // summary:
+ // add an external stylesheet for the editing area
+ // uri:
+ // Url of the external css file
+ var url = uri.toString(), ownerWindow = winUtils.get(this.ownerDocument);
+
+ //if uri is relative, then convert it to absolute so that it can be resolved correctly in iframe
+ if(url.charAt(0) === '.' || (url.charAt(0) !== '/' && !uri.host)){
+ url = (new _Url(ownerWindow.location, url)).toString();
+ }
+
+ if(array.indexOf(this.editingAreaStyleSheets, url) > -1){
+// console.debug("dijit/_editor/RichText.addStyleSheet(): Style sheet "+url+" is already applied");
+ return;
+ }
+
+ this.editingAreaStyleSheets.push(url);
+ this.onLoadDeferred.then(lang.hitch(this, function(){
+ if(this.document.createStyleSheet){ //IE
+ this.document.createStyleSheet(url);
+ }else{ //other browser
+ var head = this.document.getElementsByTagName("head")[0];
+ var stylesheet = this.document.createElement("link");
+ stylesheet.rel = "stylesheet";
+ stylesheet.type = "text/css";
+ stylesheet.href = url;
+ head.appendChild(stylesheet);
+ }
+ }));
+ },
+
+ removeStyleSheet: function(/*dojo/_base/url*/ uri){
+ // summary:
+ // remove an external stylesheet for the editing area
+ var url = uri.toString(), ownerWindow = winUtils.get(this.ownerDocument);
+ //if uri is relative, then convert it to absolute so that it can be resolved correctly in iframe
+ if(url.charAt(0) === '.' || (url.charAt(0) !== '/' && !uri.host)){
+ url = (new _Url(ownerWindow.location, url)).toString();
+ }
+ var index = array.indexOf(this.editingAreaStyleSheets, url);
+ if(index === -1){
+// console.debug("dijit/_editor/RichText.removeStyleSheet(): Style sheet "+url+" has not been applied");
+ return;
+ }
+ delete this.editingAreaStyleSheets[index];
+ query('link[href="' + url + '"]', this.window.document).orphan();
+ },
+
+ // disabled: Boolean
+ // The editor is disabled; the text cannot be changed.
+ disabled: false,
+
+ _mozSettingProps: {'styleWithCSS': false},
+ _setDisabledAttr: function(/*Boolean*/ value){
+ value = !!value;
+ this._set("disabled", value);
+ if(!this.isLoaded){
+ return;
+ } // this method requires init to be complete
+ var preventIEfocus = has("ie") && (this.isLoaded || !this.focusOnLoad);
+ if(preventIEfocus){
+ this.editNode.unselectable = "on";
+ }
+ this.editNode.contentEditable = !value;
+ this.editNode.tabIndex = value ? "-1" : this.tabIndex;
+ if(preventIEfocus){
+ this.defer(function(){
+ if(this.editNode){ // guard in case widget destroyed before timeout
+ this.editNode.unselectable = "off";
+ }
+ });
+ }
+ if(has("mozilla") && !value && this._mozSettingProps){
+ var ps = this._mozSettingProps;
+ var n;
+ for(n in ps){
+ if(ps.hasOwnProperty(n)){
+ try{
+ this.document.execCommand(n, false, ps[n]);
+ }catch(e2){
+ }
+ }
+ }
+ }
+ this._disabledOK = true;
+ },
+
+ /* Event handlers
+ *****************/
+
+ onLoad: function(/*String*/ html){
+ // summary:
+ // Handler after the iframe finishes loading.
+ // html: String
+ // Editor contents should be set to this value
+ // tags:
+ // protected
+
+ if(!this.window.__registeredWindow){
+ this.window.__registeredWindow = true;
+ this._iframeRegHandle = focus.registerIframe(this.iframe);
+ }
+
+ // there's a wrapper div around the content, see _getIframeDocTxt().
+ this.editNode = this.document.body.firstChild;
+ var _this = this;
+
+ // Helper code so IE and FF skip over focusing on the <iframe> and just focus on the inner <div>.
+ // See #4996 IE wants to focus the BODY tag.
+ this.beforeIframeNode = domConstruct.place("<div tabIndex=-1></div>", this.iframe, "before");
+ this.afterIframeNode = domConstruct.place("<div tabIndex=-1></div>", this.iframe, "after");
+ this.iframe.onfocus = this.document.onfocus = function(){
+ _this.editNode.focus();
+ };
+
+ this.focusNode = this.editNode; // for InlineEditBox
+
+
+ var events = this.events.concat(this.captureEvents);
+ var ap = this.iframe ? this.document : this.editNode;
+ this.own.apply(this,
+ array.map(events, function(item){
+ var type = item.toLowerCase().replace(/^on/, "");
+ return on(ap, type, lang.hitch(this, item));
+ }, this)
+ );
+
+ this.own(
+ // mouseup in the margin does not generate an onclick event
+ on(ap, "mouseup", lang.hitch(this, "onClick"))
+ );
+
+ if(has("ie")){ // IE contentEditable
+ this.own(on(this.document, "mousedown", lang.hitch(this, "_onIEMouseDown"))); // #4996 fix focus
+
+ // give the node Layout on IE
+ // TODO: this may no longer be needed, since we've reverted IE to using an iframe,
+ // not contentEditable. Removing it would also probably remove the need for creating
+ // the extra <div> in _getIframeDocTxt()
+ this.editNode.style.zoom = 1.0;
+ }
+
+ if(has("webkit")){
+ //WebKit sometimes doesn't fire right on selections, so the toolbar
+ //doesn't update right. Therefore, help it out a bit with an additional
+ //listener. A mouse up will typically indicate a display change, so fire this
+ //and get the toolbar to adapt. Reference: #9532
+ this._webkitListener = this.own(on(this.document, "mouseup", lang.hitch(this, "onDisplayChanged")))[0];
+ this.own(on(this.document, "mousedown", lang.hitch(this, function(e){
+ var t = e.target;
+ if(t && (t === this.document.body || t === this.document)){
+ // Since WebKit uses the inner DIV, we need to check and set position.
+ // See: #12024 as to why the change was made.
+ this.defer("placeCursorAtEnd");
+ }
+ })));
+ }
+
+ if(has("ie")){
+ // Try to make sure 'hidden' elements aren't visible in edit mode (like browsers other than IE
+ // do). See #9103
+ try{
+ this.document.execCommand('RespectVisibilityInDesign', true, null);
+ }catch(e){/* squelch */
+ }
+ }
+
+ this.isLoaded = true;
+
+ this.set('disabled', this.disabled); // initialize content to editable (or not)
+
+ // Note that setValue() call will only work after isLoaded is set to true (above)
+
+ // Set up a function to allow delaying the setValue until a callback is fired
+ // This ensures extensions like dijit.Editor have a way to hold the value set
+ // until plugins load (and do things like register filters).
+ var setContent = lang.hitch(this, function(){
+ this.setValue(html);
+
+ // Tell app that the Editor has finished loading. isFulfilled() check avoids spurious
+ // console warning when this function is called repeatedly because Editor DOMNode was moved.
+ if(this.onLoadDeferred && !this.onLoadDeferred.isFulfilled()){
+ this.onLoadDeferred.resolve(true);
+ }
+
+ this.onDisplayChanged();
+ if(this.focusOnLoad){
+ // after the document loads, then set focus after updateInterval expires so that
+ // onNormalizedDisplayChanged has run to avoid input caret issues
+ domReady(lang.hitch(this, "defer", "focus", this.updateInterval));
+ }
+ // Save off the initial content now
+ this.value = this.getValue(true);
+ });
+ if(this.setValueDeferred){
+ this.setValueDeferred.then(setContent);
+ }else{
+ setContent();
+ }
+ },
+
+ onKeyDown: function(/* Event */ e){
+ // summary:
+ // Handler for keydown event
+ // tags:
+ // protected
+
+ // Modifier keys should not cause the onKeyPressed event because they do not cause any change to the
+ // display
+ if(e.keyCode === keys.SHIFT ||
+ e.keyCode === keys.ALT ||
+ e.keyCode === keys.META ||
+ e.keyCode === keys.CTRL){
+ return true;
+ }
+
+ if(e.keyCode === keys.TAB && this.isTabIndent){
+ //prevent tab from moving focus out of editor
+ e.stopPropagation();
+ e.preventDefault();
+
+ // FIXME: this is a poor-man's indent/outdent. It would be
+ // better if it added 4 " " chars in an undoable way.
+ // Unfortunately pasteHTML does not prove to be undoable
+ if(this.queryCommandEnabled((e.shiftKey ? "outdent" : "indent"))){
+ this.execCommand((e.shiftKey ? "outdent" : "indent"));
+ }
+ }
+
+ // Make tab and shift-tab skip over the <iframe>, going from the nested <div> to the toolbar
+ // or next element after the editor
+ if(e.keyCode == keys.TAB && !this.isTabIndent && !e.ctrlKey && !e.altKey){
+ if(e.shiftKey){
+ // focus the <iframe> so the browser will shift-tab away from it instead
+ this.beforeIframeNode.focus();
+ }else{
+ // focus node after the <iframe> so the browser will tab away from it instead
+ this.afterIframeNode.focus();
+ }
+
+ // Prevent onKeyPressed from firing in order to avoid triggering a display change event when the
+ // editor is tabbed away; this fixes toolbar controls being inappropriately disabled in IE9+
+ return true;
+ }
+
+ if(has("ie") < 9 && e.keyCode === keys.BACKSPACE && this.document.selection.type === "Control"){
+ // IE has a bug where if a non-text object is selected in the editor,
+ // hitting backspace would act as if the browser's back button was
+ // clicked instead of deleting the object. see #1069
+ e.stopPropagation();
+ e.preventDefault();
+ this.execCommand("delete");
+ }
+
+ if(has("ff")){
+ if(e.keyCode === keys.PAGE_UP || e.keyCode === keys.PAGE_DOWN){
+ if(this.editNode.clientHeight >= this.editNode.scrollHeight){
+ // Stop the event to prevent firefox from trapping the cursor when there is no scroll bar.
+ e.preventDefault();
+ }
+ }
+ }
+
+ var handlers = this._keyHandlers[e.keyCode],
+ args = arguments;
+
+ if(handlers && !e.altKey){
+ array.some(handlers, function(h){
+ // treat meta- same as ctrl-, for benefit of mac users
+ if(!(h.shift ^ e.shiftKey) && !(h.ctrl ^ (e.ctrlKey || e.metaKey))){
+ if(!h.handler.apply(this, args)){
+ e.preventDefault();
+ }
+ return true;
+ }
+ }, this);
+ }
+
+ // function call after the character has been inserted
+ this.defer("onKeyPressed", 1);
+
+ return true;
+ },
+
+ onKeyUp: function(/*===== e =====*/){
+ // summary:
+ // Handler for onkeyup event
+ // tags:
+ // callback
+ },
+
+ setDisabled: function(/*Boolean*/ disabled){
+ // summary:
+ // Deprecated, use set('disabled', ...) instead.
+ // tags:
+ // deprecated
+ kernel.deprecated('dijit.Editor::setDisabled is deprecated', 'use dijit.Editor::attr("disabled",boolean) instead', 2.0);
+ this.set('disabled', disabled);
+ },
+ _setValueAttr: function(/*String*/ value){
+ // summary:
+ // Registers that attr("value", foo) should call setValue(foo)
+ this.setValue(value);
+ },
+ _setDisableSpellCheckAttr: function(/*Boolean*/ disabled){
+ if(this.document){
+ domAttr.set(this.document.body, "spellcheck", !disabled);
+ }else{
+ // try again after the editor is finished loading
+ this.onLoadDeferred.then(lang.hitch(this, function(){
+ domAttr.set(this.document.body, "spellcheck", !disabled);
+ }));
+ }
+ this._set("disableSpellCheck", disabled);
+ },
+
+ addKeyHandler: function(/*String|Number*/ key, /*Boolean*/ ctrl, /*Boolean*/ shift, /*Function*/ handler){
+ // summary:
+ // Add a handler for a keyboard shortcut
+ // tags:
+ // protected
+
+ if(typeof key == "string"){
+ // Something like Ctrl-B. Since using keydown event, we need to convert string to a number.
+ key = key.toUpperCase().charCodeAt(0);
+ }
+
+ if(!lang.isArray(this._keyHandlers[key])){
+ this._keyHandlers[key] = [];
+ }
+
+ this._keyHandlers[key].push({
+ shift: shift || false,
+ ctrl: ctrl || false,
+ handler: handler
+ });
+ },
+
+ onKeyPressed: function(){
+ // summary:
+ // Handler for after the user has pressed a key, and the display has been updated.
+ // (Runs on a timer so that it runs after the display is updated)
+ // tags:
+ // private
+ this.onDisplayChanged(/*e*/); // can't pass in e
+ },
+
+ onClick: function(/*Event*/ e){
+ // summary:
+ // Handler for when the user clicks.
+ // tags:
+ // private
+
+ // console.info('onClick',this._tryDesignModeOn);
+ this.onDisplayChanged(e);
+ },
+
+ _onIEMouseDown: function(){
+ // summary:
+ // IE only to prevent 2 clicks to focus
+ // tags:
+ // protected
+
+ if(!this.focused && !this.disabled){
+ this.focus();
+ }
+ },
+
+ _onBlur: function(e){
+ // summary:
+ // Called from focus manager when focus has moved away from this editor
+ // tags:
+ // protected
+
+ // Workaround IE problem when you blur the browser windows while an editor is focused: IE hangs
+ // when you focus editor #1, blur the browser window, and then click editor #0. See #16939.
+ if(has("ie") || has("trident")){
+ this.defer(function(){
+ if(!focus.curNode){
+ this.ownerDocumentBody.focus();
+ }
+ });
+ }
+
+ this.inherited(arguments);
+
+ var newValue = this.getValue(true);
+ if(newValue !== this.value){
+ this.onChange(newValue);
+ }
+ this._set("value", newValue);
+ },
+
+ _onFocus: function(/*Event*/ e){
+ // summary:
+ // Called from focus manager when focus has moved into this editor
+ // tags:
+ // protected
+
+ // console.info('_onFocus')
+ if(!this.disabled){
+ if(!this._disabledOK){
+ this.set('disabled', false);
+ }
+ this.inherited(arguments);
+ }
+ },
+
+ // TODO: remove in 2.0
+ blur: function(){
+ // summary:
+ // Remove focus from this instance.
+ // tags:
+ // deprecated
+ if(!has("ie") && this.window.document.documentElement && this.window.document.documentElement.focus){
+ this.window.document.documentElement.focus();
+ }else if(this.ownerDocumentBody.focus){
+ this.ownerDocumentBody.focus();
+ }
+ },
+
+ focus: function(){
+ // summary:
+ // Move focus to this editor
+ if(!this.isLoaded){
+ this.focusOnLoad = true;
+ return;
+ }
+ if(has("ie") < 9){
+ //this.editNode.focus(); -> causes IE to scroll always (strict and quirks mode) to the top the Iframe
+ // if we fire the event manually and let the browser handle the focusing, the latest
+ // cursor position is focused like in FF
+ this.iframe.fireEvent('onfocus', document.createEventObject()); // createEventObject/fireEvent only in IE < 11
+ }else{
+ // Firefox and chrome
+ this.editNode.focus();
+ }
+ },
+
+ // _lastUpdate: 0,
+ updateInterval: 200,
+ _updateTimer: null,
+ onDisplayChanged: function(/*Event*/ /*===== e =====*/){
+ // summary:
+ // This event will be fired every time the display context
+ // changes and the result needs to be reflected in the UI.
+ // description:
+ // If you don't want to have update too often,
+ // onNormalizedDisplayChanged should be used instead
+ // tags:
+ // private
+
+ // var _t=new Date();
+ if(this._updateTimer){
+ this._updateTimer.remove();
+ }
+ this._updateTimer = this.defer("onNormalizedDisplayChanged", this.updateInterval);
+
+ // Technically this should trigger a call to watch("value", ...) registered handlers,
+ // but getValue() is too slow to call on every keystroke so we don't.
+ },
+ onNormalizedDisplayChanged: function(){
+ // summary:
+ // This event is fired every updateInterval ms or more
+ // description:
+ // If something needs to happen immediately after a
+ // user change, please use onDisplayChanged instead.
+ // tags:
+ // private
+ delete this._updateTimer;
+ },
+ onChange: function(/*===== newContent =====*/){
+ // summary:
+ // This is fired if and only if the editor loses focus and
+ // the content is changed.
+ },
+ _normalizeCommand: function(/*String*/ cmd, /*Anything?*/argument){
+ // summary:
+ // Used as the advice function to map our
+ // normalized set of commands to those supported by the target
+ // browser.
+ // tags:
+ // private
+
+ var command = cmd.toLowerCase();
+ if(command === "formatblock"){
+ if(has("safari") && argument === undefined){
+ command = "heading";
+ }
+ }else if(command === "hilitecolor" && !has("mozilla")){
+ command = "backcolor";
+ }
+
+ return command;
+ },
+
+ _qcaCache: {},
+ queryCommandAvailable: function(/*String*/ command){
+ // summary:
+ // Tests whether a command is supported by the host. Clients
+ // SHOULD check whether a command is supported before attempting
+ // to use it, behaviour for unsupported commands is undefined.
+ // command:
+ // The command to test for
+ // tags:
+ // private
+
+ // memoizing version. See _queryCommandAvailable for computing version
+ var ca = this._qcaCache[command];
+ if(ca !== undefined){
+ return ca;
+ }
+ return (this._qcaCache[command] = this._queryCommandAvailable(command));
+ },
+
+ _queryCommandAvailable: function(/*String*/ command){
+ // summary:
+ // See queryCommandAvailable().
+ // tags:
+ // private
+
+ var ie = 1;
+ var mozilla = 1 << 1;
+ var webkit = 1 << 2;
+ var opera = 1 << 3;
+
+ function isSupportedBy(browsers){
+ return {
+ ie: Boolean(browsers & ie),
+ mozilla: Boolean(browsers & mozilla),
+ webkit: Boolean(browsers & webkit),
+ opera: Boolean(browsers & opera)
+ };
+ }
+
+ var supportedBy = null;
+
+ switch(command.toLowerCase()){
+ case "bold":
+ case "italic":
+ case "underline":
+ case "subscript":
+ case "superscript":
+ case "fontname":
+ case "fontsize":
+ case "forecolor":
+ case "hilitecolor":
+ case "justifycenter":
+ case "justifyfull":
+ case "justifyleft":
+ case "justifyright":
+ case "delete":
+ case "selectall":
+ case "toggledir":
+ supportedBy = isSupportedBy(mozilla | ie | webkit | opera);
+ break;
+
+ case "createlink":
+ case "unlink":
+ case "removeformat":
+ case "inserthorizontalrule":
+ case "insertimage":
+ case "insertorderedlist":
+ case "insertunorderedlist":
+ case "indent":
+ case "outdent":
+ case "formatblock":
+ case "inserthtml":
+ case "undo":
+ case "redo":
+ case "strikethrough":
+ case "tabindent":
+ supportedBy = isSupportedBy(mozilla | ie | opera | webkit);
+ break;
+
+ case "blockdirltr":
+ case "blockdirrtl":
+ case "dirltr":
+ case "dirrtl":
+ case "inlinedirltr":
+ case "inlinedirrtl":
+ supportedBy = isSupportedBy(ie);
+ break;
+ case "cut":
+ case "copy":
+ case "paste":
+ supportedBy = isSupportedBy(ie | mozilla | webkit | opera);
+ break;
+
+ case "inserttable":
+ supportedBy = isSupportedBy(mozilla | ie);
+ break;
+
+ case "insertcell":
+ case "insertcol":
+ case "insertrow":
+ case "deletecells":
+ case "deletecols":
+ case "deleterows":
+ case "mergecells":
+ case "splitcell":
+ supportedBy = isSupportedBy(ie | mozilla);
+ break;
+
+ default:
+ return false;
+ }
+
+ return ((has("ie") || has("trident")) && supportedBy.ie) ||
+ (has("mozilla") && supportedBy.mozilla) ||
+ (has("webkit") && supportedBy.webkit) ||
+ (has("opera") && supportedBy.opera); // Boolean return true if the command is supported, false otherwise
+ },
+
+ execCommand: function(/*String*/ command, argument){
+ // summary:
+ // Executes a command in the Rich Text area
+ // command:
+ // The command to execute
+ // argument:
+ // An optional argument to the command
+ // tags:
+ // protected
+ var returnValue;
+
+ //focus() is required for IE to work
+ //In addition, focus() makes sure after the execution of
+ //the command, the editor receives the focus as expected
+ if(this.focused){
+ // put focus back in the iframe, unless focus has somehow been shifted out of the editor completely
+ this.focus();
+ }
+
+ command = this._normalizeCommand(command, argument);
+
+ if(argument !== undefined){
+ if(command === "heading"){
+ throw new Error("unimplemented");
+ }else if(command === "formatblock" && (has("ie") || has("trident"))){
+ argument = '<' + argument + '>';
+ }
+ }
+
+ //Check to see if we have any over-rides for commands, they will be functions on this
+ //widget of the form _commandImpl. If we don't, fall through to the basic native
+ //exec command of the browser.
+ var implFunc = "_" + command + "Impl";
+ if(this[implFunc]){
+ returnValue = this[implFunc](argument);
+ }else{
+ argument = arguments.length > 1 ? argument : null;
+ if(argument || command !== "createlink"){
+ returnValue = this.document.execCommand(command, false, argument);
+ }
+ }
+
+ this.onDisplayChanged();
+ return returnValue;
+ },
+
+ queryCommandEnabled: function(/*String*/ command){
+ // summary:
+ // Check whether a command is enabled or not.
+ // command:
+ // The command to execute
+ // tags:
+ // protected
+ if(this.disabled || !this._disabledOK){
+ return false;
+ }
+
+ command = this._normalizeCommand(command);
+
+ //Check to see if we have any over-rides for commands, they will be functions on this
+ //widget of the form _commandEnabledImpl. If we don't, fall through to the basic native
+ //command of the browser.
+ var implFunc = "_" + command + "EnabledImpl";
+
+ if(this[implFunc]){
+ return this[implFunc](command);
+ }else{
+ return this._browserQueryCommandEnabled(command);
+ }
+ },
+
+ queryCommandState: function(command){
+ // summary:
+ // Check the state of a given command and returns true or false.
+ // tags:
+ // protected
+
+ if(this.disabled || !this._disabledOK){
+ return false;
+ }
+ command = this._normalizeCommand(command);
+ try{
+ return this.document.queryCommandState(command);
+ }catch(e){
+ //Squelch, occurs if editor is hidden on FF 3 (and maybe others.)
+ return false;
+ }
+ },
+
+ queryCommandValue: function(command){
+ // summary:
+ // Check the value of a given command. This matters most for
+ // custom selections and complex values like font value setting.
+ // tags:
+ // protected
+
+ if(this.disabled || !this._disabledOK){
+ return false;
+ }
+ var r;
+ command = this._normalizeCommand(command);
+ if((has("ie") || has("trident")) && command === "formatblock"){
+ r = this._native2LocalFormatNames[this.document.queryCommandValue(command)];
+ }else if(has("mozilla") && command === "hilitecolor"){
+ var oldValue;
+ try{
+ oldValue = this.document.queryCommandValue("styleWithCSS");
+ }catch(e){
+ oldValue = false;
+ }
+ this.document.execCommand("styleWithCSS", false, true);
+ r = this.document.queryCommandValue(command);
+ this.document.execCommand("styleWithCSS", false, oldValue);
+ }else{
+ r = this.document.queryCommandValue(command);
+ }
+ return r;
+ },
+
+ // Misc.
+
+ _sCall: function(name, args){
+ // summary:
+ // Deprecated, remove for 2.0. New code should access this.selection directly.
+ // Run the named method of dijit/selection over the
+ // current editor instance's window, with the passed args.
+ // tags:
+ // private deprecated
+
+ return this.selection[name].apply(this.selection, args);
+ },
+
+ // FIXME: this is a TON of code duplication. Why?
+
+ placeCursorAtStart: function(){
+ // summary:
+ // Place the cursor at the start of the editing area.
+ // tags:
+ // private
+
+ this.focus();
+
+ //see comments in placeCursorAtEnd
+ var isvalid = false;
+ if(has("mozilla")){
+ // TODO: Is this branch even necessary?
+ var first = this.editNode.firstChild;
+ while(first){
+ if(first.nodeType === 3){
+ if(first.nodeValue.replace(/^\s+|\s+$/g, "").length > 0){
+ isvalid = true;
+ this.selection.selectElement(first);
+ break;
+ }
+ }else if(first.nodeType === 1){
+ isvalid = true;
+ var tg = first.tagName ? first.tagName.toLowerCase() : "";
+ // Collapse before childless tags.
+ if(/br|input|img|base|meta|area|basefont|hr|link/.test(tg)){
+ this.selection.selectElement(first);
+ }else{
+ // Collapse inside tags with children.
+ this.selection.selectElementChildren(first);
+ }
+ break;
+ }
+ first = first.nextSibling;
+ }
+ }else{
+ isvalid = true;
+ this.selection.selectElementChildren(this.editNode);
+ }
+ if(isvalid){
+ this.selection.collapse(true);
+ }
+ },
+
+ placeCursorAtEnd: function(){
+ // summary:
+ // Place the cursor at the end of the editing area.
+ // tags:
+ // private
+
+ this.focus();
+
+ //In mozilla, if last child is not a text node, we have to use
+ // selectElementChildren on this.editNode.lastChild otherwise the
+ // cursor would be placed at the end of the closing tag of
+ //this.editNode.lastChild
+ var isvalid = false;
+ if(has("mozilla")){
+ var last = this.editNode.lastChild;
+ while(last){
+ if(last.nodeType === 3){
+ if(last.nodeValue.replace(/^\s+|\s+$/g, "").length > 0){
+ isvalid = true;
+ this.selection.selectElement(last);
+ break;
+ }
+ }else if(last.nodeType === 1){
+ isvalid = true;
+ this.selection.selectElement(last.lastChild || last);
+ break;
+ }
+ last = last.previousSibling;
+ }
+ }else{
+ isvalid = true;
+ this.selection.selectElementChildren(this.editNode);
+ }
+ if(isvalid){
+ this.selection.collapse(false);
+ }
+ },
+
+ getValue: function(/*Boolean?*/ nonDestructive){
+ // summary:
+ // Return the current content of the editing area (post filters
+ // are applied). Users should call get('value') instead.
+ // nonDestructive:
+ // defaults to false. Should the post-filtering be run over a copy
+ // of the live DOM? Most users should pass "true" here unless they
+ // *really* know that none of the installed filters are going to
+ // mess up the editing session.
+ // tags:
+ // private
+ if(this.textarea){
+ if(this.isClosed || !this.isLoaded){
+ return this.textarea.value;
+ }
+ }
+
+ return this.isLoaded ? this._postFilterContent(null, nonDestructive) : this.value;
+ },
+ _getValueAttr: function(){
+ // summary:
+ // Hook to make attr("value") work
+ return this.getValue(true);
+ },
+
+ setValue: function(/*String*/ html){
+ // summary:
+ // This function sets the content. No undo history is preserved.
+ // Users should use set('value', ...) instead.
+ // tags:
+ // deprecated
+
+ // TODO: remove this and getValue() for 2.0, and move code to _setValueAttr()
+
+ if(!this.isLoaded){
+ // try again after the editor is finished loading
+ this.onLoadDeferred.then(lang.hitch(this, function(){
+ this.setValue(html);
+ }));
+ return;
+ }
+ if(this.textarea && (this.isClosed || !this.isLoaded)){
+ this.textarea.value = html;
+ }else{
+ html = this._preFilterContent(html);
+ var node = this.isClosed ? this.domNode : this.editNode;
+
+ node.innerHTML = html;
+ this._preDomFilterContent(node);
+ }
+
+ this.onDisplayChanged();
+ this._set("value", this.getValue(true));
+ },
+
+ replaceValue: function(/*String*/ html){
+ // summary:
+ // This function set the content while trying to maintain the undo stack
+ // (now only works fine with Moz, this is identical to setValue in all
+ // other browsers)
+ // tags:
+ // protected
+
+ if(this.isClosed){
+ this.setValue(html);
+ }else if(this.window && this.window.getSelection && !has("mozilla")){ // Safari
+ // look ma! it's a totally f'd browser!
+ this.setValue(html);
+ }else if(this.window && this.window.getSelection){ // Moz
+ html = this._preFilterContent(html);
+ this.execCommand("selectall");
+ this.execCommand("inserthtml", html);
+ this._preDomFilterContent(this.editNode);
+ }else if(this.document && this.document.selection){//IE
+ //In IE, when the first element is not a text node, say
+ //an <a> tag, when replacing the content of the editing
+ //area, the <a> tag will be around all the content
+ //so for now, use setValue for IE too
+ this.setValue(html);
+ }
+
+ this._set("value", this.getValue(true));
+ },
+
+ _preFilterContent: function(/*String*/ html){
+ // summary:
+ // Filter the input before setting the content of the editing
+ // area. DOM pre-filtering may happen after this
+ // string-based filtering takes place but as of 1.2, this is not
+ // guaranteed for operations such as the inserthtml command.
+ // tags:
+ // private
+
+ var ec = html;
+ array.forEach(this.contentPreFilters, function(ef){
+ if(ef){
+ ec = ef(ec);
+ }
+ });
+ return ec;
+ },
+ _preDomFilterContent: function(/*DomNode*/ dom){
+ // summary:
+ // filter the input's live DOM. All filter operations should be
+ // considered to be "live" and operating on the DOM that the user
+ // will be interacting with in their editing session.
+ // tags:
+ // private
+ dom = dom || this.editNode;
+ array.forEach(this.contentDomPreFilters, function(ef){
+ if(ef && lang.isFunction(ef)){
+ ef(dom);
+ }
+ }, this);
+ },
+
+ _postFilterContent: function(/*DomNode|DomNode[]|String?*/ dom, /*Boolean?*/ nonDestructive){
+ // summary:
+ // filter the output after getting the content of the editing area
+ //
+ // description:
+ // post-filtering allows plug-ins and users to specify any number
+ // of transforms over the editor's content, enabling many common
+ // use-cases such as transforming absolute to relative URLs (and
+ // vice-versa), ensuring conformance with a particular DTD, etc.
+ // The filters are registered in the contentDomPostFilters and
+ // contentPostFilters arrays. Each item in the
+ // contentDomPostFilters array is a function which takes a DOM
+ // Node or array of nodes as its only argument and returns the
+ // same. It is then passed down the chain for further filtering.
+ // The contentPostFilters array behaves the same way, except each
+ // member operates on strings. Together, the DOM and string-based
+ // filtering allow the full range of post-processing that should
+ // be necessaray to enable even the most agressive of post-editing
+ // conversions to take place.
+ //
+ // If nonDestructive is set to "true", the nodes are cloned before
+ // filtering proceeds to avoid potentially destructive transforms
+ // to the content which may still needed to be edited further.
+ // Once DOM filtering has taken place, the serialized version of
+ // the DOM which is passed is run through each of the
+ // contentPostFilters functions.
+ //
+ // dom:
+ // a node, set of nodes, which to filter using each of the current
+ // members of the contentDomPostFilters and contentPostFilters arrays.
+ //
+ // nonDestructive:
+ // defaults to "false". If true, ensures that filtering happens on
+ // a clone of the passed-in content and not the actual node
+ // itself.
+ //
+ // tags:
+ // private
+
+ var ec;
+ if(!lang.isString(dom)){
+ dom = dom || this.editNode;
+ if(this.contentDomPostFilters.length){
+ if(nonDestructive){
+ dom = lang.clone(dom);
+ }
+ array.forEach(this.contentDomPostFilters, function(ef){
+ dom = ef(dom);
+ });
+ }
+ ec = htmlapi.getChildrenHtml(dom);
+ }else{
+ ec = dom;
+ }
+
+ if(!lang.trim(ec.replace(/^\xA0\xA0*/, '').replace(/\xA0\xA0*$/, '')).length){
+ ec = "";
+ }
+
+ array.forEach(this.contentPostFilters, function(ef){
+ ec = ef(ec);
+ });
+
+ return ec;
+ },
+
+ _saveContent: function(){
+ // summary:
+ // Saves the content in an onunload event if the editor has not been closed
+ // tags:
+ // private
+
+ var saveTextarea = dom.byId(dijit._scopeName + "._editor.RichText.value");
+ if(saveTextarea){
+ if(saveTextarea.value){
+ saveTextarea.value += this._SEPARATOR;
+ }
+ saveTextarea.value += this.name + this._NAME_CONTENT_SEP + this.getValue(true);
+ }
+ },
+
+
+ escapeXml: function(/*String*/ str, /*Boolean*/ noSingleQuotes){
+ // summary:
+ // Adds escape sequences for special characters in XML.
+ // Optionally skips escapes for single quotes
+ // tags:
+ // private
+
+ str = str.replace(/&/gm, "&").replace(/</gm, "<").replace(/>/gm, ">").replace(/"/gm, """);
+ if(!noSingleQuotes){
+ str = str.replace(/'/gm, "'");
+ }
+ return str; // string
+ },
+
+ getNodeHtml: function(/* DomNode */ node){
+ // summary:
+ // Deprecated. Use dijit/_editor/html::_getNodeHtml() instead.
+ // tags:
+ // deprecated
+ kernel.deprecated('dijit.Editor::getNodeHtml is deprecated', 'use dijit/_editor/html::getNodeHtml instead', 2);
+ return htmlapi.getNodeHtml(node); // String
+ },
+
+ getNodeChildrenHtml: function(/* DomNode */ dom){
+ // summary:
+ // Deprecated. Use dijit/_editor/html::getChildrenHtml() instead.
+ // tags:
+ // deprecated
+ kernel.deprecated('dijit.Editor::getNodeChildrenHtml is deprecated', 'use dijit/_editor/html::getChildrenHtml instead', 2);
+ return htmlapi.getChildrenHtml(dom);
+ },
+
+ close: function(/*Boolean?*/ save){
+ // summary:
+ // Kills the editor and optionally writes back the modified contents to the
+ // element from which it originated.
+ // save:
+ // Whether or not to save the changes. If false, the changes are discarded.
+ // tags:
+ // private
+
+ if(this.isClosed){
+ return;
+ }
+
+ if(!arguments.length){
+ save = true;
+ }
+ if(save){
+ this._set("value", this.getValue(true));
+ }
+
+ // line height is squashed for iframes
+ // FIXME: why was this here? if(this.iframe){ this.domNode.style.lineHeight = null; }
+
+ if(this.interval){
+ clearInterval(this.interval);
+ }
+
+ if(this._webkitListener){
+ // Cleanup of WebKit fix: #9532
+ this._webkitListener.remove();
+ delete this._webkitListener;
+ }
+
+ // Guard against memory leaks on IE (see #9268)
+ if(has("ie")){
+ this.iframe.onfocus = null;
+ }
+ this.iframe._loadFunc = null;
+
+ if(this._iframeRegHandle){
+ this._iframeRegHandle.remove();
+ delete this._iframeRegHandle;
+ }
+
+ if(this.textarea){
+ var s = this.textarea.style;
+ s.position = "";
+ s.left = s.top = "";
+ if(has("ie")){
+ s.overflow = this.__overflow;
+ this.__overflow = null;
+ }
+ this.textarea.value = this.value;
+ domConstruct.destroy(this.domNode);
+ this.domNode = this.textarea;
+ }else{
+ // Note that this destroys the iframe
+ this.domNode.innerHTML = this.value;
+ }
+ delete this.iframe;
+
+ domClass.remove(this.domNode, this.baseClass);
+ this.isClosed = true;
+ this.isLoaded = false;
+
+ delete this.editNode;
+ delete this.focusNode;
+
+ if(this.window && this.window._frameElement){
+ this.window._frameElement = null;
+ }
+
+ this.window = null;
+ this.document = null;
+ this.editingArea = null;
+ this.editorObject = null;
+ },
+
+ destroy: function(){
+ if(!this.isClosed){
+ this.close(false);
+ }
+ if(this._updateTimer){
+ this._updateTimer.remove();
+ }
+ this.inherited(arguments);
+ if(RichText._globalSaveHandler){
+ delete RichText._globalSaveHandler[this.id];
+ }
+ },
+
+ _removeMozBogus: function(/* String */ html){
+ // summary:
+ // Post filter to remove unwanted HTML attributes generated by mozilla
+ // tags:
+ // private
+ return html.replace(/\stype="_moz"/gi, '').replace(/\s_moz_dirty=""/gi, '').replace(/_moz_resizing="(true|false)"/gi, ''); // String
+ },
+ _removeWebkitBogus: function(/* String */ html){
+ // summary:
+ // Post filter to remove unwanted HTML attributes generated by webkit
+ // tags:
+ // private
+ html = html.replace(/\sclass="webkit-block-placeholder"/gi, '');
+ html = html.replace(/\sclass="apple-style-span"/gi, '');
+ // For some reason copy/paste sometime adds extra meta tags for charset on
+ // webkit (chrome) on mac.They need to be removed. See: #12007"
+ html = html.replace(/<meta charset=\"utf-8\" \/>/gi, '');
+ return html; // String
+ },
+ _normalizeFontStyle: function(/* String */ html){
+ // summary:
+ // Convert 'strong' and 'em' to 'b' and 'i'.
+ // description:
+ // Moz can not handle strong/em tags correctly, so to help
+ // mozilla and also to normalize output, convert them to 'b' and 'i'.
+ //
+ // Note the IE generates 'strong' and 'em' rather than 'b' and 'i'
+ // tags:
+ // private
+ return html.replace(/<(\/)?strong([ \>])/gi, '<$1b$2')
+ .replace(/<(\/)?em([ \>])/gi, '<$1i$2'); // String
+ },
+
+ _preFixUrlAttributes: function(/* String */ html){
+ // summary:
+ // Pre-filter to do fixing to href attributes on `<a>` and `<img>` tags
+ // tags:
+ // private
+ return html.replace(/(?:(<a(?=\s).*?\shref=)("|')(.*?)\2)|(?:(<a\s.*?href=)([^"'][^ >]+))/gi,
+ '$1$4$2$3$5$2 _djrealurl=$2$3$5$2')
+ .replace(/(?:(<img(?=\s).*?\ssrc=)("|')(.*?)\2)|(?:(<img\s.*?src=)([^"'][^ >]+))/gi,
+ '$1$4$2$3$5$2 _djrealurl=$2$3$5$2'); // String
+ },
+
+ /*****************************************************************************
+ The following functions implement HTML manipulation commands for various
+ browser/contentEditable implementations. The goal of them is to enforce
+ standard behaviors of them.
+ ******************************************************************************/
+
+ /*** queryCommandEnabled implementations ***/
+
+ _browserQueryCommandEnabled: function(command){
+ // summary:
+ // Implementation to call to the native queryCommandEnabled of the browser.
+ // command:
+ // The command to check.
+ // tags:
+ // protected
+ if(!command){
+ return false;
+ }
+ var elem = has("ie") < 9 ? this.document.selection.createRange() : this.document;
+ try{
+ return elem.queryCommandEnabled(command);
+ }catch(e){
+ return false;
+ }
+ },
+
+ _createlinkEnabledImpl: function(/*===== argument =====*/){
+ // summary:
+ // This function implements the test for if the create link
+ // command should be enabled or not.
+ // argument:
+ // arguments to the exec command, if any.
+ // tags:
+ // protected
+ var enabled = true;
+ if(has("opera")){
+ var sel = this.window.getSelection();
+ if(sel.isCollapsed){
+ enabled = true;
+ }else{
+ enabled = this.document.queryCommandEnabled("createlink");
+ }
+ }else{
+ enabled = this._browserQueryCommandEnabled("createlink");
+ }
+ return enabled;
+ },
+
+ _unlinkEnabledImpl: function(/*===== argument =====*/){
+ // summary:
+ // This function implements the test for if the unlink
+ // command should be enabled or not.
+ // argument:
+ // arguments to the exec command, if any.
+ // tags:
+ // protected
+ var enabled = true;
+ if(has("mozilla") || has("webkit")){
+ enabled = this.selection.hasAncestorElement("a");
+ }else{
+ enabled = this._browserQueryCommandEnabled("unlink");
+ }
+ return enabled;
+ },
+
+ _inserttableEnabledImpl: function(/*===== argument =====*/){
+ // summary:
+ // This function implements the test for if the inserttable
+ // command should be enabled or not.
+ // argument:
+ // arguments to the exec command, if any.
+ // tags:
+ // protected
+ var enabled = true;
+ if(has("mozilla") || has("webkit")){
+ enabled = true;
+ }else{
+ enabled = this._browserQueryCommandEnabled("inserttable");
+ }
+ return enabled;
+ },
+
+ _cutEnabledImpl: function(/*===== argument =====*/){
+ // summary:
+ // This function implements the test for if the cut
+ // command should be enabled or not.
+ // argument:
+ // arguments to the exec command, if any.
+ // tags:
+ // protected
+ var enabled = true;
+ if(has("webkit")){
+ // WebKit deems clipboard activity as a security threat and natively would return false
+ var sel = this.window.getSelection();
+ if(sel){
+ sel = sel.toString();
+ }
+ enabled = !!sel;
+ }else{
+ enabled = this._browserQueryCommandEnabled("cut");
+ }
+ return enabled;
+ },
+
+ _copyEnabledImpl: function(/*===== argument =====*/){
+ // summary:
+ // This function implements the test for if the copy
+ // command should be enabled or not.
+ // argument:
+ // arguments to the exec command, if any.
+ // tags:
+ // protected
+ var enabled = true;
+ if(has("webkit")){
+ // WebKit deems clipboard activity as a security threat and natively would return false
+ var sel = this.window.getSelection();
+ if(sel){
+ sel = sel.toString();
+ }
+ enabled = !!sel;
+ }else{
+ enabled = this._browserQueryCommandEnabled("copy");
+ }
+ return enabled;
+ },
+
+ _pasteEnabledImpl: function(/*===== argument =====*/){
+ // summary:c
+ // This function implements the test for if the paste
+ // command should be enabled or not.
+ // argument:
+ // arguments to the exec command, if any.
+ // tags:
+ // protected
+ var enabled = true;
+ if(has("webkit")){
+ return true;
+ }else{
+ enabled = this._browserQueryCommandEnabled("paste");
+ }
+ return enabled;
+ },
+
+ /*** execCommand implementations ***/
+
+ _inserthorizontalruleImpl: function(argument){
+ // summary:
+ // This function implements the insertion of HTML 'HR' tags.
+ // into a point on the page. IE doesn't to it right, so
+ // we have to use an alternate form
+ // argument:
+ // arguments to the exec command, if any.
+ // tags:
+ // protected
+ if(has("ie")){
+ return this._inserthtmlImpl("<hr>");
+ }
+ return this.document.execCommand("inserthorizontalrule", false, argument);
+ },
+
+ _unlinkImpl: function(argument){
+ // summary:
+ // This function implements the unlink of an 'a' tag.
+ // argument:
+ // arguments to the exec command, if any.
+ // tags:
+ // protected
+ if((this.queryCommandEnabled("unlink")) && (has("mozilla") || has("webkit"))){
+ var a = this.selection.getAncestorElement("a");
+ this.selection.selectElement(a);
+ return this.document.execCommand("unlink", false, null);
+ }
+ return this.document.execCommand("unlink", false, argument);
+ },
+
+ _hilitecolorImpl: function(argument){
+ // summary:
+ // This function implements the hilitecolor command
+ // argument:
+ // arguments to the exec command, if any.
+ // tags:
+ // protected
+ var returnValue;
+ var isApplied = this._handleTextColorOrProperties("hilitecolor", argument);
+ if(!isApplied){
+ if(has("mozilla")){
+ // mozilla doesn't support hilitecolor properly when useCSS is
+ // set to false (bugzilla #279330)
+ this.document.execCommand("styleWithCSS", false, true);
+ console.log("Executing color command.");
+ returnValue = this.document.execCommand("hilitecolor", false, argument);
+ this.document.execCommand("styleWithCSS", false, false);
+ }else{
+ returnValue = this.document.execCommand("hilitecolor", false, argument);
+ }
+ }
+ return returnValue;
+ },
+
+ _backcolorImpl: function(argument){
+ // summary:
+ // This function implements the backcolor command
+ // argument:
+ // arguments to the exec command, if any.
+ // tags:
+ // protected
+ if(has("ie")){
+ // Tested under IE 6 XP2, no problem here, comment out
+ // IE weirdly collapses ranges when we exec these commands, so prevent it
+ // var tr = this.document.selection.createRange();
+ argument = argument ? argument : null;
+ }
+ var isApplied = this._handleTextColorOrProperties("backcolor", argument);
+ if(!isApplied){
+ isApplied = this.document.execCommand("backcolor", false, argument);
+ }
+ return isApplied;
+ },
+
+ _forecolorImpl: function(argument){
+ // summary:
+ // This function implements the forecolor command
+ // argument:
+ // arguments to the exec command, if any.
+ // tags:
+ // protected
+ if(has("ie")){
+ // Tested under IE 6 XP2, no problem here, comment out
+ // IE weirdly collapses ranges when we exec these commands, so prevent it
+ // var tr = this.document.selection.createRange();
+ argument = argument ? argument : null;
+ }
+ var isApplied = false;
+ isApplied = this._handleTextColorOrProperties("forecolor", argument);
+ if(!isApplied){
+ isApplied = this.document.execCommand("forecolor", false, argument);
+ }
+ return isApplied;
+ },
+
+ _inserthtmlImpl: function(argument){
+ // summary:
+ // This function implements the insertion of HTML content into
+ // a point on the page.
+ // argument:
+ // The content to insert, if any.
+ // tags:
+ // protected
+ argument = this._preFilterContent(argument);
+ var rv = true;
+ if(has("ie") < 9){
+ var insertRange = this.document.selection.createRange();
+ if(this.document.selection.type.toUpperCase() === 'CONTROL'){
+ var n = insertRange.item(0);
+ while(insertRange.length){
+ insertRange.remove(insertRange.item(0));
+ }
+ n.outerHTML = argument;
+ }else{
+ insertRange.pasteHTML(argument);
+ }
+ insertRange.select();
+ }else if(has("trident") < 8){
+ var insertRange;
+ var selection = rangeapi.getSelection(this.window);
+ if(selection && selection.rangeCount && selection.getRangeAt){
+ insertRange = selection.getRangeAt(0);
+ insertRange.deleteContents();
+
+ var div = domConstruct.create('div');
+ div.innerHTML = argument;
+ var node, lastNode;
+ var n = this.document.createDocumentFragment();
+ while((node = div.firstChild)){
+ lastNode = n.appendChild(node);
+ }
+ insertRange.insertNode(n);
+ if(lastNode) {
+ insertRange = insertRange.cloneRange();
+ insertRange.setStartAfter(lastNode);
+ insertRange.collapse(false);
+ selection.removeAllRanges();
+ selection.addRange(insertRange);
+ }
+ }
+ }else if(has("mozilla") && !argument.length){
+ //mozilla can not inserthtml an empty html to delete current selection
+ //so we delete the selection instead in this case
+ this.selection.remove(); // FIXME
+ }else{
+ rv = this.document.execCommand("inserthtml", false, argument);
+ }
+ return rv;
+ },
+
+ _boldImpl: function(argument){
+ // summary:
+ // This function implements an over-ride of the bold command.
+ // argument:
+ // Not used, operates by selection.
+ // tags:
+ // protected
+ var applied = false;
- if(has("ie")){
++ if(has("ie") || has("trident")){
+ this._adaptIESelection();
+ applied = this._adaptIEFormatAreaAndExec("bold");
+ }
+ if(!applied){
+ applied = this.document.execCommand("bold", false, argument);
+ }
+ return applied;
+ },
+
+ _italicImpl: function(argument){
+ // summary:
+ // This function implements an over-ride of the italic command.
+ // argument:
+ // Not used, operates by selection.
+ // tags:
+ // protected
+ var applied = false;
- if(has("ie")){
++ if(has("ie") || has("trident")){
+ this._adaptIESelection();
+ applied = this._adaptIEFormatAreaAndExec("italic");
+ }
+ if(!applied){
+ applied = this.document.execCommand("italic", false, argument);
+ }
+ return applied;
+ },
+
+ _underlineImpl: function(argument){
+ // summary:
+ // This function implements an over-ride of the underline command.
+ // argument:
+ // Not used, operates by selection.
+ // tags:
+ // protected
+ var applied = false;
- if(has("ie")){
++ if(has("ie") || has("trident")){
+ this._adaptIESelection();
+ applied = this._adaptIEFormatAreaAndExec("underline");
+ }
+ if(!applied){
+ applied = this.document.execCommand("underline", false, argument);
+ }
+ return applied;
+ },
+
+ _strikethroughImpl: function(argument){
+ // summary:
+ // This function implements an over-ride of the strikethrough command.
+ // argument:
+ // Not used, operates by selection.
+ // tags:
+ // protected
+ var applied = false;
- if(has("ie")){
++ if(has("ie") || has("trident")){
+ this._adaptIESelection();
+ applied = this._adaptIEFormatAreaAndExec("strikethrough");
+ }
+ if(!applied){
+ applied = this.document.execCommand("strikethrough", false, argument);
+ }
+ return applied;
+ },
+
+ _superscriptImpl: function(argument){
+ // summary:
+ // This function implements an over-ride of the superscript command.
+ // argument:
+ // Not used, operates by selection.
+ // tags:
+ // protected
+ var applied = false;
- if(has("ie")){
++ if(has("ie") || has("trident")){
+ this._adaptIESelection();
+ applied = this._adaptIEFormatAreaAndExec("superscript");
+ }
+ if(!applied){
+ applied = this.document.execCommand("superscript", false, argument);
+ }
+ return applied;
+ },
+
+ _subscriptImpl: function(argument){
+ // summary:
+ // This function implements an over-ride of the superscript command.
+ // argument:
+ // Not used, operates by selection.
+ // tags:
+ // protected
+ var applied = false;
- if(has("ie")){
++ if(has("ie") || has("trident")){
+ this._adaptIESelection();
+ applied = this._adaptIEFormatAreaAndExec("subscript");
+
+ }
+ if(!applied){
+ applied = this.document.execCommand("subscript", false, argument);
+ }
+ return applied;
+ },
+
+ _fontnameImpl: function(argument){
+ // summary:
+ // This function implements the fontname command
+ // argument:
+ // arguments to the exec command, if any.
+ // tags:
+ // protected
+ var isApplied;
- if(has("ie")){
++ if(has("ie") || has("trident")){
+ isApplied = this._handleTextColorOrProperties("fontname", argument);
+ }
+ if(!isApplied){
+ isApplied = this.document.execCommand("fontname", false, argument);
+ }
+ return isApplied;
+ },
+
+ _fontsizeImpl: function(argument){
+ // summary:
+ // This function implements the fontsize command
+ // argument:
+ // arguments to the exec command, if any.
+ // tags:
+ // protected
+ var isApplied;
- if(has("ie")){
++ if(has("ie") || has("trident")){
+ isApplied = this._handleTextColorOrProperties("fontsize", argument);
+ }
+ if(!isApplied){
+ isApplied = this.document.execCommand("fontsize", false, argument);
+ }
+ return isApplied;
+ },
+
+ _insertorderedlistImpl: function(argument){
+ // summary:
+ // This function implements the insertorderedlist command
+ // argument:
+ // arguments to the exec command, if any.
+ // tags:
+ // protected
+ var applied = false;
- if(has("ie")){
++ if(has("ie") || has("trident")){
+ applied = this._adaptIEList("insertorderedlist", argument);
+ }
+ if(!applied){
+ applied = this.document.execCommand("insertorderedlist", false, argument);
+ }
+ return applied;
+ },
+
+ _insertunorderedlistImpl: function(argument){
+ // summary:
+ // This function implements the insertunorderedlist command
+ // argument:
+ // arguments to the exec command, if any.
+ // tags:
+ // protected
+ var applied = false;
- if(has("ie")){
++ if(has("ie") || has("trident")){
+ applied = this._adaptIEList("insertunorderedlist", argument);
+ }
+ if(!applied){
+ applied = this.document.execCommand("insertunorderedlist", false, argument);
+ }
+ return applied;
+ },
+
+ getHeaderHeight: function(){
+ // summary:
+ // A function for obtaining the height of the header node
+ return this._getNodeChildrenHeight(this.header); // Number
+ },
+
+ getFooterHeight: function(){
+ // summary:
+ // A function for obtaining the height of the footer node
+ return this._getNodeChildrenHeight(this.footer); // Number
+ },
+
+ _getNodeChildrenHeight: function(node){
+ // summary:
+ // An internal function for computing the cumulative height of all child nodes of 'node'
+ // node:
+ // The node to process the children of;
+ var h = 0;
+ if(node && node.childNodes){
+ // IE didn't compute it right when position was obtained on the node directly is some cases,
+ // so we have to walk over all the children manually.
+ var i;
+ for(i = 0; i < node.childNodes.length; i++){
+ var size = domGeometry.position(node.childNodes[i]);
+ h += size.h;
+ }
+ }
+ return h; // Number
+ },
+
+ _isNodeEmpty: function(node, startOffset){
+ // summary:
+ // Function to test if a node is devoid of real content.
+ // node:
+ // The node to check.
+ // tags:
+ // private.
+ if(node.nodeType === 1/*element*/){
+ if(node.childNodes.length > 0){
+ return this._isNodeEmpty(node.childNodes[0], startOffset); // huh? why test just first child?
+ }
+ return true;
+ }else if(node.nodeType === 3/*text*/){
+ return (node.nodeValue.substring(startOffset) === "");
+ }
+ return false;
+ },
+
+ _removeStartingRangeFromRange: function(node, range){
+ // summary:
+ // Function to adjust selection range by removing the current
+ // start node.
+ // node:
+ // The node to remove from the starting range.
+ // range:
+ // The range to adapt.
+ // tags:
+ // private
+ if(node.nextSibling){
+ range.setStart(node.nextSibling, 0);
+ }else{
+ var parent = node.parentNode;
+ while(parent && parent.nextSibling == null){
+ //move up the tree until we find a parent that has another node, that node will be the next node
+ parent = parent.parentNode;
+ }
+ if(parent){
+ range.setStart(parent.nextSibling, 0);
+ }
+ }
+ return range;
+ },
+
+ _adaptIESelection: function(){
+ // summary:
+ // Function to adapt the IE range by removing leading 'newlines'
+ // Needed to fix issue with bold/italics/underline not working if
+ // range included leading 'newlines'.
+ // In IE, if a user starts a selection at the very end of a line,
+ // then the native browser commands will fail to execute correctly.
+ // To work around the issue, we can remove all empty nodes from
+ // the start of the range selection.
+ var selection = rangeapi.getSelection(this.window);
+ if(selection && selection.rangeCount && !selection.isCollapsed){
+ var range = selection.getRangeAt(0);
+ var firstNode = range.startContainer;
+ var startOffset = range.startOffset;
+
+ while(firstNode.nodeType === 3/*text*/ && startOffset >= firstNode.length && firstNode.nextSibling){
+ //traverse the text nodes until we get to the one that is actually highlighted
+ startOffset = startOffset - firstNode.length;
+ firstNode = firstNode.nextSibling;
+ }
+
+ //Remove the starting ranges until the range does not start with an empty node.
+ var lastNode = null;
+ while(this._isNodeEmpty(firstNode, startOffset) && firstNode !== lastNode){
+ lastNode = firstNode; //this will break the loop in case we can't find the next sibling
+ range = this._removeStartingRangeFromRange(firstNode, range); //move the start container to the next node in the range
+ firstNode = range.startContainer;
+ startOffset = 0; //start at the beginning of the new starting range
+ }
+ selection.removeAllRanges();// this will work as long as users cannot select multiple ranges. I have not been able to do that in the editor.
+ selection.addRange(range);
+ }
+ },
+
+ _adaptIEFormatAreaAndExec: function(command){
+ // summary:
+ // Function to handle IE's quirkiness regarding how it handles
+ // format commands on a word. This involves a lit of node splitting
+ // and format cloning.
+ // command:
+ // The format command, needed to check if the desired
+ // command is true or not.
+ var selection = rangeapi.getSelection(this.window);
+ var doc = this.document;
+ var rs, ret, range, txt, startNode, endNode, breaker, sNode;
+ if(command && selection && selection.isCollapsed){
+ var isApplied = this.queryCommandValue(command);
+ if(isApplied){
+
+ // We have to split backwards until we hit the format
+ var nNames = this._tagNamesForCommand(command);
+ range = selection.getRangeAt(0);
+ var fs = range.startContainer;
+ if(fs.nodeType === 3){
+ var offset = range.endOffset;
+ if(fs.length < offset){
+ //We are not looking from the right node, try to locate the correct one
+ ret = this._adjustNodeAndOffset(rs, offset);
+ fs = ret.node;
+ offset = ret.offset;
+ }
+ }
+ var topNode;
+ while(fs && fs !== this.editNode){
+ // We have to walk back and see if this is still a format or not.
+ // Hm, how do I do this?
+ var tName = fs.tagName ? fs.tagName.toLowerCase() : "";
+ if(array.indexOf(nNames, tName) > -1){
+ topNode = fs;
+ break;
+ }
+ fs = fs.parentNode;
+ }
+
+ // Okay, we have a stopping place, time to split things apart.
+ if(topNode){
+ // Okay, we know how far we have to split backwards, so we have to split now.
+ rs = range.startContainer;
+ var newblock = doc.createElement(topNode.tagName);
+ domConstruct.place(newblock, topNode, "after");
+ if(rs && rs.nodeType === 3){
+ // Text node, we have to split it.
+ var nodeToMove, tNode;
+ var endOffset = range.endOffset;
+ if(rs.length < endOffset){
+ //We are not splitting the right node, try to locate the correct one
+ ret = this._adjustNodeAndOffset(rs, endOffset);
+ rs = ret.node;
+ endOffset = ret.offset;
+ }
+
+ txt = rs.nodeValue;
+ startNode = doc.createTextNode(txt.substring(0, endOffset));
+ var endText = txt.substring(endOffset, txt.length);
+ if(endText){
+ endNode = doc.createTextNode(endText);
+ }
+ // Place the split, then remove original nodes.
+ domConstruct.place(startNode, rs, "before");
+ if(endNode){
+ breaker = doc.createElement("span");
+ breaker.className = "ieFormatBreakerSpan";
+ domConstruct.place(breaker, rs, "after");
+ domConstruct.place(endNode, breaker, "after");
+ endNode = breaker;
+ }
+ domConstruct.destroy(rs);
+
+ // Okay, we split the text. Now we need to see if we're
+ // parented to the block element we're splitting and if
+ // not, we have to split all the way up. Ugh.
+ var parentC = startNode.parentNode;
+ var tagList = [];
+ var tagData;
+ while(parentC !== topNode){
+ var tg = parentC.tagName;
+ tagData = {tagName: tg};
+ tagList.push(tagData);
+
+ var newTg = doc.createElement(tg);
+ // Clone over any 'style' data.
+ if(parentC.style){
+ if(newTg.style){
+ if(parentC.style.cssText){
+ newTg.style.cssText = parentC.style.cssText;
+ tagData.cssText = parentC.style.cssText;
+ }
+ }
+ }
+ // If font also need to clone over any font data.
+ if(parentC.tagName === "FONT"){
+ if(parentC.color){
+ newTg.color = parentC.color;
+ tagData.color = parentC.color;
+ }
+ if(parentC.face){
+ newTg.face = parentC.face;
+ tagData.face = parentC.face;
+ }
+ if(parentC.size){ // this check was necessary on IE
+ newTg.size = parentC.size;
+ tagData.size = parentC.size;
+ }
+ }
+ if(parentC.className){
+ newTg.className = parentC.className;
+ tagData.className = parentC.className;
+ }
+
+ // Now move end node and every sibling
+ // after it over into the new tag.
+ if(endNode){
+ nodeToMove = endNode;
+ while(nodeToMove){
+ tNode = nodeToMove.nextSibling;
+ newTg.appendChild(nodeToMove);
+ nodeToMove = tNode;
+ }
+ }
+ if(newTg.tagName == parentC.tagName){
+ breaker = doc.createElement("span");
+ breaker.className = "ieFormatBreakerSpan";
+ domConstruct.place(breaker, parentC, "after");
+ domConstruct.place(newTg, breaker, "after");
+ }else{
+ domConstruct.place(newTg, parentC, "after");
+ }
+ startNode = parentC;
+ endNode = newTg;
+ parentC = parentC.parentNode;
+ }
+
+ // Lastly, move the split out all the split tags
+ // to the new block as they should now be split properly.
+ if(endNode){
+ nodeToMove = endNode;
+ if(nodeToMove.nodeType === 1 || (nodeToMove.nodeType === 3 && nodeToMove.nodeValue)){
+ // Non-blank text and non-text nodes need to clear out that blank space
+ // before moving the contents.
+ newblock.innerHTML = "";
+ }
+ while(nodeToMove){
+ tNode = nodeToMove.nextSibling;
+ newblock.appendChild(nodeToMove);
+ nodeToMove = tNode;
+ }
+ }
+
+ // We had intermediate tags, we have to now recreate them inbetween the split
+ // and restore what styles, classnames, etc, we can.
+ var newrange;
+ if(tagList.length){
+ tagData = tagList.pop();
+ var newContTag = doc.createElement(tagData.tagName);
+ if(tagData.cssText && newContTag.style){
+ newContTag.style.cssText = tagData.cssText;
+ }
+ if(tagData.className){
+ newContTag.className = tagData.className;
+ }
+ if(tagData.tagName === "FONT"){
+ if(tagData.color){
+ newContTag.color = tagData.color;
+ }
+ if(tagData.face){
+ newContTag.face = tagData.face;
+ }
+ if(tagData.size){
+ newContTag.size = tagData.size;
+ }
+ }
+ domConstruct.place(newContTag, newblock, "before");
+ while(tagList.length){
+ tagData = tagList.pop();
+ var newTgNode = doc.createElement(tagData.tagName);
+ if(tagData.cssText && newTgNode.style){
+ newTgNode.style.cssText = tagData.cssText;
+ }
+ if(tagData.className){
+ newTgNode.className = tagData.className;
+ }
+ if(tagData.tagName === "FONT"){
+ if(tagData.color){
+ newTgNode.color = tagData.color;
+ }
+ if(tagData.face){
+ newTgNode.face = tagData.face;
+ }
+ if(tagData.size){
+ newTgNode.size = tagData.size;
+ }
+ }
+ newContTag.appendChild(newTgNode);
+ newContTag = newTgNode;
+ }
+
+ // Okay, everything is theoretically split apart and removed from the content
+ // so insert the dummy text to select, select it, then
+ // clear to position cursor.
+ sNode = doc.createTextNode(".");
+ breaker.appendChild(sNode);
+ newContTag.appendChild(sNode);
+ newrange = rangeapi.create(this.window);
+ newrange.setStart(sNode, 0);
+ newrange.setEnd(sNode, sNode.length);
+ selection.removeAllRanges();
+ selection.addRange(newrange);
+ this.selection.collapse(false);
+ sNode.parentNode.innerHTML = "";
+ }else{
+ // No extra tags, so we have to insert a breaker point and rely
+ // on filters to remove it later.
+ breaker = doc.createElement("span");
+ breaker.className = "ieFormatBreakerSpan";
+ sNode = doc.createTextNode(".");
+ breaker.appendChild(sNode);
+ domConstruct.place(breaker, newblock, "before");
+ newrange = rangeapi.create(this.window);
+ newrange.setStart(sNode, 0);
+ newrange.setEnd(sNode, sNode.length);
+ selection.removeAllRanges();
+ selection.addRange(newrange);
+ this.selection.collapse(false);
+ sNode.parentNode.innerHTML = "";
+ }
+ if(!newblock.firstChild){
+ // Empty, we don't need it. Split was at end or similar
+ // So, remove it.
+ domConstruct.destroy(newblock);
+ }
+ return true;
+ }
+ }
+ return false;
+ }else{
+ range = selection.getRangeAt(0);
+ rs = range.startContainer;
+ if(rs && rs.nodeType === 3){
+ // Text node, we have to split it.
+ var offset = range.startOffset;
+ if(rs.length < offset){
+ //We are not splitting the right node, try to locate the correct one
+ ret = this._adjustNodeAndOffset(rs, offset);
+ rs = ret.node;
+ offset = ret.offset;
+ }
+ txt = rs.nodeValue;
+ startNode = doc.createTextNode(txt.substring(0, offset));
+ var endText = txt.substring(offset);
+ if(endText !== ""){
+ endNode = doc.createTextNode(txt.substring(offset));
+ }
+ // Create a space, we'll select and bold it, so
+ // the whole word doesn't get bolded
+ breaker = doc.createElement("span");
+ sNode = doc.createTextNode(".");
+ breaker.appendChild(sNode);
+ if(startNode.length){
+ domConstruct.place(startNode, rs, "after");
+ }else{
+ startNode = rs;
+ }
+ domConstruct.place(breaker, startNode, "after");
+ if(endNode){
+ domConstruct.place(endNode, breaker, "after");
+ }
+ domConstruct.destroy(rs);
+ var newrange = rangeapi.create(this.window);
+ newrange.setStart(sNode, 0);
+ newrange.setEnd(sNode, sNode.length);
+ selection.removeAllRanges();
+ selection.addRange(newrange);
+ doc.execCommand(command);
+ domConstruct.place(breaker.firstChild, breaker, "before");
+ domConstruct.destroy(breaker);
+ newrange.setStart(sNode, 0);
+ newrange.setEnd(sNode, sNode.length);
+ selection.removeAllRanges();
+ selection.addRange(newrange);
+ this.selection.collapse(false);
+ sNode.parentNode.innerHTML = "";
+ return true;
+ }
+ }
+ }else{
+ return false;
+ }
+ },
+
+ _adaptIEList: function(command /*===== , argument =====*/){
+ // summary:
+ // This function handles normalizing the IE list behavior as
+ // much as possible.
+ // command:
+ // The list command to execute.
+ // argument:
+ // Any additional argument.
+ // tags:
+ // private
+ var selection = rangeapi.getSelection(this.window);
+ if(selection.isCollapsed){
+ // In the case of no selection, lets commonize the behavior and
+ // make sure that it indents if needed.
+ if(selection.rangeCount && !this.queryCommandValue(command)){
+ var range = selection.getRangeAt(0);
+ var sc = range.startContainer;
+ if(sc && sc.nodeType == 3){
+ // text node. Lets see if there is a node before it that isn't
+ // some sort of breaker.
+ if(!range.startOffset){
+ // We're at the beginning of a text area. It may have been br split
+ // Who knows? In any event, we must create the list manually
+ // or IE may shove too much into the list element. It seems to
+ // grab content before the text node too if it's br split.
+ // Why can't IE work like everyone else?
+
+ // Create a space, we'll select and bold it, so
+ // the whole word doesn't get bolded
+ var lType = "ul";
+ if(command === "insertorderedlist"){
+ lType = "ol";
+ }
+ var list = this.document.createElement(lType);
+ var li = domConstruct.create("li", null, list);
+ domConstruct.place(list, sc, "before");
+ // Move in the text node as part of the li.
+ li.appendChild(sc);
+ // We need a br after it or the enter key handler
+ // sometimes throws errors.
+ domConstruct.create("br", null, list, "after");
+ // Okay, now lets move our cursor to the beginning.
+ var newrange = rangeapi.create(this.window);
+ newrange.setStart(sc, 0);
+ newrange.setEnd(sc, sc.length);
+ selection.removeAllRanges();
+ selection.addRange(newrange);
+ this.selection.collapse(true);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ },
+
+ _handleTextColorOrProperties: function(command, argument){
+ // summary:
+ // This function handles applying text color as best it is
+ // able to do so when the selection is collapsed, making the
+ // behavior cross-browser consistent. It also handles the name
+ // and size for IE.
+ // command:
+ // The command.
+ // argument:
+ // Any additional arguments.
+ // tags:
+ // private
+ var selection = rangeapi.getSelection(this.window);
+ var doc = this.document;
+ var rs, ret, range, txt, startNode, endNode, breaker, sNode;
+ argument = argument || null;
+ if(command && selection && selection.isCollapsed){
+ if(selection.rangeCount){
+ range = selection.getRangeAt(0);
+ rs = range.startContainer;
+ if(rs && rs.nodeType === 3){
+ // Text node, we have to split it.
+ var offset = range.startOffset;
+ if(rs.length < offset){
+ //We are not splitting the right node, try to locate the correct one
+ ret = this._adjustNodeAndOffset(rs, offset);
+ rs = ret.node;
+ offset = ret.offset;
+ }
+ txt = rs.nodeValue;
+ startNode = doc.createTextNode(txt.substring(0, offset));
+ var endText = txt.substring(offset);
+ if(endText !== ""){
+ endNode = doc.createTextNode(txt.substring(offset));
+ }
+ // Create a space, we'll select and bold it, so
+ // the whole word doesn't get bolded
+ breaker = doc.createElement("span");
+ sNode = doc.createTextNode(".");
+ breaker.appendChild(sNode);
+ // Create a junk node to avoid it trying to style the breaker.
+ // This will get destroyed later.
+ var extraSpan = doc.createElement("span");
+ breaker.appendChild(extraSpan);
+ if(startNode.length){
+ domConstruct.place(startNode, rs, "after");
+ }else{
+ startNode = rs;
+ }
+ domConstruct.place(breaker, startNode, "after");
+ if(endNode){
+ domConstruct.place(endNode, breaker, "after");
+ }
+ domConstruct.destroy(rs);
+ var newrange = rangeapi.create(this.window);
+ newrange.setStart(sNode, 0);
+ newrange.setEnd(sNode, sNode.length);
+ selection.removeAllRanges();
+ selection.addRange(newrange);
+ if(has("webkit")){
+ // WebKit is frustrating with positioning the cursor.
+ // It stinks to have a selected space, but there really
+ // isn't much choice here.
+ var style = "color";
+ if(command === "hilitecolor" || command === "backcolor"){
+ style = "backgroundColor";
+ }
+ domStyle.set(breaker, style, argument);
+ this.selection.remove();
+ domConstruct.destroy(extraSpan);
+ breaker.innerHTML = " "; //
+ this.selection.selectElement(breaker);
+ this.focus();
+ }else{
+ this.execCommand(command, argument);
+ domConstruct.place(breaker.firstChild, breaker, "before");
+ domConstruct.destroy(breaker);
+ newrange.setStart(sNode, 0);
+ newrange.setEnd(sNode, sNode.length);
+ selection.removeAllRanges();
+ selection.addRange(newrange);
+ this.selection.collapse(false);
+ sNode.parentNode.removeChild(sNode);
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+ },
+
+ _adjustNodeAndOffset: function(/*DomNode*/node, /*Int*/offset){
+ // summary:
+ // In the case there are multiple text nodes in a row the offset may not be within the node.
+ // If the offset is larger than the node length, it will attempt to find
+ // the next text sibling until it locates the text node in which the offset refers to
+ // node:
+ // The node to check.
+ // offset:
+ // The position to find within the text node
+ // tags:
+ // private.
+ while(node.length < offset && node.nextSibling && node.nextSibling.nodeType === 3){
+ //Adjust the offset and node in the case of multiple text nodes in a row
+ offset = offset - node.length;
+ node = node.nextSibling;
+ }
+ return {"node": node, "offset": offset};
+ },
+
+ _tagNamesForCommand: function(command){
+ // summary:
+ // Function to return the tab names that are associated
+ // with a particular style.
+ // command: String
+ // The command to return tags for.
+ // tags:
+ // private
+ if(command === "bold"){
+ return ["b", "strong"];
+ }else if(command === "italic"){
+ return ["i", "em"];
+ }else if(command === "strikethrough"){
+ return ["s", "strike"];
+ }else if(command === "superscript"){
+ return ["sup"];
+ }else if(command === "subscript"){
+ return ["sub"];
+ }else if(command === "underline"){
+ return ["u"];
+ }
+ return [];
+ },
+
+ _stripBreakerNodes: function(/*DOMNode*/ node){
+ // summary:
+ // Function for stripping out the breaker spans inserted by the formatting command.
+ // Registered as a filter for IE, handles the breaker spans needed to fix up
+ // How bold/italic/etc, work when selection is collapsed (single cursor).
+ if(!this.isLoaded){
+ return;
+ } // this method requires init to be complete
+ query(".ieFormatBreakerSpan", node).forEach(function(b){
+ while(b.firstChild){
+ domConstruct.place(b.firstChild, b, "before");
+ }
+ domConstruct.destroy(b);
+ });
+ return node;
+ },
+
+ _stripTrailingEmptyNodes: function(/*DOMNode*/ node){
+ // summary:
+ // Function for stripping trailing nodes without any text, excluding trailing nodes
+ // like <img> or <div><img></div>, even though they don't have text either.
+
+ function isEmpty(node){
+ // If not for old IE we could check for Element children by node.firstElementChild
+ return (/^(p|div|br)$/i.test(node.nodeName) && node.children.length == 0 &&
+ /^[\s\xA0]*$/.test(node.textContent || node.innerText || "")) ||
+ (node.nodeType === 3/*text*/ && /^[\s\xA0]*$/.test(node.nodeValue));
+ }
+ while(node.lastChild && isEmpty(node.lastChild)){
+ domConstruct.destroy(node.lastChild);
+ }
+
+ return node;
+ },
+
+ // Needed to support ToggleDir plugin. Intentionally not inside if(has("dojo-bidi")) block
+ // so that (for backwards compatibility) ToggleDir plugin works even when has("dojo-bidi") is falsy.
+ _setTextDirAttr: function(/*String*/ value){
+ // summary:
+ // Sets textDir attribute. Sets direction of editNode accordingly.
+ this._set("textDir", value);
+ this.onLoadDeferred.then(lang.hitch(this, function(){
+ this.editNode.dir = value;
+ }));
+ }
+ });
+
+ return RichText;
+});
diff --cc dijit/package.json
index fc2681a,0000000..52bfcec
mode 100644,000000..100644
--- a/dijit/package.json
+++ b/dijit/package.json
@@@ -1,26 -1,0 +1,26 @@@
+{
+ "name": "dijit",
- "version":"1.10.1",
++ "version":"1.10.2",
+ "directories": {
+ "lib": "."
+ },
+ "main":"main",
+ "dependencies": {
- "dojo":"1.10.1"
++ "dojo":"1.10.2"
+ },
+ "description": "Dijit provides a complete collection of user interface controls based on Dojo, giving you the power to create web applications that are highly optimized for usability, performance, internationalization, accessibility, but above all deliver an incredible user experience.",
+ "licenses": [
+ {
+ "type": "AFLv2.1",
+ "url": "http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L43"
+ },
+ {
+ "type": "BSD",
+ "url": "http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L13"
+ }
+ ],
+ "bugs": "http://bugs.dojotoolkit.org/",
+ "keywords": ["JavaScript", "Dojo", "Widget"],
+ "homepage": "http://dojotoolkit.org/",
+ "dojoBuild": "dijit.profile.js"
+}
diff --cc dijit/tests/form/robot/Select.html
index f077c4b,0000000..fcbe453
mode 100644,000000..100644
--- a/dijit/tests/form/robot/Select.html
+++ b/dijit/tests/form/robot/Select.html
@@@ -1,840 -1,0 +1,912 @@@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <title>doh.robot Select Test</title>
+
+ <style>
+ @import "../../../../util/doh/robot/robot.css";
+ </style>
+
+ <!-- required: dojo.js -->
+ <script type="text/javascript" src="../../../../dojo/dojo.js"></script>
+
+ <script type="text/javascript">
+ dojo.require("dijit.robotx");
+ dojo.require("dojo.date");
+ dojo.require("dijit.tests.helpers"); // functions to help test
+
+ dojo.ready(function(){
+ doh.robot.initRobot('../test_Select.html');
+
+ // refs to parts of s1 Select
+ var s1, s1menu, Tenessee, Virginia, Washington, // s1 select
+ s2, s2menu, s8a, s8a_menu, s8b, s8b_menu, s3, s3_menu; // long list selects
+
+ // log of calls to onChange handler
+ var changes = [];
+
++ var focus; // dijit/focus
++
+ doh.register("setup",
+ function setUp(){
+ s1 = dijit.byId('s1');
+ s1_menu = dijit.byId('s1_menu');
+ doh.t(s1, "s1 select exists");
+ doh.t(s1_menu, "s1 menu exists");
+
+ s2 = dijit.byId('s2');
+ s2_menu = dijit.byId('s2_menu');
+ s8a = dijit.byId('s8a');
+ s8a_menu = dijit.byId('s8a_menu');
+ s8b = dijit.byId('s8b');
+ s8b_menu = dijit.byId('s8b_menu');
+ s3 = dijit.byId('s3');
+ s3_menu = dijit.byId('s3_menu');
+ s13 = dijit.byId('s13');
+ s13_menu = dijit.byId('s13_menu');
+ s14 = dijit.byId('s14');
+ s14_menu = dijit.byId('s14_menu');
+
+ form = dijit.byId('form');
+
+ // preload menus since first load can take a while
+ var doNothing = function(){};
+ s1.loadDropDown(doNothing);
+ dijit.byId('s5').loadDropDown(doNothing);
+ dijit.byId('s6').loadDropDown(doNothing);
+ s8a.loadDropDown(doNothing);
+ s8b.loadDropDown(doNothing);
++
++ // modules in test page
++ focus = dojo.global.require("dijit/focus");
+ }
+ );
+
+ doh.register("mouse", [
+ {
+ name: "two clicks to select",
+ timeout: 10000,
+ runTest: function(){
+ var d = new doh.Deferred();
+
+ // click to open drop down
+ doh.robot.mouseMoveAt(s1.domNode, 0, 1);
+ doh.robot.mouseClick({left:true}, 500);
+ doh.robot.sequence(d.getTestErrback(function(){
+ doh.t(isVisible(s1_menu), "drop down menu displayed after click");
+ doh.is("true", s1.domNode.getAttribute('aria-expanded'), 'aria-expanded');
+ doh.is("listbox", s1_menu.domNode.getAttribute('role'), 'menu role');
+ doh.is(5, dojo.query("tr", s1_menu.domNode).length, "5 options in menu");
-
++
+ var selectedOptions = dojo.query(".dijitSelectSelectedOption .dijitMenuItemLabel", s1_menu.domNode);
+ doh.is(1, selectedOptions.length, "one selected option");
+ doh.is("Virginia", innerText(selectedOptions[0]), "Virginia highlighted");
+
+ Washington = dojo.query("tr", s1_menu.domNode)[2];
+ doh.t(Washington, "Washington is another menu choice");
+ }), 1000);
+
+ // select an option in the drop down
+ doh.robot.mouseMoveAt(function(){
+ // function wrapper because Washington isn't defined until about sequence() runs
+ return Washington;
+ }, 0, 1);
+ doh.robot.mouseClick({left:true}, 500);
+ doh.robot.sequence(d.getTestErrback(function(){
+ doh.t(isHidden(s1_menu), "clicking choice hides menu");
+ doh.is("false", s1.domNode.getAttribute('aria-expanded'), 'aria-expanded');
-
++
+ doh.is("Washington", innerText(s1.containerNode), "select shows selected option");
+ doh.is("WA", s1.get("value"), "get(value) after selecting Washington");
+ }), 1000);
+
+ // click to open drop down again
+ doh.robot.mouseMoveAt(s1.domNode, 0, 1);
+ doh.robot.mouseClick({left:true}, 500);
+ doh.robot.sequence(d.getTestErrback(function(){
+ doh.t(isVisible(s1_menu), "drop down menu displayed after third click");
-
++
+ var selectedOptions = dojo.query(".dijitSelectSelectedOption .dijitMenuItemLabel", s1_menu.domNode);
+ doh.is(1, selectedOptions.length, "one selected option");
+ doh.is("Washington", innerText(selectedOptions[0]), "Washington highlighted");
+ }), 1000);
+
+ // clicking away should close the drop down
+ doh.robot.mouseMove(10, 10, 500);
+ doh.robot.mouseClick({left:true}, 500);
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.t(isHidden(s1_menu), "clicking away hides menu");
+ }), 1000);
+
+ return d;
+ }
+ },
+
+ {
+ name: "mouse down, slide, mouse up",
+ timeout: 15000,
+ setUp: function(){
+ s1.set("value", "VA");
+ },
+ runTest: function(){
+ var d = new doh.Deferred();
+
+ // mouse-down to open drop down
+ doh.robot.mouseMoveAt(s1.domNode, 0, 1);
+ doh.robot.mousePress({left:true}, 500);
+ doh.robot.sequence(d.getTestErrback(function(){
+ doh.t(isVisible(s1_menu), "drop down menu displayed after mouse down");
+ doh.is(5, dojo.query("tr", s1_menu.domNode).length, "5 options in menu");
-
++
+ var selectedOptions = dojo.query(".dijitSelectSelectedOption .dijitMenuItemLabel", s1_menu.domNode);
+ doh.is(1, selectedOptions.length, "one selected option");
+ doh.is("Virginia", innerText(selectedOptions[0]), "Virginia highlighted");
+
+ Washington = dojo.query("tr", s1_menu.domNode)[2];
+ doh.t(Washington, "Washington is another menu choice");
+ }), 1000);
+
+ // select an option in the drop down
+ doh.robot.mouseMoveAt(function(){
+ // function wrapper because Washington isn't defined until above sequence() runs
+ return Washington;
+ }, 0, 1);
+ doh.robot.mouseRelease({left:true}, 500);
+ doh.robot.sequence(d.getTestErrback(function(){
+ doh.t(isHidden(s1_menu), "clicking choice hides menu");
-
++
+ doh.is("Washington", innerText(s1.containerNode), "select shows selected option");
+ doh.is("WA", s1.get("value"), "get(value) after selecting Washington");
+ }), 1000);
+
+ // mouse down to open drop down again
+ doh.robot.mouseMoveAt(s1.domNode, 0, 1);
+ doh.robot.mousePress({left:true}, 500);
+ doh.robot.sequence(d.getTestErrback(function(){
+ doh.t(isVisible(s1_menu), "drop down menu displayed after second mouse down");
-
++
+ var selectedOptions = dojo.query(".dijitSelectSelectedOption .dijitMenuItemLabel", s1_menu.domNode);
+ doh.is(1, selectedOptions.length, "one selected option");
+ doh.is("Washington", innerText(selectedOptions[0]), "Washington highlighted");
+ }), 1000);
+
+ // mouse up away from node should leave drop down open
+ doh.robot.mouseMove(10, 10, 500);
+ doh.robot.mouseRelease({left:true}, 500);
+ doh.robot.sequence(d.getTestErrback(function(){
+ doh.t(isVisible(s1_menu), "mouse up away from menu leaves menu open");
+ }), 500);
+
+ // then another mouse click should close it
+ doh.robot.mouseClick({left:true}, 500);
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.t(isHidden(s1_menu), "clicking on blank screen hides menu");
+ }), 1000);
+
+ return d;
+ }
+ },
+ {
+ name: "maxHeight",
+ timeout: 10000,
+ runTest: function(){
+ var d = new doh.Deferred();
+
+ // open drop down
+ doh.robot.mouseMoveAt(s8a.domNode, 0, 1);
+ doh.robot.mouseClick({left:true}, 500);
+ doh.robot.sequence(d.getTestErrback(function(){
+ doh.t(isVisible(s8a_menu.domNode), "drop down menu displayed after mouse down");
-
++
+ var pos = dojo.position(s8a_menu.domNode.parentNode); // scrollbar on wrapper
+ doh.t(pos.h >= 200, "height at least 200: " + pos.h);
+ doh.t(pos.h < 220, "height including borders not much more than 200: " + pos.h);
+ }), 1000);
+
+ // close drop down
+ doh.robot.mouseMove(10, 10, 500, 1);
+ doh.robot.mouseClick({left:true}, 500);
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.t(isHidden(s8a_menu), "clicking on blank screen hides menu");
+ }), 1000);
+
+ return d;
+ }
+ },
+
+ // Not sure if this is good or not, but sometimes menu can overlap
- // the select itself. In this case, mouse up should leave the menu open
++ // the select itself. In this case, mouse up should leave the menu open
+ {
+ name: "menu overlaps select",
+ timeout: 6000,
+ runTest: function(){
+ var d = new doh.Deferred(),
+ overlapping = false;
+
+ // open drop down
+ doh.robot.mouseMoveAt(s8b.domNode, 0, 1);
+ doh.robot.mouseClick({left:true}, 500);
+ doh.robot.sequence(d.getTestErrback(function(){
+ doh.t(isVisible(s8b_menu.domNode), "drop down menu displayed after mouse click");
+
+ var select = dojo.position(s8b.domNode),
+ menu = dojo.position(s8b_menu.domNode);
+ doh.t(menu.h >= 220, "menu has big height: " + menu.h);
+
+ // Sometimes the menu overlaps and (at least on chrome/win) sometimes it doesn't.
+ // Not sure why the behavior changes but not treating it as a bug.
+ overlapping = menu.y < select.y && menu.y+menu.h > select.y+select.h;
+ }), 1000);
+
+ // clicking again should select the option from the menu
+ doh.robot.mouseClick({left:true}, 500);
+ doh.robot.sequence(d.getTestCallback(function(){
+ if(overlapping){
+ doh.t(isHidden(s8b_menu), "clicking again selects something from menu");
+ }
+ }), 1000);
+
+ return d;
+ }
+ }
+
+ ]);
+
+ doh.register("keyboard", [
+ {
+ name: "tabIndex",
+ timeout: 10000,
+ setUp: function(){
+ dojo.byId("htmlSelect2").focus();
+ dijit.byId("s2").set("disabled", true);
+ },
+ runTest: function(){
+ var d = new doh.Deferred();
+
+ // tab to s1
+ doh.robot.keyPress(dojo.keys.TAB, 500, {});
+ doh.robot.sequence(d.getTestErrback(function(){
+ doh.is("s1", dojo.global.dijit.focus.curNode.id)
+
+ // Make sure _KeyNavMixin superclass didn't set tabIndex to -1.
+ doh.is("0", dijit.byId("s1").domNode.tabIndex, "tabIndex")
+ }), 1000);
+
+ // tab to button after s1
+ doh.robot.keyPress(dojo.keys.TAB, 500, {});
+ doh.robot.sequence(d.getTestErrback(function(){
+ doh.is("s1button", dojo.global.dijit.focus.curNode.id)
+ }), 1000);
+
+ // tab again should skip disabled s2, and go to s3
+ doh.robot.keyPress(dojo.keys.TAB, 500, {});
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.is("s3", dojo.global.dijit.focus.curNode.id)
+ }), 1000);
+
+ return d;
+ },
+ tearDown: function(){
+ dijit.byId("s2").set("disabled", false);
+ }
+ },
+ {
+ name: "selecting",
+ timeout: 9000,
+ setUp: function(){
+ dojo.byId("htmlSelect2").focus();
+ },
+ runTest: function(){
+ var d = new doh.Deferred();
+ var nextFocused, previousSelectionFocused, wasVisible, stillVisible, widgetFocused;
+
+ // tab to s1
+ doh.robot.keyPress(dojo.keys.TAB, 1000, {});
+ doh.robot.sequence(function(){
+ widgetFocused = dojo.global.dijit.focus.curNode.id == "s1";
+ }, 1000);
+
+ // down arrow to open drop down and focus first item
+ doh.robot.keyPress(dojo.keys.DOWN_ARROW, 500, {});
+ doh.robot.sequence(function(){
+ wasVisible = isVisible(s1_menu);
+ previousSelectionFocused = dojo.query("tr", s1_menu.domNode)[2] == dojo.global.dijit.focus.curNode;
+ }, 1000);
+
+ // down arrow to next menu choice
+ doh.robot.keyPress(dojo.keys.DOWN_ARROW, 500, {});
+ doh.robot.sequence(function(){
+ stillVisible = isVisible(s1_menu);
+ nextFocused = dojo.query("tr", s1_menu.domNode)[3] == dojo.global.dijit.focus.curNode;
+ }, 500);
+
+ // ENTER to select option
+ doh.robot.keyPress(dojo.keys.ENTER, 500, {});
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.t(widgetFocused, "widget focus");
+ doh.t(wasVisible, "drop down menu displayed after down arrow");
+ doh.t(previousSelectionFocused, "focused on previously selected item")
+ doh.t(stillVisible, "drop down menu still displayed");
+ doh.t(nextFocused, "focused on next menu item")
+ doh.t(isHidden(s1_menu), "drop down menu closes after ENTER key");
+ doh.is("FL", s1.get("value"));
+ }), 1000);
+
+ return d;
+ },
+ tearDown: function(){
+ dijit.byId("s1").set("value", "VA");
+ }
+ },
+ {
+ name: "ESC to close menu",
+ timeout: 9000,
+ setUp: function(){
+ dojo.byId("htmlSelect2").focus();
+ },
+ runTest: function(){
+ var d = new doh.Deferred();
+ var nextFocused, wasVisible, widgetFocused;
+
+ // tab to s1
+ doh.robot.keyPress(dojo.keys.TAB, 1000, {});
+ doh.robot.sequence(function(){
+ widgetFocused = dojo.global.dijit.focus.curNode.id == "s1";
+ }, 1000);
+
+ // down arrow to open drop down and focus first item
+ doh.robot.keyPress(dojo.keys.DOWN_ARROW, 500, {});
+ doh.robot.sequence(function(){
+ wasVisible = isVisible(s1_menu);
+ }, 1000);
+ doh.robot.keyPress(dojo.keys.DOWN_ARROW, 500, {});
+ doh.robot.sequence(function(){
+ nextFocused = dojo.query("tr", s1_menu.domNode)[2] == dojo.global.dijit.focus.curNode;
+ }, 500);
+
+ // ESC to close menu
+ doh.robot.keyPress(dojo.keys.ESCAPE, 500, {});
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.t(widgetFocused, "widget focus");
+ doh.t(wasVisible, "drop down menu displayed after down arrow");
+ doh.t(nextFocused, "focused next menu item")
+ doh.t(isHidden(s1_menu), "drop down menu closes after ENTER key");
+ doh.is("VA", s1.get("value"), "value unchanged");
+ }), 1000);
+
+ return d;
+ },
+ tearDown: function(){
+ dijit.byId("s1").set("value", "VA");
+ }
+ },
+ {
+ name: "single letter search after failed multiple letter search, no dropdown",
+ timeout: 9000,
+ setUp: function(){
+ s2.set("value", "CA");
+ s2.focus();
+ },
+ runTest: function(){
+ var d = new doh.Deferred();
+
+ // c search fails to find CA due to multi-character searching
+ doh.robot.typeKeys("aaaaaaac", 1000, 1200);
+
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.is("AZ", s2.get("value"), "hidden value");
+ doh.f(isVisible(s2_menu), "no menu");
+ }), 1000);
+
+ return d;
+ }
+ },
+ {
+ name: "multiple letter search after failed multiple letter search, no dropdown",
+ timeout: 9000,
+ runTest: function(){
+ var d = new doh.Deferred();
+
+ // ala suffix fails to find either alabama or alaska due to multi-character searching
+ doh.robot.typeKeys("aaaaaaaaala", 1000, 1400);
+
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.is("AR", s2.get("value"), "hidden value");
+ doh.f(isVisible(s2_menu), "no menu");
+ }), 1000);
+
+ return d;
+ }
+ },
+ {
+ name: "multiple letter searching from current, no dropdown",
+ timeout: 9000,
+ runTest: function(){
+ var d = new doh.Deferred();
+
+ // search for Alabama
+ doh.robot.typeKeys("al", 1000, 1600); // al search finds alabama
+
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.is("AL", s2.get("value"), "hidden value");
+ doh.f(isVisible(s2_menu), "no menu");
+ }), 1000);
+
+ return d;
+ }
+ },
+ {
+ name: "multiple letter searching find next, no dropdown",
+ timeout: 9000,
+ runTest: function(){
+ var d = new doh.Deferred();
+
+ doh.robot.typeKeys("alas", 1000, 1600); // s search finds alaska
+
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.is("AK", s2.get("value"), "hidden value");
+ doh.f(isVisible(s2_menu), "no menu");
+ }), 1000);
+
+ return d;
+ }
+ },
+ {
+ name: "single letter searching, no dropdown",
+ timeout: 9000,
+ runTest: function(){
+ var d = new doh.Deferred();
+
- // c search finds CA
++ // c search finds CA
+ doh.robot.typeKeys("c", 500, 500);
+
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.is("CA", s2.get("value"), "hidden value");
+ doh.f(isVisible(s2_menu), "no menu");
+ }), 1000);
+
+ return d;
+ }
+ },
+ {
+ name: "multi letter searching with SPACE, no dropdown",
+ timeout: 9000,
+ runTest: function(){
+ var d = new doh.Deferred();
+
+ // c search fails to find CA due to multi-character searching
+ doh.robot.typeKeys("new m", 500, 1200);
+
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.is("NM", s2.get("value"), "hidden value");
+ doh.f(isVisible(s2_menu), "no menu");
+ }), 1000);
+
+ return d;
+ }
+ },
+ {
+ name: "single letter searching from dropdown",
+ timeout: 9000,
+ setUp: function(){
+ s2.set("value", "CA");
+ },
+ runTest: function(){
+ var d = new doh.Deferred();
+
+ // down arrow to open drop down and search with several a's
+ doh.robot.keyPress(dojo.keys.DOWN_ARROW, 500, {});
+ doh.robot.typeKeys("aaaaaaac", 1000, 1200); // c search fails to find CA due to multi-character searching
+
+ // SPACE to close menu
+ doh.robot.keyPress(dojo.keys.SPACE, 1100, {}); // long delay so that multi-letter searching is deactivated
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.is("AZ", s2.get("value"), "hidden value");
+ }), 1000);
+
+ return d;
+ }
+ },
+ {
+ name: "multiple letter searching no match from dropdown",
+ timeout: 9000,
+ runTest: function(){
+ var d = new doh.Deferred();
+
+ // ENTER to open drop down and search with several a's
+ doh.robot.keyPress(dojo.keys.ENTER, 500, {});
+ doh.robot.typeKeys("aaaaaaaaala", 1000, 1400); // ala suffix fails to find either alabama or alaska
+
+ // ENTER to close menu
+ doh.robot.keyPress(dojo.keys.ENTER, 500, {});
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.is("AR", s2.get("value"), "hidden value");
+ }), 1000);
+
+ return d;
+ }
+ },
+ {
+ name: "multiple letter searching from current from dropdown",
+ timeout: 9000,
+ runTest: function(){
+ var d = new doh.Deferred();
+ var wasVisible;
+
+ // SPACE to open drop down and search with several a's
+ doh.robot.keyPress(dojo.keys.SPACE, 500, {});
+ doh.robot.typeKeys("al", 1000, 1600); // al search finds alabama
+ doh.robot.sequence(function(){
+ wasVisible = isVisible(s2_menu);
+ }, 1000);
+
+ // SPACE to close menu
+ doh.robot.keyPress(dojo.keys.SPACE, 1100, {}); // long delay so that multi-letter searching is deactivated
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.t(wasVisible, "no auto selection");
+ doh.is("AL", s2.get("value"), "hidden value");
+ }), 1000);
+
+ return d;
+ }
+ },
+ {
+ name: "multiple letter searching find next from dropdown",
+ timeout: 9000,
+ runTest: function(){
+ var d = new doh.Deferred();
+ var wasVisible;
+
+ // down arrow to open drop down and search with several a's
+ doh.robot.keyPress(dojo.keys.DOWN_ARROW, 500, {});
+ doh.robot.typeKeys("alas", 1000, 1600); // s search finds alaska
+ doh.robot.sequence(function(){
+ wasVisible = isVisible(s2_menu);
+ }, 1000);
+
+ // ENTER to close menu
+ doh.robot.keyPress(dojo.keys.ENTER, 500, {});
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.t(wasVisible, "no auto selection");
+ doh.is("AK", s2.get("value"), "hidden value");
+ }), 1000);
+
+ return d;
+ }
+ },
+ {
+ name: "toolbar",
+ timeout: 10000,
+ setUp: function(){
+ dojo.byId("beforeToolbar").focus();
+ },
+ runTest: function(){
+ var d = new doh.Deferred();
+
+ // tab into toolbar, then arrow to select
+ doh.robot.keyPress(dojo.keys.TAB, 500, {});
+ doh.robot.keyPress(dojo.keys.RIGHT_ARROW, 500, {});
+
+ // select Alabama
+ doh.robot.keyPress(dojo.keys.DOWN_ARROW, 500, {});
+ doh.robot.keyPress(dojo.keys.DOWN_ARROW, 500, {});
+ doh.robot.keyPress(dojo.keys.ENTER, 500, {});
+
+ // go to right toggle button and click it
+ doh.robot.keyPress(dojo.keys.RIGHT_ARROW, 500, {});
+ doh.robot.keyPress(dojo.keys.SPACE, 500, {});
+
+ // go to left toggle button and click it
+ doh.robot.keyPress(dojo.keys.LEFT_ARROW, 500, {});
+ doh.robot.keyPress(dojo.keys.LEFT_ARROW, 500, {});
+ doh.robot.keyPress(dojo.keys.SPACE, 500, {});
+
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.is("AL", dijit.byId("toolbarSelect").get("value"), "select value");
+ doh.t(dijit.byId("toggle1").get("checked"), "first toggle button");
+ doh.t(dijit.byId("toggle2").get("checked"), "second toggle button");
+ }), 1000);
+
+ return d;
+ }
+ }
+ ]);
+
+ doh.register("display", [
+ {
+ name: "empty drop down",
+ timeout: 6000,
+ runTest: function(){
+ var d = new doh.Deferred();
+
+ // Open widget w/no choices
+ var s6 = dijit.byId("s6");
+ doh.robot.mouseMoveAt(s6.domNode, 0, 1);
+ doh.robot.mouseClick({left:true}, 500);
+ doh.robot.sequence(d.getTestCallback(function(){
+ var menu = dijit.byId("s6_menu"),
+ trs = dojo.query("tr", menu.domNode);
+ doh.is(1, trs.length, "one entry in menu");
+ doh.is("", innerText(dojo.query(".dijitMenuItemLabel", trs[0])), "blank");
+ }), 500);
+
+ return d;
+ }
+ },
+
+ // Currently (for better or worse) the select's width changes based on which option
+ // is selected. Make sure that the drop down is wide enough to show all entries,
+ // even when a narrow entry is selected.
+ {
+ name: "width",
+ timeout: 10000,
+ runTest: function(){
+ var d = new doh.Deferred();
+
+ var s5 = dijit.byId("s5"),
+ s5_menu = dijit.byId("s5_menu"),
+ menuSize;
+
+ // originally s5 is narrow
+ var s5origSize = dojo.position(s5.domNode);
+
+ // Open drop down menu and make sure it's wider than the select
+ // (to display "no move" and "no copy" choices)
+ doh.robot.mouseMoveAt(s5.domNode, 0, 1);
+ doh.robot.mouseClick({left:true}, 500);
+ doh.robot.sequence(d.getTestErrback(function(){
+ menuSize = dojo.position(s5_menu.domNode);
+ doh.t(menuSize.w > s5origSize.w, "menu (" + menuSize.w + ") wider than select (" + s5origSize.w + ")");
+ }), 1000);
+
+ // Select last entry in the drop down. Should make the select wider.
+ doh.robot.mouseMoveAt(function(){
+ return dojo.query("tr", s5_menu.domNode)[4];
+ }, 500, 1);
+ doh.robot.mouseClick({left:true}, 500);
+ doh.robot.sequence(d.getTestCallback(function(){
+ var s5newSize = dojo.position(s5.domNode);
+ doh.t(s5newSize.w > s5origSize.w, "select changed width from " + s5origSize.w + " to " + s5newSize.w);
+ }), 500);
-
++
+ return d;
+ }
+ }
+ ]);
+
+ doh.register("required", [
+ function initial_value(){
+ doh.t(!s3.get('value'), "initial blank value");
+ doh.t(!s3.options[0].value, "blank option");
+ doh.t(s3.get('required'), "required");
+ doh.f(s3.isValid(), "should not be valid");
+ },
+ {
+ name: "select blank option",
+ timeout: 6000,
+ runTest: function(){
+ var d = new doh.Deferred();
+
+ // Open drop down menu
+ doh.robot.mouseMoveAt(s3.domNode, 0, 1);
+ doh.robot.mouseClick({left:true}, 500);
+ // Select blank option
+ doh.robot.mouseMoveAt(function(){
+ return dojo.query("tr", s3_menu.domNode)[0];
+ }, 1000, 1);
+ doh.robot.mouseClick({left:true}, 500);
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.t(isHidden(s3_menu), "drop down menu closes after selection");
+ doh.t(!s3.get('value'), "blank value");
+ doh.t(!s3.options[0].value, "blank option");
+ doh.f(s3.isValid(), "should not be valid");
+ }), 1000);
-
++
+ return d;
+ }
+ },
+ {
+ name: "select option w/ blank present",
+ timeout: 6000,
+ runTest: function(){
+ var d = new doh.Deferred();
+
+ // Open drop down menu
+ doh.robot.mouseMoveAt(s3.domNode, 0, 1);
+ doh.robot.mouseClick({left:true}, 500);
+ // Select 4th option
+ doh.robot.mouseMoveAt(function(){
+ return dojo.query("tr", s3_menu.domNode)[4];
+ }, 1000, 1);
+ doh.robot.mouseClick({left:true}, 500);
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.t(isHidden(s3_menu), "drop down menu closes after selection");
+ doh.is("AZ", s3.get('value'), "Arizona");
+ doh.f(!s3.options[0].value, "no blank option");
+ doh.t(s3.isValid(), "should be valid");
+ }), 1000);
-
++
+ return d;
+ }
+ },
+ {
+ name: "select option w/o blank present",
+ timeout: 6000,
+ runTest: function(){
+ var d = new doh.Deferred();
+
+ // Open drop down menu
+ doh.robot.mouseMoveAt(s3.domNode, 0, 1);
+ doh.robot.mouseClick({left:true}, 500);
+ // Select 4th option
+ doh.robot.mouseMoveAt(function(){
+ return dojo.query("tr", s3_menu.domNode)[4];
+ }, 1000, 1);
+ doh.robot.mouseClick({left:true}, 500);
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.t(isHidden(s3_menu), "drop down menu closes after selection");
+ doh.is("AR", s3.get('value'), "Arkansas");
+ doh.t(s3.isValid(), "should be valid");
+ }), 1000);
-
++
+ return d;
+ }
+ }
+ ]);
+
+ doh.register("form submit", [
+ {
+ name: "first validate",
+ timeout: 5000,
+ runTest: function(){
+ // First validate() should focus s13 and display a tooltip.
+ // (s13 is also required, but has a value from previous tests)
+ var d = new doh.Deferred();
+
+ var handle = dijit.focus.watch("curNode", d.getTestErrback(function(name, oval, nval){
+ if(!nval){ return; } // ignore spurious event on IE
+ console.log("focused on", nval && (nval.id || nval.innerHTML));
+ doh.is("s13", nval.id, "focused on first required select");
+ handle.unwatch();
+ setTimeout(d.getTestCallback(function(){
+ // Tooltip should appear with error message
+ masterTT = dojo.global.dijit._masterTT;
+ doh.t(masterTT && isVisible(masterTT.domNode), "visible");
+ doh.is("s13", masterTT.aroundNode.id, "tooltip for s13");
+ doh.is("This value is required.", dojo.trim(innerText(masterTT.domNode)));
+ }), 750);
+ }));
+
+ // Validate the form programatically
+ dijit.byId("form").validate();
+
+ return d;
+ }
+ },
+ {
+ name: "select non-blank option for s13",
+ timeout: 6000,
+ runTest: function(){
+ var d = new doh.Deferred();
+
+ // Open drop down menu
+ doh.robot.mouseMoveAt(s13.domNode, 0, 1);
+ doh.robot.mouseClick({left:true}, 500);
+ // Select 4th option
+ doh.robot.mouseMoveAt(function(){
+ return dojo.query("tr", s13_menu.domNode)[4];
+ }, 1000, 1);
+ doh.robot.mouseClick({left:true}, 500);
+ doh.robot.sequence(d.getTestCallback(function(){
+ doh.t(isHidden(s13_menu), "drop down menu closes after selection");
+ doh.is("AZ", s13.get('value'), "Arizona");
+ doh.t(s13.isValid(), "should be valid");
+ }), 1000);
+
+ return d;
+ }
+ },
+ {
+ name: "revalidate",
+ timeout: 5000,
+ runTest: function(){
+ // Second validate() should focus s14 and display a tooltip
+ var d = new doh.Deferred();
+
+ var handle = dijit.focus.watch("curNode", d.getTestErrback(function(name, oval, nval){
+ if(!nval){ return; } // ignore spurious event on IE
+ if(nval.id == "submit"){ return; } // ignore focus onto submit button
+ doh.is("s14", nval.id, "focused on second required select");
+ handle.unwatch();
+ setTimeout(d.getTestCallback(function(){
+ // Tooltip should appear with error message
+ masterTT = dojo.global.dijit._masterTT;
+ doh.t(masterTT && isVisible(masterTT.domNode), "visible");
+ doh.is("s14", masterTT.aroundNode.id, "tooltip for s14");
+ doh.is("This value is required.", dojo.trim(innerText(masterTT.domNode)));
+ }), 750);
+ }));
+
+ // Press button to submit the form
+ doh.robot.mouseMoveAt("submit", 0, 1);
+ doh.robot.mouseClick({left:true}, 500);
+
+ return d;
+ }
+ }
+ ]);
+
++ doh.register("validation in dialog", [
++ {
++ name: "show and validate dialog",
++ timeout: 5000,
++ runTest: function(){
++ var d = new doh.Deferred();
++
++ // Show dialog
++ doh.robot.mouseMoveAt("dlg1OpenBtn", 0, 1);
++ doh.robot.mouseClick({left: true}, 500);
++
++ // Clicking validate button should show "required but empty" tooltip on the Select
++ doh.robot.mouseMoveAt("dlg1ValidateBtn", 0, 1);
++ doh.robot.mouseClick({left: true}, 500);
++ doh.robot.sequence(d.getTestCallback(function(){
++ doh.is("dlg1Select", focus.curNode && focus.curNode.id, "focused on select");
++ var masterTT = dijit.Tooltip._masterTT;
++ doh.t(masterTT && isVisible(masterTT.domNode), "tooltip shown");
++ }), 750);
++ return d;
++ }
++ },
++ {
++ name: "hide dialog",
++ timeout: 5000,
++ runTest: function(){
++ // Hiding the dialog should make the validation tooltip disappear
++ var d = new doh.Deferred();
++
++ doh.is("dlg1Select", focus.curNode && focus.curNode.id, "focused on select");
++
++ // Close dialog via ESCAPE key because on FF, robot when run from runTests.html has
++ // problems positioning mouse over the close icon. It strangely positions a few
++ // hundred pixels above the close icon, perhaps due to the iframes and scrolling.
++ doh.robot.keyPress(dojo.keys.ESCAPE, 500, {});
++ doh.robot.sequence(d.getTestCallback(function(){
++ doh.t(isHidden(dojo.byId("dlg1")), "dialog hidden");
++ var masterTT = dijit.Tooltip._masterTT;
++ doh.t(isHidden(masterTT.domNode), "tooltip hidden");
++ }), 750);
++ return d;
++ }
++ },
++ {
++ name: "show again",
++ timeout: 5000,
++ runTest: function(){
++ var d = new doh.Deferred();
++
++ // Show dialog again. Presumably focus is already on the open button, so can
++ // keyboard trigger it.
++ doh.robot.keyPress(dojo.keys.SPACE, 500, {});
++
++ // Opening the dialog focuses the select, which is invalid since it's got no value.
++ // Since this is the second time it's being focused, it should popup the tooltip
++ // immediately.
++ doh.robot.sequence(d.getTestCallback(function(){
++ doh.t(isVisible(dojo.byId("dlg1")), "dialog shown again");
++ doh.is("dlg1Select", focus.curNode.id, "focused on select");
++ var masterTT = dijit.Tooltip._masterTT;
++ doh.t(masterTT && isVisible(masterTT.domNode), "tooltip shown");
++ }), 750);
++ return d;
++ }
++ }
++ ]);
++
+ doh.run();
+ });
+ </script>
+ </head>
+</html>
diff --cc dijit/tests/form/test_Select.html
index 460e54a,0000000..0ae0389
mode 100644,000000..100644
--- a/dijit/tests/form/test_Select.html
+++ b/dijit/tests/form/test_Select.html
@@@ -1,1285 -1,0 +1,1213 @@@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+ <title>dijit/form/Select test</title>
+
+ <style>
+ .ark { text-decoration: underline; }
+ form {
+ margin: 10px 0px;
+ border: solid gray 2px;
+ }
+ .area {
+ border: 1px solid gray;
+ padding: 2px;
+ display: inline;
+ white-space: nowrap;
+ }
+ h3 {
+ margin: 0;
+ }
+ body .customStyled .dijitSelect,
+ body .customStyled .dijitSelect .dijitButtonContents {
+ border-width: 2px;
+ border-color: blue;
+ }
+ body .customStyled .dijitSelect .dijitInputField {
+ padding: 5px;
+ font-family: Arial;
+ font-size: 150%;
+ }
+ .dj_a11y .dijitSelect,
+ .dj_a11y .dijitSelect .dijitButtonContents {
+ border-width: medium !important;
+ }
+ .dj_a11y .dijitSelect,
+ .dj_a11y .customStyled .dijitSelect,
+ .dj_a11y .dijitSelect *,
+ .dj_a11y .customStyled .dijitSelect * {
+ background-color: white !important;
+ background-image: none !important;
+ border-color: black !important;
+ color: black !important;
+ }
+ </style>
+
+ <script type="text/javascript" src="../boilerplate.js"></script>
+
+ <script type="text/javascript">
+ require([
+ "doh/runner",
+ "dojo/_base/array",
+ "dojo/_base/lang",
+ "dojo/parser",
+ "dojo/data/ItemFileReadStore", // for testing old API (remove for 2.0)
+ "dojo/data/ItemFileWriteStore", // for testing old API (remove for 2.0)
+ "dojo/store/Memory",
+ "dojo/store/Observable",
+ "dijit/focus",
+ "dijit/registry",
+ "dijit/form/Select",
+ "dijit/form/Button",
+ "dijit/form/Form",
+ "dijit/Dialog",
+ "dijit/Tooltip",
+ "dijit/tests/helpers",
+
+ // Used by parser
+ "dijit/Toolbar",
+ "dijit/form/ToggleButton",
+
+ "dojo/domReady!"
+ ], function(doh, array, lang, parser, ItemFileReadStore, ItemFileWriteStore, Memory, Observable,
+ focus, registry, Select, Button, Form, Dialog, Tooltip, helpers){
+
+ var numOptions = 0;
+ var numChanges = 0;
+ var addNum = 10;
+
+ // Add test=true to the URL to run unit tests.
+ // Add testPerformance=true to the URL to run performance tests - note SLOW to run
+ var test = /mode=test/i.test(window.location.href),
+ testPerformance = /mode=benchmark/i.test(window.location.href);
+
+ // dojo/data (legacy) data stores
+ var data = {
+ identifier: "value",
+ label: "label",
+ items: [
+ {value: "AL", label: "Alabama"},
+ {value: "AK", label: "Alaska"},
+ {value: "AZ", label: "Arizona"},
+ {value: "AR", label: "Arkansas"},
+ {value: "CA", label: "California"},
+ {value: "CO", label: "Colorado"},
+ {value: "CT", label: "Connecticut"}
+ ]
+ };
+ var data2 = {
+ identifier: "value",
+ label: "label",
+ items: [
+ {value: "DE", label: "Delaware"},
+ {value: "FL", label: "Florida"},
+ {value: "GA", label: "Georgia"},
+ {value: "HI", label: "Hawaii"},
+ {value: "ID", label: "Idaho"},
+ {value: "IL", label: "Illinois"},
+ {value: "IN", label: "Indiana"}
+ ]
+ };
+
+ readStore = new ItemFileReadStore({data: lang.clone(data)});
+ store2 = new ItemFileReadStore({data: lang.clone(data2)});
+ writeStore = new ItemFileWriteStore({data: lang.clone(data)});
+
+ // dojo/store (new API) data stores
+ var vals = [
+ {value: "AL", label: "Alabama"},
+ {value: "AK", label: "Alaska"},
+ {value: "AZ", label: "Arizona"},
+ {value: "AR", label: "Arkansas"},
+ {value: "CA", label: "Calif<a href='javascript:alert()'>ornia</a>"},
+ {value: "CO", label: "Colorado"},
+ {value: "CT", label: "Connecticut"}
+ ];
+ var vals2 = [
+ {value: "DE", label: "Delaware"},
+ {value: "FL", label: "Florida"},
+ {value: "GA", label: "Georgia"},
+ {value: "HI", label: "Hawaii"},
+ {value: "ID", label: "Idaho"},
+ {value: "IL", label: "Illinois"},
+ {value: "IN", label: "Indiana"}
+ ];
+ memoryStore = new Memory({
+ idProperty: "value",
+ data: lang.clone(vals)
+ });
+ memoryStore2 = new Memory({
+ idProperty: "value",
+ data: lang.clone(vals2)
+ });
+ memoryStore3 = new Observable(new Memory({ // updates will reflect to select
+ idProperty: "value",
+ data: lang.clone(vals)
+ }));
+
+ parser.parse();
+
+ s1.on("change", function(val){
+ console.log("First Select Changed to " + val);
+ numChanges++;
+ });
+
+ var programmatic = new Select({
+ options: [
+ { label: 'foo', value: 'foo', selected: true },
+ { label: 'bar', value: 'bar' }
+ ],
+ "aria-label":"programmatic"
+ });
+ programmatic.placeAt('testProgramatic');
+
+ var programmaticDisabled = new Select({
+ disabled: true,
+ options: [
+ { label: 'foo', value: 'foo', selected: true },
+ { label: 'bar', value: 'bar' }
+ ],
+ "aria-label":"programmatic disabled"
+ });
+ programmaticDisabled.placeAt('testProgramatic');
+
+ if(test){
+ doh.register("API", [
+ {
+ name: "test_set",
+ timeout: 5000,
+ runTest: function(t){
+ var d = new doh.Deferred();
+ t.is("VA", form.get("value").s1, "initial value");
+ s1.set("value", s1.getOptions(2)); // set s1 to a valid value via index search
+ t.is("WA", s1.value);
+
- setTimeout(function(){ try{ // allow onChange to fire
-
- // set s1 to non-existing value, which (currently) makes the Select pick
- // the first option in the drop down
- s1.set("value", "UT");
- t.is("TN", s1.value);
-
- setTimeout(function(){ try{ // allow onChange to fire
- t.is(2, numChanges);
-
- // prevent onChange from firing
- s1.set("value", "FL", false);
-
- setTimeout(function(){ try{ // allow onChange to fire if it's wrong
- t.is(2, numChanges);
- d.callback(true);
- }catch(e){ d.errback(e); }}, 0)
- }catch(e){ d.errback(e); }}, 0);
- }catch(e){ d.errback(e); }}, 0);
++ setTimeout(function(){
++ try{ // allow onChange to fire
++
++ // set s1 to non-existing value, which (currently) makes the Select pick
++ // the first option in the drop down
++ s1.set("value", "UT");
++ t.is("TN", s1.value);
++
++ setTimeout(function(){
++ try{ // allow onChange to fire
++ t.is(2, numChanges);
++
++ // prevent onChange from firing
++ s1.set("value", "FL", false);
++
++ setTimeout(function(){
++ try{ // allow onChange to fire if it's wrong
++ t.is(2, numChanges);
++ d.callback(true);
++ }catch(e){
++ d.errback(e);
++ }
++ }, 0)
++ }catch(e){
++ d.errback(e);
++ }
++ }, 0);
++ }catch(e){
++ d.errback(e);
++ }
++ }, 0);
+ return d;
+ }
+ },
+ {
+ name: "test_set by number",
+ timeout: 5000,
+ runTest: function(t){
+ var d = new doh.Deferred();
+ t.is(100, form.get("value").s5, "initial value");
+ s5.set("value", s5.getOptions(2)); // set s5 to a valid value via index search
+ t.is(102, s5.value);
+
- setTimeout(function(){ try{ // allow onChange to fire
-
- // set s5 to non-existing value, which (currently) makes the Select pick
- // the first option in the drop down
- s5.set("value", 200);
- t.is(100, s5.value);
-
- setTimeout(function(){ try{ // allow onChange to fire
- t.is(2, numChanges);
-
- // prevent onChange from firing
- s5.set("value", 103, false);
- t.t(/.*No Move.*/.test(s5.containerNode.innerHTML), s5.containerNode.innerHTML);
-
- setTimeout(function(){ try{ // allow onChange to fire if it's wrong
- t.is(2, numChanges);
- d.callback(true);
- }catch(e){ d.errback(e); }}, 0)
- }catch(e){ d.errback(e); }}, 0);
- }catch(e){ d.errback(e); }}, 0);
++ setTimeout(function(){
++ try{ // allow onChange to fire
++
++ // set s5 to non-existing value, which (currently) makes the Select pick
++ // the first option in the drop down
++ s5.set("value", 200);
++ t.is(100, s5.value);
++
++ setTimeout(function(){
++ try{ // allow onChange to fire
++ t.is(2, numChanges);
++
++ // prevent onChange from firing
++ s5.set("value", 103, false);
++ t.t(/.*No Move.*/.test(s5.containerNode.innerHTML), s5.containerNode.innerHTML);
++
++ setTimeout(function(){
++ try{ // allow onChange to fire if it's wrong
++ t.is(2, numChanges);
++ d.callback(true);
++ }catch(e){
++ d.errback(e);
++ }
++ }, 0)
++ }catch(e){
++ d.errback(e);
++ }
++ }, 0);
++ }catch(e){
++ d.errback(e);
++ }
++ }, 0);
+ return d;
+ }
+ },
+ {
+ name: 'test_disabled',
+ runTest: function(t){
+ t.is(testDisabled.disabled, true);
+ }
+ },
+ {
+ name: 'test_programmatic',
+ runTest: function(t){
+ t.is(programmatic.options.length, 2);
+ t.is(programmaticDisabled.options.length, 2);
+ t.is(programmaticDisabled.disabled, true);
+ }
+ },
+ function test_setOptions(t){
+ programmatic.set('options', data2.items);
+ programmatic.reset();
+ t.is(7, programmatic.getOptions().length);
+ t.is("DE", programmatic.value);
+ },
+ // Test that destroying a Select destroys the internal Menu and MenuItems too
+ {
+ name: "test_destroy",
+ timeout: 5000,
+ runTest: function(t){
+ var oldCnt = registry.length;
-
++
+ s1.destroy();
+
+ var newCnt = registry.length;
+ t.t(newCnt < oldCnt + 3, "should have destroyed many widgets, went from " + oldCnt + " to " + newCnt);
+ }
+ },
+ {
+ name: "aria attributes",
+ timeout: 2000,
- runTest:function(){
++ runTest: function(){
+ var d = new doh.Deferred();
+ select = registry.byId("s2");
-
++
+ doh.is("listbox", select._popupStateNode.getAttribute("role"), "select _popupStateNode role");
+ doh.t(select._popupStateNode.getAttribute("aria-haspopup"), "aria-haspopup on select");
+ doh.f(select._popupStateNode.getAttribute("aria-expanded"), "initially false aria-expanded");
+ doh.f(select._popupStateNode.getAttribute("aria-owns"), "initially missing aria-owns");
+
+ select.openDropDown();
+ setTimeout(d.getTestCallback(function(){
+ doh.t(select._popupStateNode.getAttribute("aria-expanded"), "now aria-expanded should be true");
+ doh.is("s2_menu", select._popupStateNode.getAttribute("aria-owns"), "should aria-own the menu");
+ // Check roles and attributes on the Menu
+ var menu = registry.byId("s2_menu");
+ doh.is("listbox", menu.domNode.getAttribute("role"), "Dlg.domNode should have a role");
+ doh.is("s2", menu.domNode.getAttribute("aria-labelledby"), "aria-labelledby should point back to button");
+ }), 0);
+
+ return d;
+ },
- tearDown:function(){
++ tearDown: function(){
+ select.closeDropDown();
+ }
+ }
+ ]);
+
+ // Test legacy dojo/data API, remove for 2.0
+ doh.register("dojo/data store", [
+ // Tests that when the currently selected item is changed in the data store,
+ // the change is reflected in the Select widget
+ function test_changeSelected(t){
+ t.is("AL", s11.value);
+ t.t(/<span.*>Alabama<\/span>/i.test(s11.containerNode.innerHTML), "expected Alabama but got " + s11.containerNode.innerHTML);
+ s11.set("value", ["AK"]); // test array
+ t.is("AK", s11.value);
+ t.t(/<span.*>Alaska<\/span>/i.test(s11.containerNode.innerHTML), "expected Alaska but got " + s11.containerNode.innerHTML);
+ var d = new doh.Deferred();
+ writeStore.fetchItemByIdentity({
+ identity: "AK",
+ onItem: d.getTestCallback(function(item){
+ writeStore.setValue(item, "label", "North Pole");
+ t.t(/<span.*>North Pole<\/span>/i.test(s11.containerNode.innerHTML), "expected North Pole but got " + s11.containerNode.innerHTML);
+ })
+ });
+ return d;
+ },
+
+ // Test that a delete of the non-selected item will remove that item from the Select's
+ // list of options.
+ function test_deleteNonSelected(t){
+ t.is(7, s11.getOptions().length);
+ t.is("AK", s11.value);
+ var d = new doh.Deferred();
+ writeStore.fetchItemByIdentity({
+ identity: "AZ",
+ onItem: d.getTestCallback(function(item){
+ writeStore.deleteItem(item);
+ t.is(6, s11.getOptions().length);
+ })
+ });
+ return d;
+ },
+
+ // Test that a delete of the selected item will remove that item from the Select's
+ // list of options, and switch to a new selected item
+ function test_deleteSelected(t){
+ t.is(6, s11.getOptions().length);
+ t.is("AK", s11.value);
+ var d = new doh.Deferred();
+ writeStore.fetchItemByIdentity({
+ identity: "AK",
+ onItem: d.getTestCallback(function(item){
+ writeStore.deleteItem(item);
+ t.is("AL", s11.value);
+ t.is(5, s11.getOptions().length);
+ })
+ });
+ return d;
+ },
+
+ // Test that new items added to the data store appear in the select's options
+ function test_newItem(t){
+ t.is(5, s11.getOptions().length);
- t.is("AL", s11.value);
++ t.is("AL", s11.value);
+
+ var d = new doh.Deferred();
+ writeStore.newItem({value: "NY", label: "New York"});
+ setTimeout(d.getTestCallback(function(){
+ t.is(6, s11.getOptions().length);
+ s11.set("value", s11.getOptions({ label: "New York" })); // set via label search
+ t.is("NY", s11.value);
+ }), 100);
+
+ return d;
+ },
+
+ // Test that a Select's store can be changed using deprecated API
+ // TODO remove in 2.0
+ function test_deprecatedSetStore(t){
+ t.is("AL", s12_dep.value);
+ s12_dep.setStore(store2, "FL");
+ t.is("FL", s12_dep.value);
+ s12_dep.setStore(readStore, "CA");
+ t.is("CA", s12_dep.value);
+ s12_dep.setStore(store2);
+ t.is("DE", s12_dep.value);
+ t.is(7, s12_dep.getOptions().length);
+ }
+ ]);
+
+ doh.register("dojo/store", [
+ // Tests that when the currently selected item is changed in the data store,
+ // the change is reflected in the Select widget
+ function test_changeSelected(t){
+ // destroy second widget that shares same store to make sure Observable handles are cleaned up
+ ds11a.destroy();
+ t.is("AL", ds11.value);
+ t.t(/<span.*>Alabama<\/span>/i.test(ds11.containerNode.innerHTML));
+ ds11.set("value", "AK");
+ t.is("AK", ds11.value);
+ t.t(/<span.*>Alaska<\/span>/i.test(ds11.containerNode.innerHTML));
+
+ var item = memoryStore3.get("AK");
+ item.label = "North Pole";
+ memoryStore3.put(item);
+ t.t(/<span.*>North Pole<\/span>/i.test(ds11.containerNode.innerHTML), "select displayed value updated");
+ },
+
+ // Test that a delete of the non-selected item will remove that item from the Select's
+ // list of options.
+ function test_deleteNonSelected(t){
+ t.is(7, ds11.getOptions().length);
+ t.is("AK", ds11.value);
+
+ memoryStore3.remove("AZ");
+ t.is(6, ds11.getOptions().length);
+ },
+
+ // Test that a delete of the selected item will remove that item from the Select's
+ // list of options, and switch to a new selected item
+ function test_deleteSelected(t){
+ t.is(6, ds11.getOptions().length);
+ t.is("AK", ds11.value);
+
+ memoryStore3.remove("AK");
+ t.is("AL", ds11.value);
+ t.is(5, ds11.getOptions().length);
+ },
+
+ // Test that new items added to the data store appear in the select's options
+ function test_newItem(t){
+ t.is(5, ds11.getOptions().length);
- t.is("AL", ds11.value);
++ t.is("AL", ds11.value);
+
+ memoryStore3.add({value: "NY", label: "New York"});
+
+ // TODO: try w/out the timeout
+ var d = new doh.Deferred();
+ setTimeout(d.getTestCallback(function(){
- t.is(6, ds11.getOptions().length);
- ds11.set("value", "NY");
- t.is("NY", ds11.value);
++ t.is(6, ds11.getOptions().length);
++ ds11.set("value", "NY");
++ t.is("NY", ds11.value);
+ }), 100);
+ return d;
+ },
+
+ // Test that a Select's store can be changed using set API
+ function test_setStore(t){
+ t.is(7, ds12.getOptions().length);
+ t.is("AL", ds12.value);
+ ds12.set("store", memoryStore2);
+ ds12.set("value", "FL")
+ t.is(7, ds12.getOptions().length);
+ t.is("FL", ds12.value);
+ ds12.set("store", memoryStore);
+ ds12.set("value", "CA")
+ t.is(7, ds12.getOptions().length);
+ // ensure that escaping prevented child nodes
+ t.is(ds12.containerNode.firstChild.childNodes.length, 1);
+ t.is("CA", ds12.value);
+ ds12.set("store", memoryStore2);
+ t.is(7, ds12.getOptions().length);
+ t.is("DE", ds12.value);
+ },
+
+ // Test that a Select's store can be changed using deprecated API
+ // TODO remove in 2.0
+ function test_deprecatedSetStore(t){
+ t.is(7, ds12_dep.getOptions().length);
+ t.is("AL", ds12_dep.value);
+ ds12_dep.setStore(memoryStore2, "FL");
+ t.is(7, ds12_dep.getOptions().length);
+ t.is("FL", ds12_dep.value);
+ ds12_dep.setStore(memoryStore, "CA");
+ t.is(7, ds12_dep.getOptions().length);
+ // ensure that escaping prevented child nodes
+ t.is(ds12_dep.containerNode.firstChild.childNodes.length, 1);
+ t.is("CA", ds12_dep.value);
+ ds12_dep.setStore(memoryStore2);
+ t.is(7, ds12_dep.getOptions().length);
+ t.is("DE", ds12_dep.value);
+ },
+
+ function test_setQuery(){
+ ds.set("value", "CT");
+ doh.is("Connecticut", ds.get("displayedValue"));
+ ds.set("query", {value: /^A.*/});
+ doh.is("Alabama", ds.get("displayedValue"));
+ }
+ ]);
-
++
+ doh.register("validation", [
+ function required(){
+ var s3 = registry.byId("s3");
+ doh.is("Incomplete", s3.get("state"), "incomplete because required but no value");
+
+ var stateWatch = "no notification";
+ s3.watch("state", function(name, oval, nval){
+ stateWatch = nval;
+ });
+
+ s3.set("required", false);
+ doh.is("", stateWatch, "watch fired after set requried=true");
+
+ s3.set("required", true);
+ doh.is("Incomplete", stateWatch, "watch fired after set required=false''");
+
+ s3.set("value", "AK");
+ doh.is("", stateWatch, "watch fired after set value=AK''");
+ }
+ ]);
- doh.register("validation in dialog1", [
- function showDialog(){
- // Show dialog, returning Deferred that fires when show dialog is finished.
- // Before showing, focus the open button, so that Dialog knows where to return focus to.
- registry.byId("dlg1OpenBtn").focus();
- return registry.byId("dlg1").show();
- },
- function validateDialog(){
- // Clicking validate button should show "required but empty" tooltip on the Select
- var d = new doh.Deferred();
- registry.byId("dlg1ValidateBtn").focus();
- registry.byId("dlg1").validate();
- setTimeout(d.getTestCallback(function(){
- doh.is("dlg1Select", focus.curNode && focus.curNode.id, "focused on select");
- masterTT = Tooltip._masterTT;
- doh.t(masterTT && helpers.isVisible(masterTT.domNode), "tooltip shown");
- }), 300);
- return d;
- },
- {
- name: "hideDialog",
- timeout: 5000,
- runTest: function(){
- // Hiding the dialog should make the validation tooltip disappear
- var d = new doh.Deferred();
- registry.byId("dlg1").hide().then(function(){
- setTimeout(d.getTestCallback(function(){
- doh.t(helpers.isHidden(masterTT.domNode), "tooltip hidden");
- }), 300);
- });
- return d;
- }
- },
- function showDialogAgain(){
- // Show dialog, returning Deferred that fires when show dialog is finished.
- // Before showing, focus the open button, so that Dialog knows where to return focus to.
- registry.byId("dlg1OpenBtn").focus();
- return registry.byId("dlg1").show();
- },
- function tooltipAppears(){
- // Opening the dialog focuses the select, which is invalid since it's got no value.
- // Since this is the second time it's being focused, it should popup the tooltip
- // immediately.
- var d = new doh.Deferred();
- registry.byId("dlg1ValidateBtn").focus();
- registry.byId("dlg1").validate();
- setTimeout(d.getTestCallback(function(){
- doh.is("dlg1Select", focus.curNode && focus.curNode.id, "focused on select");
- masterTT = Tooltip._masterTT;
- doh.t(masterTT && helpers.isVisible(masterTT.domNode), "tooltip shown");
- }), 300);
- return d;
- },
- function destroyingSelectHidesTooltip(){
- var d = new doh.Deferred();
- registry.byId("dlg1Select").destroy();
- setTimeout(d.getTestCallback(function(){
- doh.t(helpers.isHidden(masterTT.domNode), "tooltip hidden");
- }), 300);
- return d;
- },
- function tearDown(){
- return registry.byId("dlg1").hide();
- }
- ]);
-
- doh.register("validation in dialog2", [
- function showDialog(){
- // Show dialog, returning Deferred that fires when show dialog is finished.
- // Before showing, focus the open button, so that Dialog knows where to return focus to.
- registry.byId("dlg2OpenBtn").focus();
- return registry.byId("dlg2").show();
- },
- function validateDialog(){
- // Clicking validate button should show "required but empty" tooltip on the Select
- var d = new doh.Deferred();
- registry.byId("dlg2ValidateBtn").focus();
- registry.byId("dlg2").validate();
- setTimeout(d.getTestCallback(function(){
- doh.is("dlg2Select1", focus.curNode && focus.curNode.id, "focused on select");
- masterTT = Tooltip._masterTT;
- doh.t(masterTT && helpers.isVisible(masterTT.domNode), "tooltip shown");
- }), 300);
- return d;
- },
- function hideDialog(){
- // Hiding the dialog should make the validation tooltip disappear
- var d = new doh.Deferred();
- registry.byId("dlg2").hide().then(function(){
- setTimeout(d.getTestCallback(function(){
- doh.t(helpers.isHidden(masterTT.domNode), "tooltip hidden");
- }), 300);
- });
- return d;
- }
- ]);
+ }
+
- if(testPerformance){
++ if(testPerformance){
+ doh.register("performance", [
+ {
+ name: "test_performance_single",
+ testType: "perf",
+ trialDuration: 100,
+ trialIterations: 100,
+ trialDelay: 100,
+ runTest: function(t){
+ var opt = {value: "Test", label: "Test Option"};
+ s6.addOption(opt);
+ s6.removeOption(opt);
+ }
+ },
+ {
+ name: "test_performance_separate",
+ testType: "perf",
+ trialDuration: 100,
+ trialIterations: 100,
+ trialDelay: 100,
+ setUp: function(t){
+ var opts = (t.options = []);
+ for(var i = 0; i < addNum; i++){
+ opts.push({value: i + "", label: "Option " + (i + 1)});
+ }
+ },
+ runTest: function(t){
+ array.forEach(t.options, function(opt){
+ s6.addOption(opt);
+ });
+ array.forEach(t.options, function(opt){
+ s6.removeOption(opt);
+ });
+ },
+ tearDown: function(t){
+ delete t.options;
+ }
+ },
+ {
+ name: "test_performance_batch",
+ testType: "perf",
+ trialDuration: 100,
+ trialIterations: 100,
+ trialDelay: 100,
+ setUp: function(t){
+ var opts = (t.options = []);
+ for(var i = 0; i < addNum; i++){
+ opts.push({value: i + "", label: "Option " + (i + 1)});
+ }
+ },
+ runTest: function(t){
+ s6.addOption(t.options);
+ s6.removeOption(t.options);
+ },
+ tearDown: function(t){
+ delete t.options;
+ }
+ }
+ ]);
+ }
+
+ if(test || testPerformance){
+ doh.run();
+ }
+ });
+ </script>
+ </head>
+ <body role="main">
+ <h1 class="testTitle">Test: dijit/form/Select</h1>
+
+ <p>
+ Note: load <a href="test_Select.html?mode=test">test_Select.html?mode=test</a> to run unit tests, or
+ <a href="test_Select.html?mode=benchmark">test_Select.html?mode=benchmark</a> to run performance tests.
+ </p>
+
+ <script type="dojo/require">
+ registry: "dijit/registry"
+ </script>
+
+ <form method="get" id="htmlForm" action="get">
+ <h2>HTML select for comparison</h2>
+ <label for="htmlSelect">Four options:</label>
+ <select id="htmlSelect" title="native title">
+ <option value="one">one</option>
+ <option value="two">two</option>
+ <option value="three">three</option>
+ <option value="four">four</option>
+ </select>
+ <label for="htmlSelect2">Empty:</label>
+ <select id="htmlSelect2">
+ </select>
+ </form>
+ <form id="form" data-dojo-id="form" data-dojo-type="dijit/form/Form" method="get"
+ onSubmit="return this.validate();">
+ <h2>dijit/form/Select form</h2>
+ <h4 class="testSubtitle">Setting Defaults</h4>
+ <label for="s1">Test One: </label>
+ <select id="s1" data-dojo-id="s1" data-dojo-type="dijit/form/Select" data-dojo-props='name:"s1" '>
+ <option value="TN">Tennessee</option>
+ <option value="VA" selected="selected">Virginia</option>
+ <option value="WA">Washington</option>
+ <option value="FL">Florida</option>
+ <option value="CA">California</option>
+ </select>
+ <button id="s1button" data-dojo-type="dijit/form/Button" data-dojo-props='type:"button", onClick:function(){ console.log(s1.get("displayedValue")); }'>
+ Get Displayed Value
+ </button>
+ <label for="s2">Test Two: </label>
+ <select id="s2" data-dojo-id="s2" data-dojo-type="dijit/form/Select" data-dojo-props='name:"s2", value:"CA", title: "widget title" '>
+ <option value="AL">Alabama</option>
+ <option value="AK">Alaska</option>
+ <option value="AZ">Arizona</option>
+ <option value="AR">Arkansas</option>
+ <option value="CA">California</option>
+ <option value="NH">New Hampshire</option>
+ <option value="NJ">New Jersey</option>
+ <option value="NM">New Mexico</option>
+ <option value="NY">New York</option>
+ </select>
+ <label for="s3">Test Three (required): </label>
+ <select id="s3" data-dojo-id="s3" data-dojo-type="dijit/form/Select" data-dojo-props='name:"s3", style:{width:"150px"},
+ required:true,
+ onChange: function(){
+ if(!this.options[0].value){
+ this.removeOption(0);
+ }
+ }
+ '>
+ <option> </option>
+ <option value="AL">Alabama</option>
+ <option value="AK">Alaska</option>
+ <option></option>
+ <option value="AZ">Arizona</option>
+ <option value="AR">Arkansas</option>
+ <option></option>
+ <option value="CA">California</option>
+ </select>
+ <hr>
+ <h4 class="testSubtitle">Rich Text (Need to use divs and spans - since browsers hack selects to pieces)</h4>
+ <label id="ls4">Rich text One:</label>
+ <span id="s4" data-dojo-id="s4" data-dojo-type="dijit/form/Select" data-dojo-props='name:"s4", value:"AK", "aria-labelledby":"ls4" '>
+ <span data-dojo-value="AL"><b>Alabama</b></span>
+ <span data-dojo-value="AK"><span style="color:red;">A</span><span style="color:orange;">l</span><span style="color:yellow;">a</span><span style="color:green;">s</span><span style="color:blue;">k</span><span style="color:purple;">a</span></span>
+ <span data-dojo-value="AZ"><i>Arizona</i></span>
+ <span data-dojo-value="AR"><span class="ark">Arkansas</span></span>
+ <span data-dojo-value="CA"><span style="font-size:25%">C</span><span style="font-size:50%">a</span><span style="font-size:75%">l</span><span style="font-size:90%">i</span><span style="font-size:100%">f</span><span style="font-size:125%">o</span><span style="font-size:133%">r</span><span style="font-size:150%">n</span><span style="font-size:175%">i</span><span style="font-size:200%">a</span></span>
+ <button value="NM" disabled="disabled">New<br> Mexico</button>
+ </span>
+ <button data-dojo-type="dijit/form/Button" data-dojo-props='type:"button", onClick:function(){ s4.set("disabled", !s4.get("disabled")); }'>
+ Toggle Disabled
+ </button>
+ <label for="s5">Rich text two: </label>
+ <span id="s5" data-dojo-id="s5" data-dojo-type="dijit/form/Select" data-dojo-props='name:"s5", value:"move"'>
+ <span data-dojo-value=100><img style="vertical-align: middle;margin-top: 1px;margin-bottom:1px;" src="../../../dijit/themes/tundra/images/dndCopy.png" alt="copy" /> Copy</span>
+ <span data-dojo-value=101><img style="vertical-align: middle;margin-top: 1px;margin-bottom:1px;" src="../../../dijit/themes/tundra/images/dndMove.png" alt="move" /> Move</span>
+ <span data-dojo-value=102><img style="vertical-align: middle;margin-top: 1px;margin-bottom:1px;" src="../../../dijit/themes/tundra/images/dndNoCopy.png" alt="no copy" /> No Copy</span>
+ <span data-dojo-value=103><img style="vertical-align: middle;margin-top: 1px;margin-bottom:1px;" src="../../../dijit/themes/tundra/images/dndNoMove.png" alt="no move" /> No Move</span>
+ <span data-dojo-value=104><img style="vertical-align: middle;margin-top: 1px;margin-bottom:1px;" src="../../../dijit/themes/tundra/images/dndNoMove.png" alt="very long menu" /> Very Long Menu Entry</span>
+ </span>
+ <hr>
+ <h4 class="testSubtitle"><label for="s6">Initially Empty</label></h4>
+ <select id="s6" data-dojo-id="s6" data-dojo-type="dijit/form/Select" data-dojo-props='name:"s6", maxHeight:100 '>
+ </select>
+ <button data-dojo-type="dijit/form/Button" data-dojo-props='type:"button", onClick:function(){ numOptions++; s6.addOption({value: numOptions + "", label: "Option " + numOptions}); }'>
+ Add Option
+ </button>
+ <button data-dojo-type="dijit/form/Button" data-dojo-props='type:"button", onClick:function(){ s6.removeOption(0); }'>
+ Remove Top Option
+ </button>
+ <button data-dojo-type="dijit/form/Button" data-dojo-props='type:"button", onClick:function(){ s6.set("disabled", !s6.get("disabled")); }'>
+ Toggle Disabled
+ </button>
+ <hr>
+ <h4 class="testSubtitle"><label for="s7">Single Item</label></h4>
+ <select id="s7" data-dojo-id="s7" data-dojo-type="dijit/form/Select" data-dojo-props='name:"s7" '>
+ <option value="NY">New York</option>
+ </select>
+ <hr>
+ <h4 class="testSubtitle">Long lists</h4>
+ <label for="s8a">maxHeight=200:</label>
+ <select id="s8a" data-dojo-id="s8a" data-dojo-type="dijit/form/Select" data-dojo-props='name:"s8a", maxHeight:200 '>
+ <option value="AL">Alabama</option>
+ <option value="AK">Alaska</option>
+ <option value="AZ">Arizona</option>
+ <option value="AR">Arkansas</option>
+ <option value="CA">California</option>
+ <option value="CO">Colorado</option>
+ <option value="CT">Connecticut</option>
+ <option value="DE">Delaware</option>
+ <option value="DC">Dist of Columbia</option>
+ <option value="FL">Florida</option>
+ <option value="GA">Georgia</option>
+ <option value="HI">Hawaii</option>
+ <option value="ID">Idaho</option>
+ <option value="IL">Illinois</option>
+ <option value="IN">Indiana</option>
+ <option value="IA">Iowa</option>
+ <option value="KS">Kansas</option>
+ <option value="KY">Kentucky</option>
+ <option value="LA">Louisiana</option>
+ <option value="ME">Maine</option>
+ <option value="MD">Maryland</option>
+ <option value="MA">Massachusetts</option>
+ <option value="MI">Michigan</option>
+ <option value="MN">Minnesota</option>
+ <option value="MS">Mississippi</option>
+ <option value="MO">Missouri</option>
+ <option value="MT">Montana</option>
+ <option value="NE">Nebraska</option>
+ <option value="NV">Nevada</option>
+ <option value="NH">New Hampshire</option>
+ <option value="NJ">New Jersey</option>
+ <option value="NM">New Mexico</option>
+ <option value="NY">New York</option>
+ <option value="NC">North Carolina</option>
+ <option value="ND">North Dakota</option>
+ <option value="OH">Ohio</option>
+ <option value="OK">Oklahoma</option>
+ <option value="OR">Oregon</option>
+ <option value="PA">Pennsylvania</option>
+ <option value="RI">Rhode Island</option>
+ <option value="SC">South Carolina</option>
+ <option value="SD">South Dakota</option>
+ <option value="TN">Tennessee</option>
+ <option value="TX">Texas</option>
+ <option value="UT">Utah</option>
+ <option value="VT">Vermont</option>
+ <option value="VA">Virginia</option>
+ <option value="WA">Washington</option>
+ <option value="WV">West Virginia</option>
+ <option value="WI">Wisconsin</option>
+ <option value="WY">Wyoming</option>
+ </select>
+ <label for="s8b">no maxHeight:</label>
+ <select id="s8b" data-dojo-id="s8b" data-dojo-type="dijit/form/Select" data-dojo-props='name:"s8b" '>
+ <option value="AL">Alabama</option>
+ <option value="AK">Alaska</option>
+ <option value="AZ">Arizona</option>
+ <option value="AR">Arkansas</option>
+ <option value="CA">California</option>
+ <option value="CO">Colorado</option>
+ <option value="CT">Connecticut</option>
+ <option value="DE">Delaware</option>
+ <option value="DC">Dist of Columbia</option>
+ <option value="FL">Florida</option>
+ <option value="GA">Georgia</option>
+ <option value="HI">Hawaii</option>
+ <option value="ID">Idaho</option>
+ <option value="IL">Illinois</option>
+ <option value="IN">Indiana</option>
+ <option value="IA">Iowa</option>
+ <option value="KS">Kansas</option>
+ <option value="KY">Kentucky</option>
+ <option value="LA">Louisiana</option>
+ <option value="ME">Maine</option>
+ <option value="MD">Maryland</option>
+ <option value="MA">Massachusetts</option>
+ <option value="MI">Michigan</option>
+ <option value="MN">Minnesota</option>
+ <option value="MS">Mississippi</option>
+ <option value="MO">Missouri</option>
+ <option value="MT">Montana</option>
+ <option value="NE">Nebraska</option>
+ <option value="NV">Nevada</option>
+ <option value="NH">New Hampshire</option>
+ <option value="NJ">New Jersey</option>
+ <option value="NM">New Mexico</option>
+ <option value="NY">New York</option>
+ <option value="NC">North Carolina</option>
+ <option value="ND">North Dakota</option>
+ <option value="OH">Ohio</option>
+ <option value="OK">Oklahoma</option>
+ <option value="OR">Oregon</option>
+ <option value="PA">Pennsylvania</option>
+ <option value="RI">Rhode Island</option>
+ <option value="SC">South Carolina</option>
+ <option value="SD">South Dakota</option>
+ <option value="TN">Tennessee</option>
+ <option value="TX">Texas</option>
+ <option value="UT">Utah</option>
+ <option value="VT">Vermont</option>
+ <option value="VA">Virginia</option>
+ <option value="WA">Washington</option>
+ <option value="WV">West Virginia</option>
+ <option value="WI">Wisconsin</option>
+ <option value="WY">Wyoming</option>
+ </select>
+
+ <hr>
+ <h4 class="testSubtitle">dojo/data store (legacy API) based</h4>
+ <label for="s9">Example 1</label>
+ <select id="s9" data-dojo-id="s9" data-dojo-type="dijit/form/Select" data-dojo-props='name:"s9", store:readStore, value:"CT" '>
+ </select>
+ <label for="s10">Example 2</label>
+ <select id="s10" data-dojo-id="s10" data-dojo-type="dijit/form/Select" data-dojo-props='name:"s10", store:readStore '>
+ </select>
+ <label for="s11">Example 3</label>
+ <select id="s11" data-dojo-id="s11" data-dojo-type="dijit/form/Select" data-dojo-props='name:"s11", store:writeStore '>
+ </select>
+ <label for="s12_dep">Example 4 Legacy</label>
+ <select id="s12_dep" data-dojo-id="s12_dep" data-dojo-type="dijit/form/Select" data-dojo-props='name:"s12_dep", store:readStore '>
+ </select>
+
+ <hr>
+ <h4 class="testSubtitle">dojo/store based</h4>
+ <label for="ds9">Example 1</label>
+ <select id="ds9" data-dojo-id="ds" data-dojo-type="dijit/form/Select"
+ data-dojo-props='name:"ds", store:memoryStore, value:"CT", labelAttr:"label" '>
+ </select>
+ <label for="ds10">Example 2</label>
+ <select id="ds10" data-dojo-id="ds10"
+ data-dojo-type="dijit/form/Select" data-dojo-props='name:"ds10", store:memoryStore, labelAttr:"label" '>
+ </select>
+ <label for="ds11">Example 3 (Observable)</label>
+ <select id="ds11" data-dojo-id="ds11" data-dojo-type="dijit/form/Select"
+ data-dojo-props='name:"ds11", store:memoryStore3, labelType: "text", labelAttr:"label" '>
+ </select>
+ <label for="ds11a">Example 3a (Observable, same store as 3)</label>
+ <select id="ds11a" data-dojo-id="ds11a" data-dojo-type="dijit/form/Select"
+ data-dojo-props='name:"ds11a", store:memoryStore3, labelAttr:"label" '>
+ </select>
+ <label for="ds12">Example 4</label>
+ <select id="ds12" data-dojo-id="ds12" data-dojo-type="dijit/form/Select"
+ data-dojo-props='name:"ds12", labelType: "text", store:memoryStore, labelAttr:"label" '>
+ </select>
+ <label for="ds12_dep">Example 5 Legacy</label>
+ <select id="ds12_dep" data-dojo-id="ds12_dep" data-dojo-type="dijit/form/Select"
+ data-dojo-props='name:"ds12_dep", labelType: "text", store:memoryStore, labelAttr:"label" '>
+ </select>
+
+ <hr>
+ <h4 class="testSubtitle">Inlined with text (all IE modes except for IE8 Standards)</h4>
+ <label for="txtPrompt">Text Prompt:</label>
+ <select id="txtPrompt" data-dojo-type="dijit/form/Select" >
+ <option value="SEL" selected="selected">Select</option>
+ <option value="OTHER">Other</option>
+ </select>
+
+ <hr>
+ <h4 class="testSubtitle">More required but blank selects</h4>
+ <label for="s13">required s13:</label>
+ <select id="s13" data-dojo-id="s13" data-dojo-type="dijit/form/Select"
+ name="s13" style="width: 150px;" required="true">
+ <option> </option>
+ <option value="AL">Alabama</option>
+ <option value="AK">Alaska</option>
+ <option></option>
+ <option value="AZ">Arizona</option>
+ <option value="AR">Arkansas</option>
+ <option></option>
+ <option value="CA">California</option>
+ </select>
+ <label for="s14">required s14:</label>
+ <select id="s14" data-dojo-id="s14" data-dojo-type="dijit/form/Select"
+ name="s14" style="width: 150px;" required="true">
+ <option> </option>
+ <option value="AL">Alabama</option>
+ <option value="AK">Alaska</option>
+ <option></option>
+ <option value="AZ">Arizona</option>
+ <option value="AR">Arkansas</option>
+ <option></option>
+ <option value="CA">California</option>
+ </select>
+
+ <hr>
+ <button data-dojo-type="dijit/form/Button" data-dojo-props='type:"button", onClick:function(){ console.dir(form.getValues()); }'>
+ Get Values
+ </button>
+ <button data-dojo-type="dijit/form/Button" data-dojo-props='id:"submit", type:"submit"'>Submit</button>
+ </form>
+
+ <h4 class="testSubtitle">Disabled</h4>
+ <label for="testDisabled">Disabled:</label>
+ <select id='testDisabled' data-dojo-id='testDisabled' data-dojo-type="dijit/form/Select" data-dojo-props='disabled:true, name:"testDisabled" '>
+ <option value="foo">foo</option>
+ <option value="bar">bar</option>
+ </select>
+ <hr>
+ <h4 class="testSubtitle">Programmatic and other tests</h4>
+ <div id="testProgramatic"></div>
+
+ <!-- testing that tooltip disappears when dialog is closed -->
+ <div dojoType="dijit/Dialog" id="dlg1">
+ <div dojoType="dijit/form/Select" id="dlg1Select"required="true"></div>
+ <div dojoType="dijit/form/Button" id="dlg1ValidateBtn">
+ <script type=dojo/method data-dojo-event="onClick">
+ var dlg1 = registry.byId("dlg1");
+ if(dlg1.validate()){
+ dlg1.hide();
+ }
+ </script>
+ validate and close
+ </div>
+ </div>
+ <button dojoType="dijit/form/Button" id="dlg1OpenBtn"
+ onclick="registry.byId('dlg1').show();">show one select dialog</button>
+
+ <div dojoType="dijit/Dialog" id="dlg2">
+ <div dojoType="dijit/form/Select" id="dlg2Select1"required="true"></div>
+ <div dojoType="dijit/form/Select" id="dlg2Select2"required="true"></div>
+ <div dojoType="dijit/form/Button" id="dlg2ValidateBtn">
+ <script type=dojo/method data-dojo-event="onClick">
+ var dlg2 = registry.byId("dlg2");
+ if(dlg2.validate()){
+ dlg2.hide();
+ }
+ </script>
+ validate and close
+ </div>
+ </div>
+ <button dojoType="dijit/form/Button" id="dlg2OpenBtn"
+ onclick="registry.byId('dlg2').show();">show two select dialog</button>
+ <br>
+ <fieldset class="area">
+ <legend>Rendering tests</legend>
+ <fieldset class="area">
+ <legend>unstyled widget</legend>
+ <fieldset class="area">
+ <legend id="lr1">normal</legend>
+ <select aria-labelledby="lr1" id="r1" data-dojo-type="dijit/form/Select">
+ <option data-dojo-value="1">1</option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area">
+ <legend id="lr2">disabled</legend>
+ <select aria-labelledby="lr2" id="r2" data-dojo-type="dijit/form/Select" disabled>
+ <option data-dojo-value="1">1</option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area">
+ <legend id="lr3">error</legend>
+ <select id="r3" aria-labelledby="lr3" data-dojo-type="dijit/form/Select" class="dijitError dijitSelectError dijitValidationTextBoxError" data-dojo-props="required:true">
+ <option data-dojo-value=""></option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area">
+ <legend id="lr4">hover</legend>
+ <select id="r4" aria-labelledby="lr4" data-dojo-type="dijit/form/Select" class="dijitHover dijitSelectHover">
+ <option data-dojo-value="1">1</option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area">
+ <legend id="lr5">focused</legend>
+ <select id="r5" aria-labelledby="lr5" data-dojo-type="dijit/form/Select" class="dijitFocused dijitSelectFocused">
+ <option data-dojo-value="1">1</option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area">
+ <legend id="lr6">RTL</legend>
+ <select id="r6" aria-labelledby="lr6" data-dojo-type="dijit/form/Select" dir="rtl">
+ <option data-dojo-value="1">1</option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area">
+ <legend id="lr7">RTL error</legend>
+ <select id="r7" aria-labelledby="lr7" data-dojo-type="dijit/form/Select" class="dijitError dijitSelectError dijitValidationTextBoxError" dir="rtl" data-dojo-props="required:true">
+ <option data-dojo-value=""></option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area">
+ <legend id="lr8">styled option</legend>
+ <span id="r8" aria-labelledby="lr8" data-dojo-type="dijit/form/Select">
+ <span data-dojo-value="1"><span style="font-size:200%;color:pink;">Large</span></span>
+ <span data-dojo-value="2">2</span>
+ </span>
+ </fieldset>
+ </fieldset>
+ <br>
+ <fieldset class="area">
+ <legend>width:60px;font-family:Arial;font-size:150%;border:2px blue;padding:5px;color:red</legend>
+ <fieldset class="area customStyled">
+ <legend id="lcs1">normal</legend>
+ <select id="cs1" aria-labelledby="lcs1" data-dojo-type="dijit/form/Select" style="color:red;width:60px;">
+ <option data-dojo-value="1">1</option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area customStyled">
+ <legend id="lcs2">disabled</legend>
+ <select id="cs2" aria-labelledby="lcs2" data-dojo-type="dijit/form/Select" style="color:red;width:60px;" disabled>
+ <option data-dojo-value="1">1</option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area customStyled">
+ <legend id="lcs3">error</legend>
+ <select id="cs3" aria-labelledby="lcs3" data-dojo-type="dijit/form/Select" class="dijitError dijitSelectError dijitValidationTextBoxError" style="color:red;width:60px;" data-dojo-props="required:true">
+ <option data-dojo-value=""></option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area customStyled">
+ <legend id="lcs4">hover</legend>
+ <select id="cs4" aria-labelledby="lcs4" data-dojo-type="dijit/form/Select" class="dijitHover dijitSelectHover" style="color:red;width:60px;">
+ <option data-dojo-value="1">1</option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area customStyled">
+ <legend id="lcs5">focused</legend>
+ <select id="cs5" data-dojo-type="dijit/form/Select" aria-labelledby="lcs5" class="dijitFocused dijitSelectFocused" style="color:red;width:60px;">
+ <option data-dojo-value="1">1</option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area customStyled">
+ <legend id="lcs6">RTL</legend>
+ <select id="cs6" aria-labelledby="lcs6" data-dojo-type="dijit/form/Select" dir="rtl" style="color:red;width:60px;">
+ <option data-dojo-value="1">1</option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area customStyled">
+ <legend id="lcs7">RTL error</legend>
+ <select id="cs7" aria-labelledby="lcs7" data-dojo-type="dijit/form/Select" class="dijitError dijitSelectError dijitValidationTextBoxError" style="color:red;width:60px;" dir="rtl" data-dojo-props="required:true">
+ <option data-dojo-value=""></option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area customStyled">
+ <legend id="lcs8">styled option</legend>
+ <span id="cs8" aria-labelledby="lcs8" data-dojo-type="dijit/form/Select" style="color:red;width:120px;">
+ <span data-dojo-value="1"><span style="font-size:200%;color:pink;">Large</span></span>
+ <span data-dojo-value="2">2</span>
+ </span>
+ </fieldset>
+ </fieldset>
+ <br>
+ <fieldset class="area dj_a11y">
+ <legend>a11y, unstyled</legend>
+ <fieldset class="area">
+ <legend id="la1">normal</legend>
+ <select id="a1" aria-labelledby="la1" data-dojo-type="dijit/form/Select">
+ <option data-dojo-value="1">1</option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area">
+ <legend id="la2">disabled</legend>
+ <select id="a2" aria-labelledby="la2" data-dojo-type="dijit/form/Select" disabled>
+ <option data-dojo-value="1">1</option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area">
+ <legend id="la3">error</legend>
+ <select id="a3" aria-labelledby="la3" data-dojo-type="dijit/form/Select" class="dijitError dijitSelectError dijitValidationTextBoxError" data-dojo-props="required:true">
+ <option data-dojo-value=""></option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area">
+ <legend id="la4">hover</legend>
+ <select id="a4" aria-labelledby="la4" data-dojo-type="dijit/form/Select" class="dijitHover dijitSelectHover">
+ <option data-dojo-value="1">1</option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area">
+ <legend id="la5">focused</legend>
+ <select id="a5" aria-labelledby="la5" data-dojo-type="dijit/form/Select" class="dijitFocused dijitSelectFocused">
+ <option data-dojo-value="1">1</option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area">
+ <legend id="la6">RTL</legend>
+ <select id="a6" aria-labelledby="la6" data-dojo-type="dijit/form/Select" dir="rtl">
+ <option data-dojo-value="1">1</option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area">
+ <legend id="la7">RTL error</legend>
+ <select id="a7" aria-labelledby="la7" data-dojo-type="dijit/form/Select" class="dijitError dijitSelectError dijitValidationTextBoxError" dir="rtl" data-dojo-props="required:true">
+ <option data-dojo-value=""></option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area">
+ <legend id="la8">styled option</legend>
+ <span id="a8" aria-labelledby="la8" data-dojo-type="dijit/form/Select">
+ <span data-dojo-value="1"><span style="font-size:200%;color:pink;">Large</span></span>
+ <span data-dojo-value="2">2</span>
+ </span>
+ </fieldset>
+ </fieldset>
+ <br>
+ <fieldset class="area dj_a11y">
+ <legend>a11y styled</legend>
+ <fieldset class="area customStyled">
+ <legend id="lacs1">normal</legend>
+ <select id="acs1" aria-labelledby="lacs1" data-dojo-type="dijit/form/Select" style="color:red;width:60px;">
+ <option data-dojo-value="1">1</option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area customStyled">
+ <legend id="lacs2">disabled</legend>
+ <select id="acs2" aria-labelledby="lacs2" data-dojo-type="dijit/form/Select" style="color:red;width:60px;" disabled>
+ <option data-dojo-value="1">1</option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area customStyled">
+ <legend id="lacs3">error</legend>
+ <select id="acs3" aria-labelledby="lacs3" data-dojo-type="dijit/form/Select" class="dijitError dijitSelectError dijitValidationTextBoxError" style="color:red;width:60px;" data-dojo-props="required:true">
+ <option data-dojo-value=""></option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area customStyled">
+ <legend id="lacs4">hover</legend>
+ <select id="acs4" aria-labelledby="lacs5" data-dojo-type="dijit/form/Select" class="dijitHover dijitSelectHover" style="color:red;width:60px;">
+ <option data-dojo-value="1">1</option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area customStyled">
+ <legend id="lacs5">focused</legend>
+ <select id="acs5" aria-labelledby="lacs5" data-dojo-type="dijit/form/Select" class="dijitFocused dijitSelectFocused" style="color:red;width:60px;">
+ <option data-dojo-value="1">1</option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area customStyled">
+ <legend id="lacs6">RTL</legend>
+ <select id="acs6" aria-labelledby="lacs6" data-dojo-type="dijit/form/Select" dir="rtl" style="color:red;width:60px;">
+ <option data-dojo-value="1">1</option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area customStyled">
+ <legend id="lacs7">RTL error</legend>
+ <select id="acs7" aria-labelledby="lacs7" data-dojo-type="dijit/form/Select" class="dijitError dijitSelectError dijitValidationTextBoxError" style="color:red;width:60px;" dir="rtl" data-dojo-props="required:true">
+ <option data-dojo-value=""></option>
+ <option data-dojo-value="2">2</option>
+ </select>
+ </fieldset>
+ <fieldset class="area customStyled">
+ <legend id="lacs8">styled option</legend>
+ <span id="acs8" aria-labelledby="lacs8" data-dojo-type="dijit/form/Select" style="color:red;width:120px;">
+ <span data-dojo-value="1"><span style="font-size:200%;color:pink;">Large</span></span>
+ <span data-dojo-value="2">2</span>
+ </span>
+ </fieldset>
+ </fieldset>
+ </fieldset>
+
+ <h4 class="testSubtitle">Toolbar</h4>
+ <input id="beforeToolbar">
+ <div data-dojo-type="dijit/Toolbar">
+ <button type="button" data-dojo-type="dijit/form/ToggleButton" id="toggle1">
+ toggle 1
+ </button>
+ <select id="toolbarSelect" data-dojo-id="s14" data-dojo-type="dijit/form/Select"
+ name="s14" style="width: 150px;" required="true">
+ <option> </option>
+ <option value="AL">Alabama</option>
+ <option value="AK">Alaska</option>
+ <option></option>
+ <option value="AZ">Arizona</option>
+ <option value="AR">Arkansas</option>
+ <option></option>
+ <option value="CA">California</option>
+ </select>
+ <button type="button" data-dojo-type="dijit/form/ToggleButton" id="toggle2">
+ toggle 2
+ </button>
+ </div>
+ </body>
+</html>
diff --cc dojo/tests/dom-attr.html
index 0000000,cf127b3..cf127b3
mode 000000,100644..100644
--- a/dojo/tests/dom-attr.html
+++ b/dojo/tests/dom-attr.html
diff --cc dojo/tests/dom-attr.js
index 0000000,c1fbd09..c1fbd09
mode 000000,100644..100644
--- a/dojo/tests/dom-attr.js
+++ b/dojo/tests/dom-attr.js
diff --cc dojox/app/ViewBase.js
index ca6716f,0000000..d76482f
mode 100644,000000..100644
--- a/dojox/app/ViewBase.js
+++ b/dojox/app/ViewBase.js
@@@ -1,269 -1,0 +1,270 @@@
+define(["require", "dojo/when", "dojo/on", "dojo/dom-attr", "dojo/dom-style", "dojo/_base/declare", "dojo/_base/lang",
+ "dojo/Deferred", "./utils/model", "./utils/constraints"],
+ function(require, when, on, domAttr, domStyle, declare, lang, Deferred, model, constraints){
+ return declare("dojox.app.ViewBase", null, {
+ // summary:
+ // View base class with model & controller capabilities. Subclass must implement rendering capabilities.
+ constructor: function(params){
+ // summary:
+ // Constructs a ViewBase instance.
+ // params:
+ // view parameters, include:
+ //
+ // - app: the app
+ // - id: view id
+ // - name: view name
+ // - parent: parent view
+ // - controller: view controller module identifier
+ // - children: children views
+ this.id = "";
+ this.name = "";
+ this.children = {};
+ this.selectedChildren = {};
+ this.loadedStores = {};
+ // private
+ this._started = false;
+ lang.mixin(this, params);
+ // mixin views configuration to current view instance.
+ if(this.parent.views){
+ lang.mixin(this, this.parent.views[this.name]);
+ }
+ },
+
+ // start view
+ start: function(){
+ // summary:
+ // start view object.
+ // load view template, view controller implement and startup all widgets in view template.
+ if(this._started){
+ return this;
+ }
+ this._startDef = new Deferred();
+ when(this.load(), lang.hitch(this, function(){
+ // call setupModel, after setupModel startup will be called after startup the loadViewDeferred will be resolved
+ this._createDataStore(this);
+ this._setupModel();
+ }));
+ return this._startDef;
+ },
+
+ load: function(){
+ var vcDef = this._loadViewController();
+ when(vcDef, lang.hitch(this, function(controller){
+ if(controller){
- lang.mixin(this, controller);
++ //lang.mixin(this, controller);
++ declare.safeMixin(this, controller);
+ }
+ }));
+ return vcDef;
+ },
+
+ _createDataStore: function(){
+ // summary:
+ // Create data store instance for View specific stores
+ //
+ // TODO: move this into a common place for use by main and ViewBase
+ //
+ if(this.parent.loadedStores){
+ lang.mixin(this.loadedStores, this.parent.loadedStores);
+ }
+
+ if(this.stores){
+ //create stores in the configuration.
+ for(var item in this.stores){
+ if(item.charAt(0) !== "_"){//skip the private properties
+ var type = this.stores[item].type ? this.stores[item].type : "dojo/store/Memory";
+ var config = {};
+ if(this.stores[item].params){
+ lang.mixin(config, this.stores[item].params);
+ }
+ // we assume the store is here through dependencies
+ try{
+ var storeCtor = require(type);
+ }catch(e){
+ throw new Error(type+" must be listed in the dependencies");
+ }
+ if(config.data && lang.isString(config.data)){
+ //get the object specified by string value of data property
+ //cannot assign object literal or reference to data property
+ //because json.ref will generate __parent to point to its parent
+ //and will cause infinitive loop when creating StatefulModel.
+ config.data = lang.getObject(config.data);
+ }
+ if(this.stores[item].observable){
+ try{
+ var observableCtor = require("dojo/store/Observable");
+ }catch(e){
+ throw new Error("dojo/store/Observable must be listed in the dependencies");
+ }
+ this.stores[item].store = observableCtor(new storeCtor(config));
+ }else{
+ this.stores[item].store = new storeCtor(config);
+ }
+ this.loadedStores[item] = this.stores[item].store; // add this store to loadedStores for the view
+ }
+ }
+ }
+ },
+
+ _setupModel: function(){
+ // summary:
+ // Load views model if it is not already loaded then call _startup.
+ // tags:
+ // private
+
+ if(!this.loadedModels){
+ var createPromise;
+ try{
+ createPromise = model(this.models, this.parent, this.app);
+ }catch(e){
+ throw new Error("Error creating models: "+e.message);
+ }
+ when(createPromise, lang.hitch(this, function(models){
+ if(models){
+ // if models is an array it comes from dojo/promise/all. Each array slot contains the same result object
+ // so pick slot 0.
+ this.loadedModels = lang.isArray(models)?models[0]:models;
+ }
+ this._startup();
+ }),
+ function(err){
+ throw new Error("Error creating models: "+err.message);
+ });
+ }else{ // loadedModels already created so call _startup
+ this._startup();
+ }
+ },
+
+ _startup: function(){
+ // summary:
+ // startup widgets in view template.
+ // tags:
+ // private
+
+ this._initViewHidden();
+ this._needsResize = true; // flag used to be sure resize has been called before transition
+
+ this._startLayout();
+ },
+
+ _initViewHidden: function(){
+ domStyle.set(this.domNode, "visibility", "hidden");
+ },
+
+ _startLayout: function(){
+ // summary:
+ // startup widgets in view template.
+ // tags:
+ // private
+ this.app.log(" > in app/ViewBase _startLayout firing layout for name=[",this.name,"], parent.name=[",this.parent.name,"]");
+
+ if(!this.hasOwnProperty("constraint")){
+ this.constraint = domAttr.get(this.domNode, "data-app-constraint") || "center";
+ }
+ constraints.register(this.constraint);
+
+
+ this.app.emit("app-initLayout", {
+ "view": this,
+ "callback": lang.hitch(this, function(){
+ //start widget
+ this.startup();
+
+ // call view assistant's init() method to initialize view
+ this.app.log(" > in app/ViewBase calling init() name=[",this.name,"], parent.name=[",this.parent.name,"]");
+ this.init();
+ this._started = true;
+ if(this._startDef){
+ this._startDef.resolve(this);
+ }
+ })
+ });
+ },
+
+
+ _loadViewController: function(){
+ // summary:
+ // Load view controller by configuration or by default.
+ // tags:
+ // private
+ //
+ var viewControllerDef = new Deferred();
+ var path;
+
+ if(!this.controller){ // no longer using this.controller === "none", if we dont have one it means none.
+ this.app.log(" > in app/ViewBase _loadViewController no controller set for view name=[",this.name,"], parent.name=[",this.parent.name,"]");
+ viewControllerDef.resolve(true);
+ return viewControllerDef;
+ }else{
+ path = this.controller.replace(/(\.js)$/, "");
+ }
+
+ var requireSignal;
+ try{
+ var loadFile = path;
+ var index = loadFile.indexOf("./");
+ if(index >= 0){
+ loadFile = path.substring(index+2);
+ }
+ requireSignal = require.on ? require.on("error", function(error){
+ if(viewControllerDef.isResolved() || viewControllerDef.isRejected()){
+ return;
+ }
+ if(error.info[0] && (error.info[0].indexOf(loadFile) >= 0)){
+ viewControllerDef.resolve(false);
+ if(requireSignal){
+ requireSignal.remove();
+ }
+ }
+ }) : null;
+
+ if(path.indexOf("./") == 0){
+ path = "app/"+path;
+ }
+
+ require([path], function(controller){
+ viewControllerDef.resolve(controller);
+ if(requireSignal){
+ requireSignal.remove();
+ }
+ });
+ }catch(e){
+ viewControllerDef.reject(e);
+ if(requireSignal){
+ requireSignal.remove();
+ }
+ }
+ return viewControllerDef;
+ },
+
+ init: function(){
+ // summary:
+ // view life cycle init()
+ },
+
+ beforeActivate: function(){
+ // summary:
+ // view life cycle beforeActivate()
+ },
+
+ afterActivate: function(){
+ // summary:
+ // view life cycle afterActivate()
+ },
+
+ beforeDeactivate: function(){
+ // summary:
+ // view life cycle beforeDeactivate()
+ },
+
+ afterDeactivate: function(){
+ // summary:
+ // view life cycle afterDeactivate()
+ },
+
+ destroy: function(){
+ // summary:
+ // view life cycle destroy()
+ }
+ });
+});
diff --cc dojox/app/controllers/HistoryHash.js
index c2b6940,0000000..c2b6940
mode 100644,000000..100755
--- a/dojox/app/controllers/HistoryHash.js
+++ b/dojox/app/controllers/HistoryHash.js
diff --cc dojox/app/controllers/Layout.js
index dd9f32c,0000000..1d0c9b6
mode 100644,000000..100644
--- a/dojox/app/controllers/Layout.js
+++ b/dojox/app/controllers/Layout.js
@@@ -1,239 -1,0 +1,240 @@@
+define(["dojo/_base/declare", "dojo/_base/lang", "dojo/_base/array", "dojo/_base/window",
+ "dojo/query", "dojo/dom-geometry", "dojo/dom-attr", "dojo/dom-style", "dijit/registry",
- "./LayoutBase", "../utils/layout", "../utils/constraints"],
- function(declare, lang, array, win, query, domGeom, domAttr, domStyle, registry, LayoutBase, layout, constraints){
++ "./LayoutBase", "../utils/layout", "../utils/constraints", "dojo/sniff"],
++function(declare, lang, array, win, query, domGeom, domAttr, domStyle, registry, LayoutBase, layout, constraints, has){
+ // module:
+ // dojox/app/controllers/Layout
+ // summary:
+ // Extends LayoutBase which binds "app-initLayout", "app-layoutView" and "app-resize" events on application instance.
+
+ return declare("dojox.app.controllers.Layout", LayoutBase, {
+
+ constructor: function(app, events){
+ // summary:
+ // bind "app-initLayout" and "app-layoutView" events on application instance.
+ //
+ // app:
+ // dojox/app application instance.
+ // events:
+ // {event : handler}
+ },
+
+ onResize: function(){
+ this._doResize(this.app);
+ // this is needed to resize the children on an orientation change or a resize of the browser.
+ // it was being done in _doResize, but was not needed for every call to _doResize.
+ this.resizeSelectedChildren(this.app);
+ },
+
+
+ resizeSelectedChildren: function(w){
+ for(var hash in w.selectedChildren){ // need this to handle all selectedChildren
+ if(w.selectedChildren[hash] && w.selectedChildren[hash].domNode){
+ this.app.log("in Layout resizeSelectedChildren calling resizeSelectedChildren calling _doResize for w.selectedChildren[hash].id="+w.selectedChildren[hash].id);
+ this._doResize(w.selectedChildren[hash]);
+ // Call resize on child widgets, needed to get the scrollableView to resize correctly initially
+ array.forEach(w.selectedChildren[hash].domNode.children, function(child){
+ if(registry.byId(child.id) && registry.byId(child.id).resize){
+ registry.byId(child.id).resize();
+ }
+ });
+
+ this.resizeSelectedChildren(w.selectedChildren[hash]);
+ }
+ }
+ },
+
+ initLayout: function(event){
+ // summary:
+ // Response to dojox/app "app-initLayout" event.
+ //
+ // example:
+ // Use emit to trigger "app-initLayout" event, and this function will respond to the event. For example:
+ // | this.app.emit("app-initLayout", view);
+ //
+ // event: Object
+ // | {"view": view, "callback": function(){}};
+ this.app.log("in app/controllers/Layout.initLayout event=",event);
+ this.app.log("in app/controllers/Layout.initLayout event.view.parent.name=[",event.view.parent.name,"]");
+
- if (!event.view.domNode.parentNode) {
++ if (!event.view.domNode.parentNode || (has("ie") == 8 && !event.view.domNode.parentElement)) {
+ if(this.app.useConfigOrder){
+ event.view.parent.domNode.appendChild(event.view.domNode);
+ }else{
+ this.addViewToParentDomByConstraint(event);
+ }
+ }
+ domAttr.set(event.view.domNode, "data-app-constraint", event.view.constraint);
+ this.inherited(arguments);
+ },
+
+ addViewToParentDomByConstraint: function(event){
+ // summary:
+ // Insert the view domNode into the parent domNode based upon the constraints.
+ // It should layout the children in this order: top, left, center, right, bottom
+ // Unless it is rtl then it should layout the children in this order: top, right, center, left, bottom
+ //
+ // event: Object
+ // | {"parent":parent, "view":view, "removeView": boolean}
+ var newViewConstraint = event.view.constraint;
+ if(newViewConstraint === "bottom"){ // if new is bottom always place last
+ event.view.parent.domNode.appendChild(event.view.domNode);
+ }else if(newViewConstraint === "top"){ // if new is top always place first
+ event.view.parent.domNode.insertBefore(event.view.domNode, event.view.parent.domNode.firstChild);
+ }else{ // need to compare new constraint to the previous ones
+ if(event.view.parent.domNode.children.length > 0){ // parent node has children, check constraints
+ // in this loop if previous is top or left skip it and look for next child, otherwise process it
+ for(var childIndex in event.view.parent.domNode.children){
+ var child = event.view.parent.domNode.children[childIndex];
+ var dir = domStyle.get(event.view.parent.domNode,"direction");
+ var isltr = (dir === "ltr");
+ var LEADING_VIEW = isltr ? "left" : "right";
+ var TRAILING_VIEW = isltr ? "right" : "left";
+ if(child.getAttribute && child.getAttribute("data-app-constraint")) {
+ var previousViewConstraint = child.getAttribute("data-app-constraint");
+ // if previous is bottom or previous is Trailing
+ // or previous is not top and newView is Leading we need to insert before this child
+ if(previousViewConstraint === "bottom" ||
+ (previousViewConstraint === TRAILING_VIEW) ||
+ (previousViewConstraint !== "top" &&
+ (newViewConstraint === LEADING_VIEW))){
+ event.view.parent.domNode.insertBefore(event.view.domNode, child);
+ break;
+ }
+ }
+ }
+ }
+ }
- if(!event.view.domNode.parentNode){ // if the domNode was not added to the parent yet add it to the end now
++ // if the domNode was not added to the parent yet add it to the end now
++ if (!event.view.domNode.parentNode || (has("ie") == 8 && !event.view.domNode.parentElement)) {
+ event.view.parent.domNode.appendChild(event.view.domNode);
+ }
+ },
+
+ _doResize: function(view){
+ // summary:
+ // resize view.
+ //
+ // view: Object
+ // view instance needs to do layout.
+ var node = view.domNode;
+ if(!node){
+ this.app.log("Warning - View has not been loaded, in Layout _doResize view.domNode is not set for view.id="+view.id+" view=",view);
+ return;
+ }
+
+ // If either height or width wasn't specified by the user, then query node for it.
+ // But note that setting the margin box and then immediately querying dimensions may return
+ // inaccurate results, so try not to depend on it.
+ var mb = {};
+ if( !("h" in mb) || !("w" in mb) ){
+ mb = lang.mixin(domGeom.getMarginBox(node), mb); // just use dojo/_base/html.marginBox() to fill in missing values
+ }
+
+ // Compute and save the size of my border box and content box
+ // (w/out calling dojo/_base/html.contentBox() since that may fail if size was recently set)
+ if(view !== this.app){
+ var cs = domStyle.getComputedStyle(node);
+ var me = domGeom.getMarginExtents(node, cs);
+ var be = domGeom.getBorderExtents(node, cs);
+ var bb = (view._borderBox = {
+ w: mb.w - (me.w + be.w),
+ h: mb.h - (me.h + be.h)
+ });
+ var pe = domGeom.getPadExtents(node, cs);
+ view._contentBox = {
+ l: domStyle.toPixelValue(node, cs.paddingLeft),
+ t: domStyle.toPixelValue(node, cs.paddingTop),
+ w: bb.w - pe.w,
+ h: bb.h - pe.h
+ };
+ }else{
+ // if we are layouting the top level app the above code does not work when hiding address bar
+ // so let's use similar code to dojo mobile.
+ view._contentBox = {
+ l: 0,
+ t: 0,
+ h: win.global.innerHeight || win.doc.documentElement.clientHeight,
+ w: win.global.innerWidth || win.doc.documentElement.clientWidth
+ };
+ }
+
+ this.inherited(arguments);
+ },
+
+ layoutView: function(event){
+ // summary:
+ // Response to dojox/app "app-layoutView" event.
+ //
+ // example:
+ // Use emit to trigger "app-layoutView" event, and this function will response the event. For example:
+ // | this.app.emit("app-layoutView", view);
+ //
+ // event: Object
+ // | {"parent":parent, "view":view, "removeView": boolean}
+ if(event.view){
+ this.inherited(arguments);
+ // normally when called from transition doResize will be false, and the resize will only be done when the app-resize event is fired
+ if(event.doResize){
+ this._doResize(event.parent || this.app);
+ this._doResize(event.view);
+ }
+ }
+ },
+
+ _doLayout: function(view){
+ // summary:
+ // do view layout.
+ //
+ // view: Object
+ // view instance needs to do layout.
+
+ if(!view){
+ console.warn("layout empty view.");
+ return;
+ }
+ this.app.log("in Layout _doLayout called for view.id="+view.id+" view=",view);
+
+ var children;
+ // TODO: probably need to handle selectedChildren here, not just selected child...
+ // TODO: why are we passing view here? not parent? This call does not seem logical?
+ var selectedChild = constraints.getSelectedChild(view, view.constraint);
+ if(selectedChild && selectedChild.isFullScreen){
+ console.warn("fullscreen sceen layout");
+ /*
+ fullScreenScene=true;
+ children=[{domNode: selectedChild.domNode,constraint: "center"}];
+ query("> [constraint]",this.domNode).forEach(function(c){
+ if(selectedChild.domNode!==c.domNode){
+ dstyle(c.domNode,"display","none");
+ }
+ })
+ */
+ }else{
+ children = query("> [data-app-constraint]", view.domNode).map(function(node){
+ var w = registry.getEnclosingWidget(node);
+ if(w){
+ w._constraint = domAttr.get(node, "data-app-constraint");
+ return w;
+ }
+
+ return {
+ domNode: node,
+ _constraint: domAttr.get(node, "data-app-constraint")
+ };
+ });
+
+ if(selectedChild){
+ children = array.filter(children, function(c){
+ // do not need to set display none here it is set in select.
+ return c.domNode && c._constraint;
+ }, view);
+ }
+ }
+ // We don't need to layout children if this._contentBox is null for the operation will do nothing.
+ if(view._contentBox){
+ layout.layoutChildren(view.domNode, view._contentBox, children);
+ }
+ }
+ });
+});
diff --cc dojox/app/main.js
index 0cb0f4f,0000000..71c8abc
mode 100644,000000..100644
--- a/dojox/app/main.js
+++ b/dojox/app/main.js
@@@ -1,375 -1,0 +1,375 @@@
+define(["require", "dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/declare", "dojo/_base/config",
+ "dojo/_base/window", "dojo/Evented", "dojo/Deferred", "dojo/when", "dojo/has", "dojo/on", "dojo/ready",
+ "dojo/dom-construct", "dojo/dom-attr", "./utils/model", "./utils/nls", "./module/lifecycle",
+ "./utils/hash", "./utils/constraints", "./utils/config"],
+ function(require, kernel, lang, declare, config, win, Evented, Deferred, when, has, on, ready, domConstruct, domAttr,
+ model, nls, lifecycle, hash, constraints, configUtils){
+
+ has.add("app-log-api", (config["app"] || {}).debugApp);
+
+ var Application = declare(Evented, {
+ constructor: function(params, node){
- lang.mixin(this, params);
++ declare.safeMixin(this, params);
+ this.params = params;
+ this.id = params.id;
+ this.defaultView = params.defaultView;
+ this.controllers = [];
+ this.children = {};
+ this.loadedModels = {};
+ this.loadedStores = {};
+ // Create a new domNode and append to body
+ // Need to bind startTransition event on application domNode,
+ // Because dojox/mobile/ViewController bind startTransition event on document.body
+ // Make application's root domNode id unique because this id can be visited by window namespace on Chrome 18.
+ this.setDomNode(domConstruct.create("div", {
+ id: this.id+"_Root",
+ style: "width:100%; height:100%; overflow-y:hidden; overflow-x:hidden;"
+ }));
+ node.appendChild(this.domNode);
+ },
+
+ createDataStore: function(params){
+ // summary:
+ // Create data store instance
+ //
+ // params: Object
+ // data stores configuration.
+
+ if(params.stores){
+ //create stores in the configuration.
+ for(var item in params.stores){
+ if(item.charAt(0) !== "_"){//skip the private properties
+ var type = params.stores[item].type ? params.stores[item].type : "dojo/store/Memory";
+ var config = {};
+ if(params.stores[item].params){
+ lang.mixin(config, params.stores[item].params);
+ }
+ // we assume the store is here through dependencies
+ try{
+ var storeCtor = require(type);
+ }catch(e){
+ throw new Error(type+" must be listed in the dependencies");
+ }
+ if(config.data && lang.isString(config.data)){
+ //get the object specified by string value of data property
+ //cannot assign object literal or reference to data property
+ //because json.ref will generate __parent to point to its parent
+ //and will cause infinitive loop when creating StatefulModel.
+ config.data = lang.getObject(config.data);
+ }
+ if(params.stores[item].observable){
+ try{
+ var observableCtor = require("dojo/store/Observable");
+ }catch(e){
+ throw new Error("dojo/store/Observable must be listed in the dependencies");
+ }
+ params.stores[item].store = observableCtor(new storeCtor(config));
+ }else{
+ params.stores[item].store = new storeCtor(config);
+ }
+ this.loadedStores[item] = params.stores[item].store;
+ }
+ }
+ }
+ },
+
+ createControllers: function(controllers){
+ // summary:
+ // Create controller instance
+ //
+ // controllers: Array
+ // controller configuration array.
+ // returns:
+ // controllerDeferred object
+
+ if(controllers){
+ var requireItems = [];
+ for(var i = 0; i < controllers.length; i++){
+ requireItems.push(controllers[i]);
+ }
+
+ var def = new Deferred();
+ var requireSignal;
+ try{
+ requireSignal = require.on ? require.on("error", function(error){
+ if(def.isResolved() || def.isRejected()){
+ return;
+ }
+ def.reject("load controllers error.");
+ if(requireSignal){
+ requireSignal.remove();
+ }
+ }) : null;
+ require(requireItems, function(){
+ def.resolve.call(def, arguments);
+ if(requireSignal){
+ requireSignal.remove();
+ }
+ });
+ }catch(e){
+ def.reject(e);
+ if(requireSignal){
+ requireSignal.remove();
+ }
+ }
+
+ var controllerDef = new Deferred();
+ when(def, lang.hitch(this, function(){
+ for(var i = 0; i < arguments[0].length; i++){
+ // instantiate controllers, set Application object, and perform auto binding
+ this.controllers.push((new arguments[0][i](this)).bind());
+ }
+ controllerDef.resolve(this);
+ }), function(){
+ //require def error, reject loadChildDeferred
+ controllerDef.reject("load controllers error.");
+ });
+ return controllerDef;
+ }
+ },
+
+ trigger: function(event, params){
+ // summary:
+ // trigger an event. Deprecated, use emit instead.
+ //
+ // event: String
+ // event name. The event is binded by controller.bind() method.
+ // params: Object
+ // event params.
+ kernel.deprecated("dojox.app.Application.trigger", "Use dojox.app.Application.emit instead", "2.0");
+ this.emit(event, params);
+ },
+
+ // setup default view and Controllers and startup the default view
+ start: function(){
+ //
+ //create application level data store
+ this.createDataStore(this.params);
+
+ // create application level data model
+ var loadModelLoaderDeferred = new Deferred();
+ var createPromise;
+ try{
+ createPromise = model(this.params.models, this, this);
+ }catch(e){
+ loadModelLoaderDeferred.reject(e);
+ return loadModelLoaderDeferred.promise;
+ }
+ when(createPromise, lang.hitch(this, function(models){
+ // if models is an array it comes from dojo/promise/all. Each array slot contains the same result object
+ // so pick slot 0.
+ this.loadedModels = lang.isArray(models)?models[0]:models;
+ this.setupControllers();
+ // if available load root NLS
+ when(nls(this.params), lang.hitch(this, function(nls){
+ if(nls){
+ lang.mixin(this.nls = {}, nls);
+ }
+ this.startup();
+ }));
+ }), function(){
+ loadModelLoaderDeferred.reject("load model error.")
+ });
+ },
+
+ setDomNode: function(domNode){
+ var oldNode = this.domNode;
+ this.domNode = domNode;
+ this.emit("app-domNode", {
+ oldNode: oldNode,
+ newNode: domNode
+ });
+ },
+
+ setupControllers: function(){
+ // create application controller instance
+ // move set _startView operation from history module to application
+ var currentHash = window.location.hash;
+ // this._startView = (((currentHash && currentHash.charAt(0) == "#") ? currentHash.substr(1) : currentHash) || this.defaultView).split('&')[0];
+ this._startView = hash.getTarget(currentHash, this.defaultView);
+ this._startParams = hash.getParams(currentHash);
+ },
+
+ startup: function(){
+ // load controllers and views
+ //
+ this.selectedChildren = {};
+ var controllers = this.createControllers(this.params.controllers);
+ // constraint on app
+ if(this.hasOwnProperty("constraint")){
+ constraints.register(this.params.constraints);
+ }else{
+ this.constraint = "center";
+ }
+ var emitLoad = function(){
+ // emit "app-load" event and let controller to load view.
+ this.emit("app-load", {
+ viewId: this.defaultView,
+ initLoad: true,
+ params: this._startParams,
+ callback: lang.hitch(this, function (){
+ this.emit("app-transition", {
+ viewId: this.defaultView,
+ forceTransitionNone: true, // we want to avoid the transition on the first display for the defaultView
+ opts: { params: this._startParams }
+ });
+ if(this.defaultView !== this._startView){
+ // transition to startView. If startView==defaultView, that means initial the default view.
+ this.emit("app-transition", {
+ viewId: this._startView,
+ opts: { params: this._startParams }
+ });
+ }
+ this.setStatus(this.lifecycle.STARTED);
+ })
+ });
+ };
+ when(controllers, lang.hitch(this, function(){
+ if(this.template){
+ // emit "app-init" event so that the Load controller can initialize root view
+ this.emit("app-init", {
+ app: this, // pass the app into the View so it can have easy access to app
+ name: this.name,
+ type: this.type,
+ parent: this,
+ templateString: this.templateString,
+ controller: this.controller,
+ callback: lang.hitch(this, function(view){
+ this.setDomNode(view.domNode);
+ emitLoad.call(this);
+ })
+ });
+ }else{
+ emitLoad.call(this);
+ }
+ }));
+ }
+ });
+
+ function generateApp(config, node){
+ // summary:
+ // generate the application
+ //
+ // config: Object
+ // app config
+ // node: domNode
+ // domNode.
+ var path;
+
+ // call configProcessHas to process any has blocks in the config
+ config = configUtils.configProcessHas(config);
+
+ if(!config.loaderConfig){
+ config.loaderConfig = {};
+ }
+ if(!config.loaderConfig.paths){
+ config.loaderConfig.paths = {};
+ }
+ if(!config.loaderConfig.paths["app"]){
+ // Register application module path
+ path = window.location.pathname;
+ if(path.charAt(path.length) != "/"){
+ path = path.split("/");
+ path.pop();
+ path = path.join("/");
+ }
+ config.loaderConfig.paths["app"] = path;
+ }
+ require(config.loaderConfig);
+
+ if(!config.modules){
+ config.modules = [];
+ }
+ // add dojox/app lifecycle module by default
+ config.modules.push("./module/lifecycle");
+ var modules = config.modules.concat(config.dependencies?config.dependencies:[]);
+
+ if(config.template){
+ path = config.template;
+ if(path.indexOf("./") == 0){
+ path = "app/"+path;
+ }
+ modules.push("dojo/text!" + path);
+ }
+
+ require(modules, function(){
+ var modules = [Application];
+ for(var i = 0; i < config.modules.length; i++){
+ modules.push(arguments[i]);
+ }
+
+ if(config.template){
+ var ext = {
+ templateString: arguments[arguments.length - 1]
+ }
+ }
+ App = declare(modules, ext);
+
+ ready(function(){
+ var app = new App(config, node || win.body());
+
+ if(has("app-log-api")){
+ app.log = function(){
+ // summary:
+ // If config is set to turn on app logging, then log msg to the console
+ //
+ // arguments:
+ // the message to be logged,
+ // all but the last argument will be treated as Strings and be concatenated together,
+ // the last argument can be an object it will be added as an argument to the console.log
+ var msg = "";
+ try{
+ for(var i = 0; i < arguments.length-1; i++){
+ msg = msg + arguments[i];
+ }
+ console.log(msg,arguments[arguments.length-1]);
+ }catch(e){}
+ };
+ }else{
+ app.log = function(){}; // noop
+ }
+
+ app.transitionToView = function(/*DomNode*/target, /*Object*/transitionOptions, /*Event?*/triggerEvent){
+ // summary:
+ // A convenience function to fire the transition event to transition to the view.
+ //
+ // target:
+ // The DOM node that initiates the transition (for example a ListItem).
+ // transitionOptions:
+ // Contains the transition options.
+ // triggerEvent:
+ // The event that triggered the transition (for example a touch event on a ListItem).
+ var opts = {bubbles:true, cancelable:true, detail: transitionOptions, triggerEvent: triggerEvent || null};
+ on.emit(target,"startTransition", opts);
+ };
+
+ app.setStatus(app.lifecycle.STARTING);
+ // Create global namespace for application.
+ // The global name is application id. ie: modelApp
+ var globalAppName = app.id;
+ if(window[globalAppName]){
- lang.mixin(app, window[globalAppName]);
++ declare.safeMixin(app, window[globalAppName]);
+ }
+ window[globalAppName] = app;
+ app.start();
+ });
+ });
+ }
+
+ return function(config, node){
+ if(!config){
+ throw new Error("App Config Missing");
+ }
+
+ if(config.validate){
+ require(["dojox/json/schema", "dojox/json/ref", "dojo/text!dojox/application/schema/application.json"], function(schema, appSchema){
+ schema = dojox.json.ref.resolveJson(schema);
+ if(schema.validate(config, appSchema)){
+ generateApp(config, node);
+ }
+ });
+ }else{
+ generateApp(config, node);
+ }
+ }
+});
diff --cc dojox/app/tests/domOrderByConstraint/main/main.js
index f9d9b14,0000000..70096eb
mode 100644,000000..100644
--- a/dojox/app/tests/domOrderByConstraint/main/main.js
+++ b/dojox/app/tests/domOrderByConstraint/main/main.js
@@@ -1,111 -1,0 +1,111 @@@
+define(["dojo/dom", "dojo/_base/connect", "dijit/registry"],
+function(dom, connect, registry){
+
+ var _connectResults = []; // events connect results
+
+ return {
+ // simple view init
+ init: function(){
+ currentModel = this.loadedModels.names;
+ var connectResult;
+
- self = this;
++ var self = this;
+ connectResult = connect.connect(dom.byId('testRTL'), "click", function(e){
+ console.log("testRTL called. ");
+ //first set the dir="rtl" on the app root
+ self.app.domNode.parentNode.dir = "rtl";
+ //next unload all chldren
+ var params = {};
+ var footer = self.app.children.domOrderByConstraint_footer;
+ var header = footer.children.domOrderByConstraint_footer_header;
+ var right = header.children.domOrderByConstraint_footer_header_right;
+ var left = right.children.domOrderByConstraint_footer_header_right_left;
+ var center = left.children.domOrderByConstraint_footer_header_right_left_center;
+ params.view = header;
+ params.parent = footer;
+ //params.viewId = view.id;
+ self.app.emit("unload-view", params);
+ });
+ _connectResults.push(connectResult);
+
+ connectResult = connect.connect(dom.byId('testRTL2'), "click", function(e){
+ console.log("testRTL called. ");
+ var params = {};
+ var footer = self.app.children.domOrderByConstraint_footer;
+ //first set the dir="rtl" on the footer
+ footer.domNode.parentNode.dir = "rtl";
+
+ //next unload all chldren
+ var header = footer.children.domOrderByConstraint_footer_header;
+ var right = header.children.domOrderByConstraint_footer_header_right;
+ var left = right.children.domOrderByConstraint_footer_header_right_left;
+ var center = left.children.domOrderByConstraint_footer_header_right_left_center;
+ params.view = header;
+ params.parent = footer;
+ //params.viewId = view.id;
+ self.app.emit("unload-view", params);
+ });
+ _connectResults.push(connectResult);
+
+ connectResult = connect.connect(dom.byId('testLTR'), "click", function(e){
+ console.log("testLTR called. ");
+ //first set the dir="rtl" on the app root
+ self.app.domNode.parentNode.dir = "ltr";
+ //next unload all chldren
+ var params = {};
+ var footer = self.app.children.domOrderByConstraint_footer;
+ var header = footer.children.domOrderByConstraint_footer_header;
+ var right = header.children.domOrderByConstraint_footer_header_right;
+ var left = right.children.domOrderByConstraint_footer_header_right_left;
+ var center = left.children.domOrderByConstraint_footer_header_right_left_center;
+ params.view = header;
+ params.parent = footer;
+ //params.viewId = view.id;
+ self.app.emit("unload-view", params);
+ });
+ _connectResults.push(connectResult);
+
+ connectResult = connect.connect(dom.byId('testLTR2'), "click", function(e){
+ console.log("testLTR called. ");
+ var params = {};
+ var footer = self.app.children.domOrderByConstraint_footer;
+ //first set the dir="ltr" on the footer
+ footer.domNode.parentNode.dir = "ltr";
+
+ //next unload all chldren
+ var header = footer.children.domOrderByConstraint_footer_header;
+ var right = header.children.domOrderByConstraint_footer_header_right;
+ var left = right.children.domOrderByConstraint_footer_header_right_left;
+ var center = left.children.domOrderByConstraint_footer_header_right_left_center;
+ params.view = header;
+ params.parent = footer;
+ //params.viewId = view.id;
+ self.app.emit("unload-view", params);
+ });
+ _connectResults.push(connectResult);
+
+ connectResult = connect.connect(dom.byId('testTrans'), "click", function(e){
+ console.log("testTrans called. ");
+ var params = {};
+ var transOpts = {
+ title : "footer,header,right,left,center2",
+ target : "footer,header,right,left,center2",
+ url : "#footer,header,right,left,center2", // this is optional if not set it will be created from target
+ params : params
+ };
+ self.app.transitionToView(e.target,transOpts,e);
+ });
+ _connectResults.push(connectResult);
+
+ },
+
+ // simple view destroy
+ destroy: function(){
+ var connectResult = _connectResults.pop();
+ while(connectResult){
+ connect.disconnect(connectResult);
+ connectResult = _connectResults.pop();
+ }
+ }
+ };
+});
diff --cc dojox/app/tests/domOrderByConstraint/main/main2.js
index 5d20e6e,0000000..03b259e
mode 100644,000000..100644
--- a/dojox/app/tests/domOrderByConstraint/main/main2.js
+++ b/dojox/app/tests/domOrderByConstraint/main/main2.js
@@@ -1,113 -1,0 +1,113 @@@
+define(["dojo/dom", "dojo/_base/connect", "dijit/registry"],
+function(dom, connect, registry){
+
+ var _connectResults = []; // events connect results
+ var currentModel = null;
+
+ var setFromModel = function (){
+ // registry.byId("firstInput1").set('value', currentModel[0].First);
+ // registry.byId("lastInput1").set('value', currentModel[0].Last);
+ // registry.byId("emailInput1").set('value', currentModel[0].Email);
+ // registry.byId("shiptostreetInput1").set('value', currentModel[0].ShipTo.Street);
+ // registry.byId("shiptocityInput1").set('value', currentModel[0].ShipTo.City);
+ // registry.byId("shiptostateInput1").set('value', currentModel[0].ShipTo.State);
+ // registry.byId("shiptozipInput1").set('value', currentModel[0].ShipTo.Zip);
+ // registry.byId("billtostreetInput1").set('value', currentModel[0].BillTo.Street);
+ // registry.byId("billtocityInput1").set('value', currentModel[0].BillTo.City);
+ // registry.byId("billtostateInput1").set('value', currentModel[0].BillTo.State);
+ // registry.byId("billtozipInput1").set('value', currentModel[0].BillTo.Zip);
+ };
+
+ return {
+ // simple view init
+ init: function(){
+ currentModel = this.loadedModels.names;
+ var connectResult;
+
- self = this;
++ var self = this;
+ connectResult = connect.connect(dom.byId('2testRTL'), "click", function(e){
+ console.log("testRTL called. ");
+ //first set the dir="rtl" on the app root
+ self.app.domNode.parentNode.dir = "rtl";
+ //next unload all chldren
+ var params = {};
+ var footer = self.app.children.domOrderByConstraint_footer;
+ var header = footer.children.domOrderByConstraint_footer_header;
+ var right = header.children.domOrderByConstraint_footer_header_right;
+ var left = right.children.domOrderByConstraint_footer_header_right_left;
+ var center = left.children.domOrderByConstraint_footer_header_right_left_center;
+ params.view = header;
+ params.parent = footer;
+ //params.viewId = view.id;
+ self.app.emit("unload-view", params);
+ });
+ _connectResults.push(connectResult);
+
+ connectResult = connect.connect(dom.byId('2testRTL2'), "click", function(e){
+ console.log("testRTL called. ");
+ var params = {};
+ var footer = self.app.children.domOrderByConstraint_footer;
+ //first set the dir="rtl" on the footer
+ footer.domNode.parentNode.dir = "rtl";
+
+ //next unload all chldren
+ var header = footer.children.domOrderByConstraint_footer_header;
+ var right = header.children.domOrderByConstraint_footer_header_right;
+ var left = right.children.domOrderByConstraint_footer_header_right_left;
+ var center = left.children.domOrderByConstraint_footer_header_right_left_center;
+ params.view = header;
+ params.parent = footer;
+ //params.viewId = view.id;
+ self.app.emit("unload-view", params);
+ });
+ _connectResults.push(connectResult);
+
+ connectResult = connect.connect(dom.byId('2testLTR'), "click", function(e){
+ console.log("testLTR called. ");
+ //first set the dir="rtl" on the app root
+ self.app.domNode.parentNode.dir = "ltr";
+ //next unload all chldren
+ var params = {};
+ var footer = self.app.children.domOrderByConstraint_footer;
+ var header = footer.children.domOrderByConstraint_footer_header;
+ var right = header.children.domOrderByConstraint_footer_header_right;
+ var left = right.children.domOrderByConstraint_footer_header_right_left;
+ var center = left.children.domOrderByConstraint_footer_header_right_left_center;
+ params.view = header;
+ params.parent = footer;
+ //params.viewId = view.id;
+ self.app.emit("unload-view", params);
+ });
+ _connectResults.push(connectResult);
+
+ connectResult = connect.connect(dom.byId('2testLTR2'), "click", function(e){
+ console.log("testLTR called. ");
+ var params = {};
+ var footer = self.app.children.domOrderByConstraint_footer;
+ //first set the dir="ltr" on the footer
+ footer.domNode.parentNode.dir = "ltr";
+
+ //next unload all chldren
+ var header = footer.children.domOrderByConstraint_footer_header;
+ var right = header.children.domOrderByConstraint_footer_header_right;
+ var left = right.children.domOrderByConstraint_footer_header_right_left;
+ var center = left.children.domOrderByConstraint_footer_header_right_left_center;
+ params.view = header;
+ params.parent = footer;
+ //params.viewId = view.id;
+ self.app.emit("unload-view", params);
+ });
+ _connectResults.push(connectResult);
+
+ },
+
+ // simple view destroy
+ destroy: function(){
+ var connectResult = _connectResults.pop();
+ while(connectResult){
+ connect.disconnect(connectResult);
+ connectResult = _connectResults.pop();
+ }
+ }
+ };
+});
diff --cc dojox/app/tests/images/a-icon-1-41x41.png
index 6b964ce,0000000..0e94b75
mode 100644,000000..100644
Binary files differ
diff --cc dojox/app/utils/hash.js
index 6ac6ed3,0000000..6ac6ed3
mode 100644,000000..100755
--- a/dojox/app/utils/hash.js
+++ b/dojox/app/utils/hash.js
diff --cc dojox/calendar/CONTRIBUTING.md
index 5ae012f,0000000..5ae012f
mode 100644,000000..100755
--- a/dojox/calendar/CONTRIBUTING.md
+++ b/dojox/calendar/CONTRIBUTING.md
diff --cc dojox/calendar/ColumnViewSecondarySheet.js
index 1c2aae9,0000000..6249a74
mode 100644,000000..100644
--- a/dojox/calendar/ColumnViewSecondarySheet.js
+++ b/dojox/calendar/ColumnViewSecondarySheet.js
@@@ -1,140 -1,0 +1,141 @@@
+define([
+"dojo/_base/array",
+"dojo/_base/declare",
+"dojo/_base/event",
+"dojo/_base/lang",
+"dojo/dom-geometry",
+"dojo/dom-style",
+"dojox/calendar/MatrixView",
+"dojo/text!./templates/ColumnViewSecondarySheet.html"],
+
+function(arr,
+ declare,
+ event,
+ lang,
+ domGeometry,
+ domStyle,
+ MatrixView,
+ template){
+
+ return declare("dojox.calendar.ColumnViewSecondarySheet", MatrixView, {
+
+ // summary:
+ // This class defines a matrix view designed to be embedded in a column view,
+ // usually to display long or all day events on one row.
+
+ templateString: template,
+
+ rowCount: 1,
+
+ cellPaddingTop: 4,
+
+ roundToDay: false,
+
+ _defaultHeight: -1,
+
+ layoutDuringResize: true,
+
+ buildRendering: function(){
+ this.inherited(arguments);
+ this._hScrollNodes = [this.gridTable, this.itemContainerTable];
+ },
+
+ _configureHScrollDomNodes: function(styleWidth){
+ arr.forEach(this._hScrollNodes, function(elt){
+ domStyle.set(elt, "width", styleWidth);
+ }, this);
+ },
+
+ _defaultItemToRendererKindFunc: function(item){
+ // tags:
+ // private
+ return item.allDay ? "horizontal" : null;
+ },
+
+ _formatGridCellLabel: function(){return null;},
+
+ _formatRowHeaderLabel: function(){return null;},
+
+
+ // events redispatch
+ __fixEvt:function(e){
+ e.sheet = "secondary";
+ e.source = this;
+ return e;
+ },
+
+ _dispatchCalendarEvt: function(e, name){
+ e = this.inherited(arguments);
+ if(this.owner.owner){ // the calendar
+ this.owner.owner[name](e);
+ }
+ },
+
+ _layoutExpandRenderers: function(index, hasHiddenItems, hiddenItems){
+ if(!this.expandRenderer || this._expandedRowCol == -1){
+ return;
+ }
+ var h = domGeometry.getMarginBox(this.domNode).h;
- if(this._defaultHeight == -1){
++ if(this._defaultHeight == -1 || // not set
++ this._defaultHeight === 0){ // initialized at 0, must be reset
+ this._defaultHeight = h;
+ }
+
- if(this._defaultHeight != -1 && this._defaultHeight != h && h >= this._getExpandedHeight() ||
++ if(this._defaultHeight != h && h >= this._getExpandedHeight() ||
+ this._expandedRowCol !== undefined && this._expandedRowCol !== -1){
+ var col = this._expandedRowCol;
+ if(col >= this.renderData.columnCount){
+ col = 0;
+ }
+ this._layoutExpandRendererImpl(0, col, null, true);
+ }else{
+ this.inherited(arguments);
+ }
+ },
+
+ expandRendererClickHandler: function(e, renderer){
+ // summary:
+ // Default action when an expand renderer is clicked.
+ // This method will expand the secondary sheet to show all the events.
+ // e: Event
+ // The mouse event.
+ // renderer: Object
+ // The renderer that was clicked.
+ // tags:
+ // callback
+
+ event.stop(e);
+
+ var h = domGeometry.getMarginBox(this.domNode).h;
+ var expandedH = this._getExpandedHeight();
+ if(this._defaultHeight == h || h < expandedH){
+ this._expandedRowCol = renderer.columnIndex;
+ this.owner.resizeSecondarySheet(expandedH);
+ }else{
+ delete this._expandedRowCol;
+ this.owner.resizeSecondarySheet(this._defaultHeight);
+ }
+ },
+
+
+ _getExpandedHeight: function(){
+ // tags:
+ // private
+
+ return (this.naturalRowsHeight && this.naturalRowsHeight.length > 0 ? this.naturalRowsHeight[0] : 0) +
+ this.expandRendererHeight + this.verticalGap + this.verticalGap;
+ },
+
+ _layoutRenderers: function(renderData){
+ if(!this._domReady){
+ return;
+ }
+ this.inherited(arguments);
+ // make sure to show the expand/collapse renderer if no item is displayed but the row was expanded.
+ if(!renderData.items || renderData.items.length === 0){
+ this._layoutExpandRenderers(0, false, null);
+ }
+ }
+
+ });
+});
diff --cc dojox/calendar/LICENSE
index b064ab1,0000000..b064ab1
mode 100644,000000..100755
--- a/dojox/calendar/LICENSE
+++ b/dojox/calendar/LICENSE
diff --cc dojox/calendar/nls/bg/buttons.js
index 61eb2fc,0000000..61eb2fc
mode 100644,000000..100755
--- a/dojox/calendar/nls/bg/buttons.js
+++ b/dojox/calendar/nls/bg/buttons.js
diff --cc dojox/calendar/nls/he/buttons.js
index 9271cba,0000000..9271cba
mode 100644,000000..100755
--- a/dojox/calendar/nls/he/buttons.js
+++ b/dojox/calendar/nls/he/buttons.js
diff --cc dojox/calendar/nls/hr/buttons.js
index 93aadf2,0000000..93aadf2
mode 100644,000000..100755
--- a/dojox/calendar/nls/hr/buttons.js
+++ b/dojox/calendar/nls/hr/buttons.js
diff --cc dojox/calendar/nls/uk/buttons.js
index 76522dc,0000000..76522dc
mode 100644,000000..100755
--- a/dojox/calendar/nls/uk/buttons.js
+++ b/dojox/calendar/nls/uk/buttons.js
diff --cc dojox/calendar/tests/hcalendar.html
index 2721891,0000000..2721891
mode 100644,000000..100755
--- a/dojox/calendar/tests/hcalendar.html
+++ b/dojox/calendar/tests/hcalendar.html
diff --cc dojox/date/islamic/Date.js
index 51496e8,0000000..81d48a4
mode 100644,000000..100644
--- a/dojox/date/islamic/Date.js
+++ b/dojox/date/islamic/Date.js
@@@ -1,431 -1,0 +1,431 @@@
+define(["dojo/_base/lang", "dojo/_base/declare", "dojo/date"], function(lang, declare, dd){
+
+ var IDate = declare("dojox.date.islamic.Date", null, {
+ // summary:
+ // The component defines the Islamic (Hijri) Calendar Object
+ // description:
+ // This module is similar to the Date() object provided by JavaScript
+ // example:
+ // | var date = new dojox.date.islamic.Date();
+ // | document.writeln(date.getFullYear()+'\'+date.getMonth()+'\'+date.getDate());
+
+
+ _date: 0,
+ _month: 0,
+ _year: 0,
+ _hours: 0,
+ _minutes: 0,
+ _seconds: 0,
+ _milliseconds: 0,
+ _day: 0,
+ _GREGORIAN_EPOCH : 1721425.5,
+ _ISLAMIC_EPOCH : 1948439.5,
+
+ constructor: function(){
+ // summary:
+ // This is the constructor
+ // description:
+ // This function initialize the date object values
+ // example:
+ // | var date1 = new dojox.date.islamic.Date();
+ // | var date2 = new dojox.date.islamic.Date("12\2\1429");
+ // | var date3 = new dojox.date.islamic.Date(date2);
+ // | var date4 = new dojox.date.islamic.Date(1429,2,12);
+
+ var len = arguments.length;
+ if(!len){// use the current date value, added "" to the similarity to date
+ this.fromGregorian(new Date());
+ }else if(len == 1){
+ var arg0 = arguments[0];
+ if(typeof arg0 == "number"){ // this is time "valueof"
+ arg0 = new Date(arg0);
+ }
+
+ if(arg0 instanceof Date){
+ this.fromGregorian(arg0);
+ }else if(arg0 == ""){
+ // date should be invalid. Dijit relies on this behavior.
+ this._date = new Date(""); //TODO: should this be NaN? _date is not a Date object
+ }else{ // this is Islamic.Date object
+ this._year = arg0._year;
+ this._month = arg0._month;
+ this._date = arg0._date;
+ this._hours = arg0._hours;
+ this._minutes = arg0._minutes;
+ this._seconds = arg0._seconds;
+ this._milliseconds = arg0._milliseconds;
+ }
+ }else if(len >=3){
+ // YYYY MM DD arguments passed, month is from 0-12
+ this._year += arguments[0];
+ this._month += arguments[1];
+ this._date += arguments[2];
+ this._hours += arguments[3] || 0;
+ this._minutes += arguments[4] || 0;
+ this._seconds += arguments[5] || 0;
+ this._milliseconds += arguments[6] || 0;
+ }
+ },
+
+ getDate:function(){
+ // summary:
+ // This function returns the date value (1 - 30)
+ // example:
+ // | var date1 = new dojox.date.islamic.Date();
+ // | document.writeln(date1.getDate);
+ return this._date;
+ },
+
+ getMonth:function(){
+ // summary:
+ // This function return the month value ( 0 - 11 )
+ // example:
+ // | var date1 = new dojox.date.islamic.Date();
+ // | document.writeln(date1.getMonth()+1);
+
+ return this._month;
+ },
+
+ getFullYear:function(){
+ // summary:
+ // This function return the Year value
+ // example:
+ // | var date1 = new dojox.date.islamic.Date();
+ // | document.writeln(date1.getFullYear());
+
+ return this._year;
+ },
+
+ getDay:function(){
+ // summary:
+ // This function return Week Day value ( 0 - 6 )
+ // example:
+ // | var date1 = new dojox.date.islamic.Date();
+ // | document.writeln(date1.getDay());
+
+ return this.toGregorian().getDay();
+ },
+
+ getHours:function(){
+ // summary:
+ // returns the Hour value
+ return this._hours;
+ },
+
+ getMinutes:function(){
+ // summary:
+ // returns the Minutes value
+ return this._minutes;
+ },
+
+ getSeconds:function(){
+ // summary:
+ // returns the seconds value
+ return this._seconds;
+ },
+
+ getMilliseconds:function(){
+ // summary:
+ // returns the Milliseconds value
+ return this._milliseconds;
+ },
+
+ setDate: function(/*number*/date){
+ // summary:
+ // This function sets the Date
+ // example:
+ // | var date1 = new dojox.date.islamic.Date();
+ // | date1.setDate(2);
+
+ date = parseInt(date);
+
+ if(date > 0 && date <= this.getDaysInIslamicMonth(this._month, this._year)){
+ this._date = date;
+ }else{
+ var mdays;
+ if(date>0){
+ for(mdays = this.getDaysInIslamicMonth(this._month, this._year);
+ date > mdays;
+ date -= mdays,mdays =this.getDaysInIslamicMonth(this._month, this._year)){
+ this._month++;
+ if(this._month >= 12){this._year++; this._month -= 12;}
+ }
+
+ this._date = date;
+ }else{
+ for(mdays = this.getDaysInIslamicMonth((this._month-1)>=0 ?(this._month-1) :11 ,((this._month-1)>=0)? this._year: this._year-1);
+ date <= 0;
+ mdays = this.getDaysInIslamicMonth((this._month-1)>=0 ? (this._month-1) :11,((this._month-1)>=0)? this._year: this._year-1)){
+ this._month--;
+ if(this._month < 0){this._year--; this._month += 12;}
+
+ date+=mdays;
+ }
+ this._date = date;
+ }
+ }
+ return this;
+ },
+
+ setFullYear:function(/*number*/year){
+ // summary:
+ // This function set Year
+ // example:
+ // | var date1 = new dojox.date.islamic.Date();
+ // | date1.setYear(1429);
+
+ this._year = +year;
+ },
+
+ setMonth: function(/*number*/month) {
+ // summary:
+ // This function set Month
+ // example:
+ // | var date1 = new dojox.date.islamic.Date();
+ // | date1.setMonth(2);
+
+ this._year += Math.floor(month / 12);
+ if(month > 0){
+ this._month = Math.floor(month % 12);
+ }else{
+ this._month = Math.floor(((month % 12) + 12) % 12);
+ }
+ },
+
+ setHours:function(){
+ // summary:
+ // set the Hours
+ var hours_arg_no = arguments.length;
+ var hours = 0;
+ if(hours_arg_no >= 1){
+ hours = parseInt(arguments[0]);
+ }
+
+ if(hours_arg_no >= 2){
+ this._minutes = parseInt(arguments[1]);
+ }
+
+ if(hours_arg_no >= 3){
+ this._seconds = parseInt(arguments[2]);
+ }
+
+ if(hours_arg_no == 4){
+ this._milliseconds = parseInt(arguments[3]);
+ }
+
+ while(hours >= 24){
+ this._date++;
+ var mdays = this.getDaysInIslamicMonth(this._month, this._year);
+ if(this._date > mdays){
+ this._month ++;
+ if(this._month >= 12){this._year++; this._month -= 12;}
+ this._date -= mdays;
+ }
+ hours -= 24;
+ }
+ this._hours = hours;
+ },
+
+ _addMinutes: function(/*Number*/minutes){
+ minutes += this._minutes;
+ this.setMinutes(minutes);
+ this.setHours(this._hours + parseInt(minutes / 60));
+ return this;
+ },
+
+ _addSeconds: function(/*Number*/seconds){
+ seconds += this._seconds;
+ this.setSeconds(seconds);
+ this._addMinutes(parseInt(seconds / 60));
+ return this;
+ },
+
+ _addMilliseconds: function(/*Number*/milliseconds){
+ milliseconds += this._milliseconds;
+ this.setMilliseconds(milliseconds);
+ this._addSeconds(parseInt(milliseconds / 1000));
+ return this;
+ },
+
+ setMinutes: function(/*Number*/minutes){
+ // summary:
+ // sets the minutes (0-59) only.
+ this._minutes = minutes % 60;
+ return this;
+ },
+
+ setSeconds: function(/*Number*/seconds){
+ // summary:
+ // sets the seconds (0-59) only.
+ this._seconds = seconds % 60;
+ return this;
+ },
+
+ setMilliseconds: function(/*Number*/milliseconds){
+ this._milliseconds = milliseconds % 1000;
+ return this;
+ },
+
+ toString:function(){
+ // summary:
+ // This returns a string representation of the date in "DDDD MMMM DD YYYY HH:MM:SS" format
+ // example:
+ // | var date1 = new dojox.date.islamic.Date();
+ // | document.writeln(date1.toString());
+
+ //FIXME: TZ/DST issues?
+ if(isNaN(this._date)){
+ return "Invalidate Date";
+ }else{
+ var x = new Date();
+ x.setHours(this._hours);
+ x.setMinutes(this._minutes);
+ x.setSeconds(this._seconds);
+ x.setMilliseconds(this._milliseconds);
+ return this._month+" "+ this._date + " " + this._year + " " + x.toTimeString();
+ }
+ },
+
+
+ toGregorian:function(){
+ // summary:
+ // This returns the equevalent Grogorian date value in Date object
+ // example:
+ // | var dateIslamic = new dojox.date.islamic.Date(1429,11,20);
+ // | var dateGregorian = dateIslamic.toGregorian();
+
+ var hYear = this._year;
+ var hMonth = this._month;
+ var hDate = this._date;
+ var julianDay = hDate + Math.ceil(29.5 * hMonth) + (hYear - 1) * 354
+ + Math.floor((3 + (11 * hYear)) / 30) + this._ISLAMIC_EPOCH - 1;
+
+ var wjd = Math.floor(julianDay - 0.5) + 0.5,
+ depoch = wjd - this._GREGORIAN_EPOCH,
+ quadricent = Math.floor(depoch / 146097),
+ dqc = this._mod(depoch, 146097),
+ cent = Math.floor(dqc / 36524),
+ dcent = this._mod(dqc, 36524),
+ quad = Math.floor(dcent / 1461),
+ dquad = this._mod(dcent, 1461),
+ yindex = Math.floor(dquad / 365),
+ year = (quadricent * 400) + (cent * 100) + (quad * 4) + yindex;
+ if(!(cent == 4 || yindex == 4)){
+ year++;
+ }
+
+ var gYearStart = this._GREGORIAN_EPOCH + (365 * (year - 1)) + Math.floor((year - 1) / 4)
+ - ( Math.floor((year - 1) / 100)) + Math.floor((year - 1) / 400);
+
+ var yearday = wjd - gYearStart;
+
+ var tjd = (this._GREGORIAN_EPOCH - 1) + (365 * (year - 1)) + Math.floor((year - 1) / 4)
+ -( Math.floor((year - 1) / 100)) + Math.floor((year - 1) / 400) + Math.floor( (739 / 12)
+ + ( (dd.isLeapYear(new Date(year,3,1)) ? -1 : -2)) + 1);
+
+ var leapadj = ((wjd < tjd ) ? 0 : (dd.isLeapYear(new Date(year,3,1)) ? 1 : 2));
+
+ var month = Math.floor((((yearday + leapadj) * 12) + 373) / 367);
+ var tjd2 = (this._GREGORIAN_EPOCH - 1) + (365 * (year - 1))
+ + Math.floor((year - 1) / 4) - (Math.floor((year - 1) / 100))
+ + Math.floor((year - 1) / 400) + Math.floor((((367 * month) - 362) / 12)
- + ((month <= 2) ? 0 : (dd.isLeapYear(new Date(year,month,1)) ? -1 : -2)) + 1);
++ + ((month <= 2) ? 0 : (dd.isLeapYear(new Date(year,(month-1),1)) ? -1 : -2)) + 1);
+
+ var day = (wjd - tjd2) + 1;
+
+ var gdate = new Date(year, (month - 1), day, this._hours, this._minutes, this._seconds, this._milliseconds);
+
+ return gdate;
+ },
+
+ //TODO: would it make more sense to make this a constructor option? or a static?
+ // ported from the Java class com.ibm.icu.util.IslamicCalendar from ICU4J v3.6.1 at http://www.icu-project.org/
+ fromGregorian:function(/*Date*/gdate){
+ // summary:
+ // This function returns the equivalent Islamic Date value for the Gregorian Date
+ // example:
+ // | var dateIslamic = new dojox.date.islamic.Date();
+ // | var dateGregorian = new Date(2008,10,12);
+ // | dateIslamic.fromGregorian(dateGregorian);
+
+ var date = new Date(gdate);
+ var gYear = date.getFullYear(),
+ gMonth = date.getMonth(),
+ gDay = date.getDate();
+
+ var julianDay = (this._GREGORIAN_EPOCH - 1) + (365 * (gYear - 1)) + Math.floor((gYear - 1) / 4)
+ + (-Math.floor((gYear - 1) / 100)) + Math.floor((gYear - 1) / 400)
+ + Math.floor((((367 * (gMonth+1)) - 362) / 12)
+ + (((gMonth+1) <= 2) ? 0 : (dd.isLeapYear(date) ? -1 : -2)) + gDay);
+ julianDay = Math.floor(julianDay) + 0.5;
+
+ var days = julianDay - this._ISLAMIC_EPOCH;
+ var hYear = Math.floor( (30 * days + 10646) / 10631.0 );
+ var hMonth = Math.ceil((days - 29 - this._yearStart(hYear)) / 29.5 );
+ hMonth = Math.min(hMonth, 11);
+ var hDay = Math.ceil(days - this._monthStart(hYear, hMonth)) + 1;
+
+ this._date = hDay;
+ this._month = hMonth;
+ this._year = hYear;
+ this._hours = date.getHours();
+ this._minutes = date.getMinutes();
+ this._seconds = date.getSeconds();
+ this._milliseconds = date.getMilliseconds();
+ this._day = date.getDay();
+ return this;
+ },
+
+ valueOf:function(){
+ // summary:
+ // This function returns The stored time value in milliseconds
+ // since midnight, January 1, 1970 UTC
+
+ return this.toGregorian().valueOf();
+ },
+
+ // ported from the Java class com.ibm.icu.util.IslamicCalendar from ICU4J v3.6.1 at http://www.icu-project.org/
+ _yearStart:function(/*Number*/year){
+ // summary:
+ // return start of Islamic year
+ return (year-1)*354 + Math.floor((3+11*year)/30.0);
+ },
+
+ // ported from the Java class com.ibm.icu.util.IslamicCalendar from ICU4J v3.6.1 at http://www.icu-project.org/
+ _monthStart:function(/*Number*/year, /*Number*/month){
+ // summary:
+ // return the start of Islamic Month
+ return Math.ceil(29.5*month) +
+ (year-1)*354 + Math.floor((3+11*year)/30.0);
+ },
+
+ // ported from the Java class com.ibm.icu.util.IslamicCalendar from ICU4J v3.6.1 at http://www.icu-project.org/
+ _civilLeapYear:function(/*Number*/year){
+ // summary:
+ // return Boolean value if Islamic leap year
+ return (14 + 11 * year) % 30 < 11;
+ },
+
+ // ported from the Java class com.ibm.icu.util.IslamicCalendar from ICU4J v3.6.1 at http://www.icu-project.org/
+ getDaysInIslamicMonth:function(/*Number*/month, /*Number*/ year){
+ // summary:
+ // returns the number of days in the given Islamic Month
+ var length = 0;
+ length = 29 + ((month+1) % 2);
+ if(month == 11 && this._civilLeapYear(year)){
+ length++;
+ }
+ return length;
+ },
+
+ _mod:function(a, b){
+ return a - (b * Math.floor(a / b));
+ }
+});
+
+//TODOC
+IDate.getDaysInIslamicMonth = function(/*dojox/date/islamic.Date*/month){
+ return new IDate().getDaysInIslamicMonth(month.getMonth(),month.getFullYear()); // dojox.date.islamic.Date
+};
+return IDate;
+});
diff --cc dojox/date/tests/islamic/Date.js
index fe7983a,0000000..0ec599b
mode 100644,000000..100644
--- a/dojox/date/tests/islamic/Date.js
+++ b/dojox/date/tests/islamic/Date.js
@@@ -1,2112 -1,0 +1,2117 @@@
+dojo.provide("dojox.date.tests.islamic.Date");
+dojo.require("dojox.date.islamic.Date");
+dojo.require("dojox.date.islamic.locale");
+dojo.require("dojox.date.islamic");
+dojo.require("dojo.date");
+dojo.require("dojo.date.locale");
+
+dojo.requireLocalization("dojo.cldr", "gregorian");
+dojo.requireLocalization("dojo.cldr", "islamic");
+
+tests.register("dojox.date.tests.islamic.Date",
+ [
+ {
+ // Test formatting and parsing of dates in various locales pre-built in dojo.cldr
+ // NOTE: we can't set djConfig.extraLocale before bootstrapping unit tests, so directly
+ // load resources here for specific locales:
+
+ name: "setup",
+ setUp: function() {
+ var partLocaleList = ["ar","en"];
+
+ dojo.forEach(partLocaleList, function(locale) {
+ dojo.requireLocalization("dojo.cldr", "islamic", locale);
+ });
+ },
+ runTest: function(t) {
+ },
+ tearDown: function() {
+ //Clean up bundles that should not exist if
+ //the test is re-run.
+ // delete dojo.cldr.nls.islamic;
+ }
+ },
+ {
+ name: "toGregorian",
+ runTest: function(t) {
+
+ var dateTable = [[1420,0,1,1999,3,17], [1420,0,12,1999,3,28],
+ [1420,0,23,1999,4,9],
+ [1420,1,4,1999,4,20],
+ [1420,1,15,1999,4,31],
+ [1420,1,26,1999,5,11],
+ [1420,2,8,1999,5,22],
+ [1420,2,19,1999,6,3],
+ [1420,2,30,1999,6,14],
+ [1420,3,11,1999,6,25],
+ [1420,3,22,1999,7,5],
+ [1420,4,4,1999,7,16],
+ [1420,4,15,1999,7,27],
+ [1420,4,26,1999,8,7],
+ [1420,5,7,1999,8,18],
+ [1420,5,18,1999,8,29],
+ [1420,5,29,1999,9,10],
+ [1420,6,11,1999,9,21],
+ [1420,6,22,1999,10,1],
+ [1420,7,3,1999,10,12],
+ [1420,7,14,1999,10,23],
+ [1420,8,29,2000,0,6],
+ [1420,9,10,2000,0,17],
+ [1420,9,21,2000,0,28],
+ [1420,10,3,2000,1,8],
+ [1420,10,14,2000,1,19],
+ [1420,10,25,2000,2,1],
+ [1420,11,6,2000,2,12],
+ [1420,11,17,2000,2,23],
+ [1420,11,28,2000,3,3],
+ [1421,0,9,2000,3,14],
+ [1421,0,20,2000,3,25],
+ [1421,1,1,2000,4,6],
+ [1421,1,12,2000,4,17],
+ [1421,1,23,2000,4,28],
+ [1421,2,5,2000,5,8],
+ [1421,2,16,2000,5,19],
+ [1421,2,27,2000,5,30],
+ [1421,3,8,2000,6,11],
+ [1421,3,19,2000,6,22],
+ [1421,4,1,2000,7,2],
+ [1421,4,12,2000,7,13],
+ [1421,4,23,2000,7,24],
+ [1421,5,4,2000,8,4],
+ [1421,5,15,2000,8,15],
+ [1421,5,26,2000,8,26],
+ [1421,6,8,2000,9,7],
+ [1421,6,19,2000,9,18],
+ [1421,6,30,2000,9,29],
+ [1421,7,11,2000,10,9],
+ [1421,7,22,2000,10,20],
+ [1421,9,7,2001,0,3],
+ [1421,9,18,2001,0,14],
+ [1421,9,7,2001,0,3],
+ [1421,9,18,2001,0,14],
+ [1421,9,29,2001,0,25],
+ [1421,10,11,2001,1,5],
+ [1421,10,22,2001,1,16],
+ [1421,11,3,2001,1,27],
+ [1421,11,14,2001,2,10],
+ [1421,11,25,2001,2,21],
+ [1422,0,7,2001,3,1],
+ [1422,0,18,2001,3,12],
+ [1422,0,29,2001,3,23],
+ [1422,1,10,2001,4,4],
+ [1422,1,21,2001,4,15],
+ [1422,2,3,2001,4,26],
+ [1422,2,14,2001,5,6],
+ [1422,2,25,2001,5,17],
+ [1422,3,6,2001,5,28],
+ [1422,3,17,2001,6,9],
+ [1422,3,28,2001,6,20],
+ [1422,4,10,2001,6,31],
+ [1422,4,21,2001,7,11],
+ [1422,5,2,2001,7,22],
+ [1422,5,13,2001,8,2],
+ [1422,5,24,2001,8,13],
+ [1422,6,6,2001,8,24],
+ [1422,6,17,2001,9,5],
+ [1422,6,28,2001,9,16],
+ [1422,7,9,2001,9,27],
+ [1422,7,20,2001,10,7],
+ [1422,8,2,2001,10,18],
+ [1422,8,13,2001,10,29],
+ [1422,8,24,2001,11,10],
+ [1422,9,5,2001,11,21],
+ [1422,9,16,2002,0,1],
+ [1422,9,27,2002,0,12],
+ [1422,10,9,2002,0,23],
+ [1422,10,20,2002,1,3],
+ [1422,11,1,2002,1,14],
+ [1422,11,12,2002,1,25],
+ [1422,11,23,2002,2,8],
+ [1423,0,5,2002,2,19],
+ [1423,0,16,2002,2,30],
+ [1423,0,27,2002,3,10],
+ [1423,1,8,2002,3,21],
+ [1423,1,19,2002,4,2],
+ [1423,2,1,2002,4,13],
+ [1423,2,12,2002,4,24],
+ [1423,2,23,2002,5,4],
+ [1423,3,4,2002,5,15],
+ [1423,3,15,2002,5,26],
+ [1423,3,26,2002,6,7],
+ [1423,4,8,2002,6,18],
+ [1423,4,19,2002,6,29],
+ [1423,4,30,2002,7,9],
+ [1423,5,11,2002,7,20],
+ [1423,5,22,2002,7,31],
+ [1423,6,4,2002,8,11],
+ [1423,6,15,2002,8,22],
+ [1423,6,26,2002,9,3],
+ [1423,7,7,2002,9,14],
+ [1423,7,18,2002,9,25],
+ [1423,7,29,2002,10,5],
+ [1423,8,11,2002,10,16],
+ [1423,8,22,2002,10,27],
+ [1423,9,3,2002,11,8],
+ [1423,9,14,2002,11,19],
+ [1423,9,25,2002,11,30],
+ [1423,10,7,2003,0,10],
+ [1423,10,18,2003,0,21],
+ [1423,10,29,2003,1,1],
+ [1423,11,10,2003,1,12],
+ [1423,11,21,2003,1,23],
+ [1424,0,2,2003,2,6],
+ [1424,0,13,2003,2,17],
+ [1424,0,24,2003,2,28],
+ [1424,1,5,2003,3,8],
+ [1424,1,16,2003,3,19],
+ [1424,1,27,2003,3,30],
+ [1424,2,9,2003,4,11],
+ [1424,2,20,2003,4,22],
+ [1424,3,1,2003,5,2],
+ [1424,3,12,2003,5,13],
+ [1424,3,23,2003,5,24],
+ [1424,4,5,2003,6,5],
+ [1424,4,16,2003,6,16],
+ [1424,4,27,2003,6,27],
+ [1424,5,8,2003,7,7],
+ [1424,5,19,2003,7,18],
+ [1424,6,1,2003,7,29],
+ [1424,6,12,2003,8,9],
+ [1424,6,23,2003,8,20],
+ [1424,7,4,2003,9,1],
+ [1424,7,15,2003,9,12],
+ [1424,7,26,2003,9,23],
+ [1424,8,8,2003,10,3],
+ [1424,8,19,2003,10,14],
+ [1424,8,30,2003,10,25],
+ [1424,10,15,2004,0,8],
+ [1424,10,26,2004,0,19],
+ [1424,11,7,2004,0,30],
+ [1424,11,18,2004,1,10],
+ [1424,11,29,2004,1,21],
+ [1425,0,11,2004,2,3],
+ [1425,0,22,2004,2,14],
+ [1425,1,3,2004,2,25],
+ [1425,1,14,2004,3,5],
+ [1425,1,25,2004,3,16],
+ [1425,2,7,2004,3,27],
+ [1425,2,18,2004,4,8],
+ [1425,2,29,2004,4,19],
+ [1425,3,10,2004,4,30],
+ [1425,3,21,2004,5,10],
+ [1425,4,3,2004,5,21],
+ [1425,4,14,2004,6,2],
+ [1425,4,25,2004,6,13],
+ [1425,5,6,2004,6,24],
+ [1425,5,17,2004,7,4],
+ [1425,5,28,2004,7,15],
+ [1425,6,10,2004,7,26],
+ [1425,6,21,2004,8,6],
+ [1425,7,2,2004,8,17],
+ [1425,7,13,2004,8,28],
+ [1425,7,24,2004,9,9],
+ [1425,8,6,2004,9,20],
+ [1425,8,17,2004,9,31],
+ [1425,8,28,2004,10,11],
+ [1425,9,9,2004,10,22],
+ [1425,10,24,2005,0,5],
+ [1425,11,5,2005,0,16],
+ [1425,11,16,2005,0,27],
+ [1425,11,27,2005,1,7],
+ [1426,0,9,2005,1,18],
+ [1426,0,20,2005,2,1],
+ [1426,1,1,2005,2,12],
+ [1426,1,12,2005,2,23],
+ [1426,1,23,2005,3,3],
+ [1426,2,5,2005,3,14],
+ [1426,2,16,2005,3,25],
+ [1426,2,27,2005,4,6],
+ [1426,3,8,2005,4,17],
+ [1426,3,19,2005,4,28],
+ [1426,4,1,2005,5,8],
+ [1426,4,12,2005,5,19],
+ [1426,4,23,2005,5,30],
+ [1426,5,4,2005,6,11],
+ [1426,5,15,2005,6,22],
+ [1426,5,26,2005,7,2],
+ [1426,6,8,2005,7,13],
+ [1426,6,19,2005,7,24],
+ [1426,6,30,2005,8,4],
+ [1426,7,11,2005,8,15],
+ [1426,7,22,2005,8,26],
+ [1426,8,4,2005,9,7],
+ [1426,8,15,2005,9,18],
+ [1426,8,26,2005,9,29],
+ [1426,9,7,2005,10,9],
+ [1426,9,18,2005,10,20],
+ [1426,9,29,2005,11,1],
+ [1426,10,11,2005,11,12],
+ [1426,10,22,2005,11,23],
+ [1426,11,3,2006,0,3],
+ [1426,11,14,2006,0,14],
+ [1426,11,25,2006,0,25],
+ [1427,0,6,2006,1,5],
+ [1427,0,17,2006,1,16],
+ [1427,0,28,2006,1,27],
+ [1427,1,9,2006,2,10],
+ [1427,1,20,2006,2,21],
+ [1427,2,2,2006,3,1],
+ [1427,2,13,2006,3,12],
+ [1427,2,24,2006,3,23],
+ [1427,3,5,2006,4,4],
+ [1427,3,16,2006,4,15],
+ [1427,3,27,2006,4,26],
+ [1427,4,9,2006,5,6],
+ [1427,4,20,2006,5,17],
+ [1427,5,1,2006,5,28],
+ [1427,5,12,2006,6,9],
+ [1427,5,23,2006,6,20],
+ [1427,6,5,2006,6,31],
+ [1427,6,16,2006,7,11],
+ [1427,6,27,2006,7,22],
+ [1427,7,8,2006,8,2],
+ [1427,7,19,2006,8,13],
+ [1427,8,1,2006,8,24],
+ [1427,8,12,2006,9,5],
+ [1427,8,23,2006,9,16],
+ [1427,9,4,2006,9,27],
+ [1427,9,15,2006,10,7],
+ [1427,9,26,2006,10,18],
+ [1427,10,8,2006,10,29],
+ [1427,10,19,2006,11,10],
+ [1427,10,30,2006,11,21],
+ [1427,11,11,2007,0,1],
+ [1427,11,22,2007,0,12],
+ [1428,0,4,2007,0,23],
+ [1428,0,15,2007,1,3],
+ [1428,0,26,2007,1,14],
+ [1428,1,7,2007,1,25],
+ [1428,1,18,2007,2,8],
+ [1428,1,29,2007,2,19],
+ [1428,2,11,2007,2,30],
+ [1428,2,22,2007,3,10],
+ [1428,3,3,2007,3,21],
+ [1428,3,14,2007,4,2],
+ [1428,3,25,2007,4,13],
+ [1428,4,7,2007,4,24],
+ [1428,4,18,2007,5,4],
+ [1428,4,29,2007,5,15],
+ [1428,5,10,2007,5,26],
+ [1428,5,21,2007,6,7],
+ [1428,6,3,2007,6,18],
+ [1428,6,14,2007,6,29],
+ [1428,6,25,2007,7,9],
+ [1428,7,6,2007,7,20],
+ [1428,7,17,2007,7,31],
+ [1428,7,28,2007,8,11],
+ [1428,8,10,2007,8,22],
+ [1428,8,21,2007,9,3],
+ [1428,9,2,2007,9,14],
+ [1428,9,13,2007,9,25],
+ [1428,9,24,2007,10,5],
+ [1428,10,6,2007,10,16],
+ [1428,10,17,2007,10,27],
+ [1429,0,1,2008,0,10],
+ [1429,0,12,2008,0,21],
+ [1429,0,23,2008,1,1],
+ [1429,1,4,2008,1,12],
+ [1429,1,15,2008,1,23],
+ [1429,1,26,2008,2,5],
+ [1429,2,8,2008,2,16],
+ [1429,2,19,2008,2,27],
+ [1429,2,30,2008,3,7],
+ [1429,3,11,2008,3,18],
+ [1429,3,22,2008,3,29],
+ [1429,4,4,2008,4,10],
+ [1429,4,15,2008,4,21],
+ [1429,4,26,2008,5,1],
+ [1429,5,7,2008,5,12],
+ [1429,5,18,2008,5,23],
+ [1429,5,29,2008,6,4],
+ [1429,6,11,2008,6,15],
+ [1429,6,22,2008,6,26],
+ [1429,7,3,2008,7,6],
+ [1429,7,14,2008,7,17],
+ [1429,7,25,2008,7,28],
+ [1429,8,7,2008,8,8],
+ [1429,8,18,2008,8,19],
+ [1429,8,29,2008,8,30],
+ [1429,9,10,2008,9,11],
+ [1429,9,21,2008,9,22],
+ [1429,10,3,2008,10,2],
+ [1429,10,14,2008,10,13],
+ [1429,10,25,2008,10,24],
+ [1430,0,10,2009,0,7],
+ [1430,0,21,2009,0,18],
+ [1430,1,2,2009,0,29],
+ [1430,1,13,2009,1,9],
+ [1430,1,24,2009,1,20],
+ [1430,2,6,2009,2,3],
+ [1430,2,17,2009,2,14],
+ [1430,2,28,2009,2,25],
+ [1430,3,9,2009,3,5],
+ [1430,3,20,2009,3,16],
+ [1430,4,2,2009,3,27],
+ [1430,4,13,2009,4,8],
+ [1430,4,24,2009,4,19],
+ [1430,5,5,2009,4,30],
+ [1430,5,16,2009,5,10],
+ [1430,5,27,2009,5,21],
+ [1430,6,9,2009,6,2],
+ [1430,6,20,2009,6,13],
+ [1430,7,1,2009,6,24],
+ [1430,7,12,2009,7,4],
+ [1430,7,23,2009,7,15],
+ [1430,8,5,2009,7,26],
+ [1430,8,16,2009,8,6],
+ [1430,8,27,2009,8,17],
+ [1430,9,8,2009,8,28],
+ [1430,9,19,2009,9,9],
+ [1430,10,1,2009,9,20],
+ [1430,10,12,2009,9,31],
+ [1430,10,23,2009,10,11],
+ [1430,11,4,2009,10,22],
+ [1430,11,15,2009,11,3],
+ [1430,11,26,2009,11,14],
+ [1431,0,8,2009,11,25],
+ [1431,0,19,2010,0,5],
+ [1431,0,30,2010,0,16],
+ [1431,1,11,2010,0,27],
+ [1431,1,22,2010,1,7],
+ [1431,2,4,2010,1,18],
+ [1431,2,15,2010,2,1],
+ [1431,2,26,2010,2,12],
+ [1431,3,7,2010,2,23],
+ [1431,3,18,2010,3,3],
+ [1431,3,29,2010,3,14],
+ [1431,4,11,2010,3,25],
+ [1431,4,22,2010,4,6],
+ [1431,5,3,2010,4,17],
+ [1431,5,14,2010,4,28],
+ [1431,5,25,2010,5,8],
+ [1431,6,7,2010,5,19],
+ [1431,6,18,2010,5,30],
+ [1431,6,29,2010,6,11],
+ [1431,7,10,2010,6,22],
+ [1431,7,21,2010,7,2],
+ [1431,8,3,2010,7,13],
+ [1431,8,14,2010,7,24],
+ [1431,8,25,2010,8,4],
+ [1431,9,6,2010,8,15],
+ [1431,9,17,2010,8,26],
+ [1431,9,28,2010,9,7],
+ [1431,10,10,2010,9,18],
+ [1431,10,21,2010,9,29],
+ [1431,11,2,2010,10,9],
+ [1431,11,13,2010,10,20],
+ [1431,11,24,2010,11,1],
+ [1432,0,5,2010,11,12],
+ [1432,0,16,2010,11,23],
+ [1432,0,27,2011,0,3],
+ [1432,1,8,2011,0,14],
+ [1432,1,19,2011,0,25],
+ [1432,2,1,2011,1,5],
+ [1432,2,12,2011,1,16],
+ [1432,2,23,2011,1,27],
+ [1432,3,4,2011,2,10],
+ [1432,3,15,2011,2,21],
+ [1432,3,26,2011,3,1],
+ [1432,4,8,2011,3,12],
+ [1432,4,19,2011,3,23],
+ [1432,4,30,2011,4,4],
+ [1432,5,11,2011,4,15],
+ [1432,5,22,2011,4,26],
+ [1432,6,4,2011,5,6],
+ [1432,6,15,2011,5,17],
+ [1432,6,26,2011,5,28],
+ [1432,7,7,2011,6,9],
+ [1432,7,18,2011,6,20],
+ [1432,7,29,2011,6,31],
+ [1432,8,11,2011,7,11],
+ [1432,8,22,2011,7,22],
+ [1432,9,3,2011,8,2],
+ [1432,9,14,2011,8,13],
+ [1432,9,25,2011,8,24],
+ [1432,10,7,2011,9,5],
+ [1432,10,18,2011,9,16],
+ [1432,10,29,2011,9,27],
+ [1432,11,10,2011,10,7],
+ [1432,11,21,2011,10,18],
+ [1433,0,3,2011,10,29],
+ [1433,1,6,2012,0,1],
+ [1433,1,17,2012,0,12],
+ [1433,1,28,2012,0,23],
+ [1433,2,10,2012,1,3],
+ [1433,2,21,2012,1,14],
+ [1433,3,2,2012,1,25],
+ [1433,3,13,2012,2,7],
+ [1433,3,24,2012,2,18],
+ [1433,4,6,2012,2,29],
+ [1433,4,17,2012,3,9],
+ [1433,4,28,2012,3,20],
+ [1433,5,9,2012,4,1],
+ [1433,5,20,2012,4,12],
+ [1433,6,2,2012,4,23],
+ [1433,6,13,2012,5,3],
+ [1433,6,24,2012,5,14],
+ [1433,7,5,2012,5,25],
+ [1433,7,16,2012,6,6],
+ [1433,7,27,2012,6,17],
+ [1433,8,9,2012,6,28],
+ [1433,8,20,2012,7,8],
+ [1433,9,1,2012,7,19],
+ [1433,9,12,2012,7,30],
+ [1433,9,23,2012,8,10],
+ [1433,10,5,2012,8,21],
+ [1433,10,16,2012,9,2],
+ [1433,10,27,2012,9,13],
+ [1433,11,8,2012,9,24],
+ [1433,11,19,2012,10,4],
+ [1434,0,1,2012,10,15],
+ [1434,0,12,2012,10,26],
+ [1434,1,26,2013,0,9],
+ [1434,2,8,2013,0,20],
+ [1434,2,19,2013,0,31],
+ [1434,2,30,2013,1,11],
+ [1434,3,11,2013,1,22],
+ [1434,3,22,2013,2,5],
+ [1434,4,4,2013,2,16],
+ [1434,4,15,2013,2,27],
+ [1434,4,26,2013,3,7],
+ [1434,5,7,2013,3,18],
+ [1434,5,18,2013,3,29],
+ [1434,5,29,2013,4,10],
+ [1434,6,11,2013,4,21],
+ [1434,6,22,2013,5,1],
+ [1434,7,3,2013,5,12],
+ [1434,7,14,2013,5,23],
+ [1434,7,25,2013,6,4],
+ [1434,8,7,2013,6,15],
+ [1434,8,18,2013,6,26],
+ [1434,8,29,2013,7,6],
+ [1434,9,10,2013,7,17],
+ [1434,9,21,2013,7,28],
+ [1434,10,3,2013,8,8],
+ [1434,10,14,2013,8,19],
+ [1434,10,25,2013,8,30],
+ [1434,11,6,2013,9,11],
+ [1434,11,17,2013,9,22],
+ [1434,11,28,2013,10,2],
+ [1435,0,9,2013,10,13],
+ [1435,0,20,2013,10,24],
+ [1435,1,1,2013,11,5],
+ [1435,1,12,2013,11,16],
+ [1435,1,23,2013,11,27],
+ [1435,2,5,2014,0,7],
+ [1435,2,16,2014,0,18],
+ [1435,2,27,2014,0,29],
+ [1435,3,8,2014,1,9],
+ [1435,3,19,2014,1,20],
+ [1435,4,1,2014,2,3],
+ [1435,4,12,2014,2,14],
+ [1435,4,23,2014,2,25],
+ [1435,5,4,2014,3,5],
+ [1435,5,15,2014,3,16],
+ [1435,5,26,2014,3,27],
+ [1435,6,8,2014,4,8],
+ [1435,6,19,2014,4,19],
+ [1435,6,30,2014,4,30],
+ [1435,7,11,2014,5,10],
+ [1435,7,22,2014,5,21],
+ [1435,8,4,2014,6,2],
+ [1435,8,15,2014,6,13],
+ [1435,8,26,2014,6,24],
+ [1435,9,7,2014,7,4],
+ [1435,9,18,2014,7,15],
+ [1435,9,29,2014,7,26],
+ [1435,10,11,2014,8,6],
+ [1435,10,22,2014,8,17],
+ [1435,11,3,2014,8,28],
+ [1435,11,14,2014,9,9],
+ [1435,11,25,2014,9,20],
+ [1436,0,7,2014,9,31],
+ [1436,0,18,2014,10,11],
+ [1436,0,29,2014,10,22],
+ [1436,1,10,2014,11,3],
+ [1436,1,21,2014,11,14],
+ [1436,2,3,2014,11,25],
+ [1436,2,14,2015,0,5],
+ [1436,2,25,2015,0,16],
+ [1436,3,6,2015,0,27],
+ [1436,3,17,2015,1,7],
+ [1436,3,28,2015,1,18],
+ [1436,4,10,2015,2,1],
+ [1436,4,21,2015,2,12],
+ [1436,5,2,2015,2,23],
+ [1436,5,13,2015,3,3],
+ [1436,5,24,2015,3,14],
+ [1436,6,6,2015,3,25],
+ [1436,6,17,2015,4,6],
+ [1436,6,28,2015,4,17],
+ [1436,7,9,2015,4,28],
+ [1436,7,20,2015,5,8],
+ [1436,8,2,2015,5,19],
+ [1436,8,13,2015,5,30],
+ [1436,8,24,2015,6,11],
+ [1436,9,5,2015,6,22],
+ [1436,9,16,2015,7,2],
+ [1436,9,27,2015,7,13],
+ [1436,10,9,2015,7,24],
+ [1436,10,20,2015,8,4],
+ [1436,11,1,2015,8,15],
+ [1436,11,12,2015,8,26],
+ [1436,11,23,2015,9,7],
+ [1437,0,4,2015,9,18],
+ [1437,0,15,2015,9,29],
+ [1437,0,26,2015,10,9],
+ [1437,1,7,2015,10,20],
+ [1437,2,22,2016,0,3],
+ [1437,3,3,2016,0,14],
+ [1437,3,14,2016,0,25],
+ [1437,3,25,2016,1,5],
+ [1437,4,7,2016,1,16],
+ [1437,4,18,2016,1,27],
+ [1437,4,29,2016,2,9],
+ [1437,5,10,2016,2,20],
+ [1437,5,21,2016,2,31],
+ [1437,6,3,2016,3,11],
+ [1437,6,14,2016,3,22],
+ [1437,6,25,2016,4,3],
+ [1437,7,6,2016,4,14],
+ [1437,7,17,2016,4,25],
+ [1437,7,28,2016,5,5],
+ [1437,8,10,2016,5,16],
+ [1437,8,21,2016,5,27],
+ [1437,9,2,2016,6,8],
+ [1437,9,13,2016,6,19],
+ [1437,9,24,2016,6,30],
+ [1437,10,6,2016,7,10],
+ [1437,10,17,2016,7,21],
+ [1437,10,28,2016,8,1],
+ [1437,11,9,2016,8,12],
+ [1437,11,20,2016,8,23],
+ [1438,0,2,2016,9,4],
+ [1438,0,13,2016,9,15],
+ [1438,0,24,2016,9,26],
+ [1438,1,5,2016,10,6],
+ [1438,1,16,2016,10,17],
+ [1438,1,27,2016,10,28],
+ [1438,3,12,2017,0,11],
+ [1438,3,23,2017,0,22],
+ [1438,4,5,2017,1,2],
+ [1438,4,16,2017,1,13],
+ [1438,4,27,2017,1,24],
+ [1438,5,8,2017,2,7],
+ [1438,5,19,2017,2,18],
+ [1438,6,1,2017,2,29],
+ [1438,6,12,2017,3,9],
+ [1438,6,23,2017,3,20],
+ [1438,7,4,2017,4,1],
+ [1438,7,15,2017,4,12],
+ [1438,7,26,2017,4,23],
+ [1438,8,8,2017,5,3],
+ [1438,8,19,2017,5,14],
+ [1438,8,30,2017,5,25],
+ [1438,9,11,2017,6,6],
+ [1438,9,22,2017,6,17],
+ [1438,10,4,2017,6,28],
+ [1438,10,15,2017,7,8],
+ [1438,10,26,2017,7,19],
+ [1438,11,7,2017,7,30],
+ [1438,11,18,2017,8,10],
+ [1438,11,29,2017,8,21],
+ [1439,0,11,2017,9,2],
+ [1439,0,22,2017,9,13],
+ [1439,1,3,2017,9,24],
+ [1439,1,14,2017,10,4],
+ [1439,1,25,2017,10,15],
+ [1439,2,7,2017,10,26],
+ [1439,2,18,2017,11,7],
+ [1439,2,29,2017,11,18],
+ [1439,3,10,2017,11,29],
+ [1439,3,21,2018,0,9],
+ [1439,4,3,2018,0,20],
+ [1439,4,14,2018,0,31],
+ [1439,4,25,2018,1,11],
+ [1439,5,6,2018,1,22],
+ [1439,5,17,2018,2,5],
+ [1439,5,28,2018,2,16],
+ [1439,6,10,2018,2,27],
+ [1439,6,21,2018,3,7],
+ [1439,7,2,2018,3,18],
+ [1439,7,13,2018,3,29],
+ [1439,7,24,2018,4,10],
+ [1439,8,6,2018,4,21],
+ [1439,8,17,2018,5,1],
+ [1439,8,28,2018,5,12],
+ [1439,9,9,2018,5,23],
+ [1439,9,20,2018,6,4],
+ [1439,10,2,2018,6,15],
+ [1439,10,13,2018,6,26],
+ [1439,10,24,2018,7,6],
+ [1439,11,5,2018,7,17],
+ [1439,11,16,2018,7,28],
+ [1439,11,27,2018,8,8],
+ [1440,0,8,2018,8,19],
+ [1440,0,19,2018,8,30],
+ [1440,0,30,2018,9,11],
+ [1440,1,11,2018,9,22],
+ [1440,1,22,2018,10,2],
+ [1440,2,4,2018,10,13],
+ [1440,2,15,2018,10,24],
+ [1440,2,26,2018,11,5],
+ [1440,3,7,2018,11,16],
+ [1440,3,18,2018,11,27],
+ [1440,3,29,2019,0,7],
+ [1440,4,11,2019,0,18],
+ [1440,4,22,2019,0,29],
+ [1440,5,3,2019,1,9],
+ [1440,5,14,2019,1,20],
+ [1440,5,25,2019,2,3],
+ [1440,6,7,2019,2,14],
+ [1440,6,18,2019,2,25],
+ [1440,6,29,2019,3,5],
+ [1440,7,10,2019,3,16],
+ [1440,7,21,2019,3,27],
+ [1440,8,3,2019,4,8],
+ [1440,8,14,2019,4,19],
+ [1440,8,25,2019,4,30],
+ [1440,9,6,2019,5,10],
+ [1440,9,17,2019,5,21],
+ [1440,9,28,2019,6,2],
+ [1440,10,10,2019,6,13],
+ [1440,10,21,2019,6,24],
+ [1440,11,2,2019,7,4],
+ [1440,11,13,2019,7,15],
+ [1440,11,24,2019,7,26],
+ [1441,0,6,2019,8,6],
+ [1441,0,17,2019,8,17],
+ [1441,0,28,2019,8,28],
+ [1441,1,9,2019,9,9],
+ [1441,1,20,2019,9,20],
+ [1441,2,2,2019,9,31],
+ [1441,2,13,2019,10,11],
+ [1441,2,24,2019,10,22],
+ [1441,4,9,2020,0,5],
+ [1441,4,20,2020,0,16],
+ [1441,5,1,2020,0,27],
+ [1441,5,12,2020,1,7],
+ [1441,5,23,2020,1,18],
+ [1441,6,5,2020,1,29],
+ [1441,6,16,2020,2,11],
+ [1441,6,27,2020,2,22],
+ [1441,7,8,2020,3,2],
+ [1441,7,19,2020,3,13],
+ [1441,8,1,2020,3,24],
+ [1441,8,12,2020,4,5],
+ [1441,8,23,2020,4,16],
+ [1441,9,4,2020,4,27],
+ [1441,9,15,2020,5,7],
+ [1441,9,26,2020,5,18],
+ [1441,10,8,2020,5,29],
+ [1441,10,19,2020,6,10],
+ [1441,10,30,2020,6,21],
+ [1441,11,11,2020,7,1],
+ [1441,11,22,2020,7,12],
+ [1442,0,4,2020,7,23],
+ [1442,0,15,2020,8,3],
+ [1442,0,26,2020,8,14],
+ [1442,1,7,2020,8,25],
+ [1442,1,18,2020,9,6],
+ [1442,1,29,2020,9,17],
+ [1442,2,11,2020,9,28],
+ [1442,2,22,2020,10,8],
+ [1442,3,3,2020,10,19],
+ [1442,3,14,2020,10,30],
+ [1442,4,18,2021,0,2],
+ [1442,4,29,2021,0,13],
+ [1442,5,10,2021,0,24],
+ [1442,5,21,2021,1,4],
+ [1442,6,3,2021,1,15],
+ [1442,6,14,2021,1,26],
+ [1442,6,25,2021,2,9],
+ [1442,7,6,2021,2,20],
+ [1442,7,17,2021,2,31],
+ [1442,7,28,2021,3,11],
+ [1442,8,10,2021,3,22],
+ [1442,8,21,2021,4,3],
+ [1442,9,2,2021,4,14],
+ [1442,9,13,2021,4,25],
+ [1442,9,24,2021,5,5],
+ [1442,10,6,2021,5,16],
+ [1442,10,17,2021,5,27],
+ [1442,10,28,2021,6,8],
+ [1442,11,9,2021,6,19],
+ [1442,11,20,2021,6,30],
+ [1443,0,1,2021,7,10],
+ [1443,0,12,2021,7,21],
+ [1443,0,23,2021,8,1],
+ [1443,1,4,2021,8,12],
+ [1443,1,15,2021,8,23],
+ [1443,1,26,2021,9,4],
+ [1443,2,8,2021,9,15],
+ [1443,2,19,2021,9,26],
+ [1443,2,30,2021,10,6],
+ [1443,3,11,2021,10,17],
+ [1443,3,22,2021,10,28],
+ [1443,5,7,2022,0,11],
+ [1443,5,18,2022,0,22],
+ [1443,5,29,2022,1,2],
+ [1443,6,11,2022,1,13],
+ [1443,6,22,2022,1,24],
+ [1443,7,3,2022,2,7],
+ [1443,7,14,2022,2,18],
+ [1443,7,25,2022,2,29],
+ [1443,8,7,2022,3,9],
+ [1443,8,18,2022,3,20],
+ [1443,8,29,2022,4,1],
+ [1443,9,10,2022,4,12],
+ [1443,9,21,2022,4,23],
+ [1443,10,3,2022,5,3],
+ [1443,10,14,2022,5,14],
+ [1443,10,25,2022,5,25],
+ [1443,11,6,2022,6,6],
+ [1443,11,17,2022,6,17],
+ [1443,11,28,2022,6,28],
+ [1444,0,10,2022,7,8],
+ [1444,0,21,2022,7,19],
+ [1444,1,2,2022,7,30],
+ [1444,1,13,2022,8,10],
+ [1444,1,24,2022,8,21],
+ [1444,2,6,2022,9,2],
+ [1444,2,17,2022,9,13],
+ [1444,2,28,2022,9,24],
+ [1444,3,9,2022,10,4],
+ [1444,3,20,2022,10,15],
+ [1444,4,2,2022,10,26],
+ [1444,5,16,2023,0,9],
+ [1444,5,27,2023,0,20],
+ [1444,6,9,2023,0,31],
+ [1444,6,20,2023,1,11],
+ [1444,7,1,2023,1,22],
+ [1444,7,12,2023,2,5],
+ [1444,7,23,2023,2,16],
+ [1444,8,5,2023,2,27],
+ [1444,8,16,2023,3,7],
+ [1444,8,27,2023,3,18],
+ [1444,9,8,2023,3,29],
+ [1444,9,19,2023,4,10],
+ [1444,10,1,2023,4,21],
+ [1444,10,12,2023,5,1],
+ [1444,10,23,2023,5,12],
+ [1444,11,4,2023,5,23],
+ [1444,11,15,2023,6,4],
+ [1444,11,26,2023,6,15],
+ [1445,0,8,2023,6,26],
+ [1445,0,19,2023,7,6],
+ [1445,0,30,2023,7,17],
+ [1445,1,11,2023,7,28],
+ [1445,1,22,2023,8,8],
+ [1445,2,4,2023,8,19],
+ [1445,2,15,2023,8,30],
+ [1445,2,26,2023,9,11],
+ [1445,3,7,2023,9,22],
+ [1445,3,18,2023,10,2],
+ [1445,3,29,2023,10,13],
+ [1445,4,11,2023,10,24],
+ [1445,5,25,2024,0,7],
+ [1445,6,7,2024,0,18],
+ [1445,6,18,2024,0,29],
+ [1445,6,29,2024,1,9],
+ [1445,7,10,2024,1,20],
+ [1445,7,21,2024,2,2],
+ [1445,8,3,2024,2,13],
+ [1445,8,14,2024,2,24],
+ [1445,8,25,2024,3,4],
+ [1445,9,6,2024,3,15],
+ [1445,9,17,2024,3,26],
+ [1445,9,28,2024,4,7],
+ [1445,10,10,2024,4,18],
+ [1445,10,21,2024,4,29],
+ [1445,11,2,2024,5,9],
+ [1445,11,13,2024,5,20],
+ [1445,11,24,2024,6,1],
+ [1446,0,5,2024,6,12],
+ [1446,0,16,2024,6,23],
+ [1446,0,27,2024,7,3],
+ [1446,1,8,2024,7,14],
+ [1446,1,19,2024,7,25],
+ [1446,2,1,2024,8,5],
+ [1446,2,12,2024,8,16],
+ [1446,2,23,2024,8,27],
+ [1446,3,4,2024,9,8],
+ [1446,3,15,2024,9,19],
+ [1446,3,26,2024,9,30],
+ [1446,4,8,2024,10,10],
+ [1446,4,19,2024,10,21],
+ [1446,6,4,2025,0,4],
+ [1446,6,15,2025,0,15],
+ [1446,6,26,2025,0,26],
+ [1446,7,7,2025,1,6],
+ [1446,7,18,2025,1,17],
+ [1446,7,29,2025,1,28],
+ [1446,8,11,2025,2,11],
+ [1446,8,22,2025,2,22],
+ [1446,9,3,2025,3,2],
+ [1446,9,14,2025,3,13],
+ [1446,9,25,2025,3,24],
+ [1446,10,7,2025,4,5],
+ [1446,10,18,2025,4,16],
+ [1446,10,29,2025,4,27],
+ [1446,11,10,2025,5,7],
+ [1446,11,21,2025,5,18],
+ [1447,0,3,2025,5,29],
+ [1447,0,14,2025,6,10],
+ [1447,0,25,2025,6,21],
+ [1447,1,6,2025,7,1],
+ [1447,1,17,2025,7,12],
+ [1447,1,28,2025,7,23],
+ [1447,2,10,2025,8,3],
+ [1447,2,21,2025,8,14],
+ [1447,3,2,2025,8,25],
+ [1447,3,13,2025,9,6],
+ [1447,3,24,2025,9,17],
+ [1447,4,6,2025,9,28],
+ [1447,4,17,2025,10,8],
+ [1447,4,28,2025,10,19],
+ [1447,5,9,2025,10,30],
+ [1447,6,13,2026,0,2],
+ [1447,6,24,2026,0,13],
+ [1447,7,5,2026,0,24],
+ [1447,7,16,2026,1,4],
+ [1447,7,27,2026,1,15],
+ [1447,8,9,2026,1,26],
+ [1447,8,20,2026,2,9],
+ [1447,9,1,2026,2,20],
+ [1447,9,12,2026,2,31],
+ [1447,9,23,2026,3,11],
+ [1447,10,5,2026,3,22],
+ [1447,10,16,2026,4,3],
+ [1447,10,27,2026,4,14],
+ [1447,11,8,2026,4,25],
+ [1447,11,19,2026,5,5],
+ [1447,11,30,2026,5,16],
+ [1448,0,11,2026,5,27],
+ [1448,0,22,2026,6,8],
+ [1448,1,3,2026,6,19],
+ [1448,1,14,2026,6,30],
+ [1448,1,25,2026,7,10],
+ [1448,2,7,2026,7,21],
- [1448,2,18,2026,8,1]
++ [1448,2,18,2026,8,1],
++ //test a month before a leap year
++ [1420,7,22,1999,11,1],
++ [1424,9,6,2003,11,1],
++ [1428,10,21,2007,11,1],
++ [1433,0,5,2011,11,1]
+
+ ];
+
+ var idate,gdate;
+
+ dojo.forEach(dateTable, function(d, i) {
+
+ console.debug(d, "at index", i);
+
+ idate = new dojox.date.islamic.Date(d[0],d[1],d[2], 15, 15, 10);
+ gdate = idate.toGregorian();
+
+ t.is(0, dojo.date.compare(new Date(d[3], d[4], d[5], 15, 15, 10), gdate, "date"));
+ });
+
+ }
+
+ },
+ {
+ name: "fromGregorian",
+ runTest: function(t) {
+
+ var dateTable = [
+
+ [1997,1,1,1417,8,23],
+ [1997,1,12,1417,9,4],
+ [1997,1,23,1417,9,15] ,
+ [1997,2,6,1417,9,26],
+ [1997,2,17,1417,10,8],
+ [1997,2,28,1417,10,19],
+ [1997,3,8,1417,10,30],
+ [1997,3,19,1417,11,11],
+ [1997,3,30,1417,11,22],
+ [1997,4,11,1418,0,3],
+ [1997,4,22,1418,0,14],
+ [1997,5,2,1418,0,25],
+ [1997,5,13,1418,1,6],
+ [1997,5,24,1418,1,17],
+ [1997,6,5,1418,1,28],
+ [1997,6,16,1418,2,10],
+ [1997,6,27,1418,2,21],
+ [1997,7,7,1418,3,2],
+ [1997,7,18,1418,3,13],
+ [1997,7,29,1418,3,24],
+ [1997,8,9,1418,4,6],
+ [1997,8,20,1418,4,17],
+ [1997,9,1,1418,4,28],
+ [1997,9,12,1418,5,9],
+ [1997,9,23,1418,5,20],
+ [1997,10,3,1418,6,2],
+ [1997,10,14,1418,6,13],
+ [1997,10,25,1418,6,24],
+ [1997,11,6,1418,7,5],
+ [1997,11,17,1418,7,16],
+ [1997,11,28,1418,7,27],
+ [1998,0,8,1418,8,9],
+ [1998,0,19,1418,8,20],
+ [1998,0,30,1418,9,1],
+ [1998,1,10,1418,9,12],
+ [1998,1,21,1418,9,23],
+ [1998,2,4,1418,10,5],
+ [1998,2,15,1418,10,16],
+ [1998,2,26,1418,10,27],
+ [1998,3,6,1418,11,8],
+ [1998,3,17,1418,11,19],
+ [1998,3,28,1419,0,1],
+ [1998,4,9,1419,0,12],
+ [1998,4,20,1419,0,23],
+ [1998,4,31,1419,1,4],
+ [1998,5,11,1419,1,15],
+ [1998,5,22,1419,1,26],
+ [1998,6,3,1419,2,8],
+ [1998,6,14,1419,2,19],
+ [1998,6,25,1419,2,30],
+ [1998,7,5,1419,3,11],
+ [1998,7,16,1419,3,22],
+ [1998,7,27,1419,4,4],
+ [1998,8,7,1419,4,15],
+ [1998,8,18,1419,4,26],
+ [1998,8,29,1419,5,7],
+ [1998,9,10,1419,5,18],
+ [1998,9,21,1419,5,29],
+ [1998,10,1,1419,6,11],
+ [1998,10,12,1419,6,22],
+ [1998,10,23,1419,7,3],
+ [1998,11,4,1419,7,14],
+ [1998,11,15,1419,7,25],
+ [1998,11,26,1419,8,7],
+ [1999,0,6,1419,8,18],
+ [1999,0,17,1419,8,29],
+ [1999,0,28,1419,9,10],
+ [1999,1,8,1419,9,21],
+ [1999,1,19,1419,10,3],
+ [1999,2,2,1419,10,14],
+ [1999,2,13,1419,10,25],
+ [1999,2,24,1419,11,6],
+ [1999,3,4,1419,11,17],
+ [1999,3,15,1419,11,28],
+ [1999,3,26,1420,0,10],
+ [1999,4,7,1420,0,21],
+ [1999,4,18,1420,1,2],
+ [1999,4,29,1420,1,13],
+ [1999,5,9,1420,1,24],
+ [1999,5,20,1420,2,6],
+ [1999,6,1,1420,2,17],
+ [1999,6,12,1420,2,28],
+ [1999,6,23,1420,3,9],
+ [1999,7,3,1420,3,20],
+ [1999,7,14,1420,4,2],
+ [1999,7,25,1420,4,13],
+ [1999,8,5,1420,4,24],
+ [1999,8,16,1420,5,5],
+ [1999,8,27,1420,5,16],
+ [1999,9,8,1420,5,27],
+ [1999,9,19,1420,6,9],
+ [1999,9,30,1420,6,20],
+ [1999,10,10,1420,7,1],
+ [1999,10,21,1420,7,12],
+ [1999,11,2,1420,7,23],
+ [1999,11,13,1420,8,5],
+ [1999,11,24,1420,8,16],
+ [2000,0,4,1420,8,27],
+ [2000,0,15,1420,9,8],
+ [2000,0,26,1420,9,19],
+ [2000,1,6,1420,10,1],
+ [2000,1,17,1420,10,12],
+ [2000,1,28,1420,10,23],
+ [2000,2,10,1420,11,4],
+ [2000,2,21,1420,11,15],
+ [2000,3,1,1420,11,26],
+ [2000,3,12,1421,0,7],
+ [2000,3,23,1421,0,18],
+ [2000,4,4,1421,0,29],
+ [2000,4,15,1421,1,10],
+ [2000,4,26,1421,1,21],
+ [2000,5,6,1421,2,3],
+ [2000,5,17,1421,2,14],
+ [2000,5,28,1421,2,25],
+ [2000,6,9,1421,3,6],
+ [2000,6,20,1421,3,17],
+ [2000,6,31,1421,3,28],
+ [2000,7,11,1421,4,10],
+ [2000,7,22,1421,4,21],
+ [2000,8,2,1421,5,2],
+ [2000,8,13,1421,5,13],
+ [2000,8,24,1421,5,24],
+ [2000,9,5,1421,6,6],
+ [2000,9,16,1421,6,17],
+ [2000,9,27,1421,6,28],
+ [2000,10,7,1421,7,9],
+ [2000,10,18,1421,7,20],
+ [2000,10,29,1421,8,2],
+ [2000,11,10,1421,8,13],
+ [2000,11,21,1421,8,24],
+ [2001,0,1,1421,9,5],
+ [2001,0,12,1421,9,16],
+ [2001,0,23,1421,9,27],
+ [2001,1,3,1421,10,9],
+ [2001,1,14,1421,10,20],
+ [2001,1,25,1421,11,1],
+ [2001,2,8,1421,11,12],
+ [2001,2,19,1421,11,23],
+ [2001,2,30,1422,0,5],
+ [2001,3,10,1422,0,16],
+ [2001,3,21,1422,0,27],
+ [2001,4,2,1422,1,8],
+ [2001,4,13,1422,1,19],
+ [2001,4,24,1422,2,1],
+ [2001,5,4,1422,2,12],
+ [2001,5,15,1422,2,23],
+ [2001,5,26,1422,3,4],
+ [2001,6,7,1422,3,15],
+ [2001,6,18,1422,3,26],
+ [2001,6,29,1422,4,8],
+ [2001,7,9,1422,4,19],
+ [2001,7,20,1422,4,30],
+ [2001,7,31,1422,5,11],
+ [2001,8,11,1422,5,22],
+ [2001,8,22,1422,6,4],
+ [2001,9,3,1422,6,15],
+ [2001,9,14,1422,6,26],
+ [2001,9,25,1422,7,7],
+ [2001,10,5,1422,7,18],
+ [2001,10,16,1422,7,29],
+ [2001,10,27,1422,8,11],
+ [2001,11,8,1422,8,22],
+ [2001,11,19,1422,9,3],
+ [2001,11,30,1422,9,14],
+ [2002,0,10,1422,9,25],
+ [2002,0,21,1422,10,7],
+ [2002,1,1,1422,10,18],
+ [2002,1,12,1422,10,29],
+ [2002,1,23,1422,11,10],
+ [2002,2,6,1422,11,21],
+ [2002,2,17,1423,0,3],
+ [2002,2,28,1423,0,14],
+ [2002,3,8,1423,0,25],
+ [2002,3,19,1423,1,6],
+ [2002,3,30,1423,1,17],
+ [2002,4,11,1423,1,28],
+ [2002,4,22,1423,2,10],
+ [2002,5,2,1423,2,21],
+ [2002,5,13,1423,3,2],
+ [2002,5,24,1423,3,13],
+ [2002,6,5,1423,3,24],
+ [2002,6,16,1423,4,6],
+ [2002,6,27,1423,4,17],
+ [2002,7,7,1423,4,28],
+ [2002,7,18,1423,5,9],
+ [2002,7,29,1423,5,20],
+ [2002,8,9,1423,6,2],
+ [2002,8,20,1423,6,13],
+ [2002,9,1,1423,6,24],
+ [2002,9,12,1423,7,5],
+ [2002,9,23,1423,7,16],
+ [2002,10,3,1423,7,27],
+ [2002,10,14,1423,8,9],
+ [2002,10,25,1423,8,20],
+ [2002,11,6,1423,9,1],
+ [2002,11,17,1423,9,12],
+ [2002,11,28,1423,9,23],
+ [2003,0,8,1423,10,5],
+ [2003,0,19,1423,10,16],
+ [2003,0,30,1423,10,27],
+ [2003,1,10,1423,11,8],
+ [2003,1,21,1423,11,19],
+ [2003,2,4,1423,11,30],
+ [2003,2,15,1424,0,11],
+ [2003,2,26,1424,0,22],
+ [2003,3,6,1424,1,3],
+ [2003,3,17,1424,1,14],
+ [2003,3,28,1424,1,25],
+ [2003,4,9,1424,2,7],
+ [2003,4,20,1424,2,18],
+ [2003,4,31,1424,2,29],
+ [2003,5,11,1424,3,10],
+ [2003,5,22,1424,3,21],
+ [2003,6,3,1424,4,3],
+ [2003,6,14,1424,4,14],
+ [2003,6,25,1424,4,25],
+ [2003,7,5,1424,5,6],
+ [2003,7,16,1424,5,17],
+ [2003,7,27,1424,5,28],
+ [2003,8,7,1424,6,10],
+ [2003,8,18,1424,6,21],
+ [2003,8,29,1424,7,2],
+ [2003,9,10,1424,7,13],
+ [2003,9,21,1424,7,24],
+ [2003,10,1,1424,8,6],
+ [2003,10,12,1424,8,17],
+ [2003,10,23,1424,8,28],
+ [2003,11,4,1424,9,9],
+ [2003,11,15,1424,9,20],
+ [2003,11,26,1424,10,2],
+ [2004,0,6,1424,10,13],
+ [2004,0,17,1424,10,24],
+ [2004,0,28,1424,11,5],
+ [2004,1,8,1424,11,16],
+ [2004,1,19,1424,11,27],
+ [2004,2,1,1425,0,9],
+ [2004,2,12,1425,0,20],
+ [2004,2,23,1425,1,1],
+ [2004,3,3,1425,1,12],
+ [2004,3,14,1425,1,23],
+ [2004,3,25,1425,2,5],
+ [2004,4,6,1425,2,16],
+ [2004,4,17,1425,2,27],
+ [2004,4,28,1425,3,8],
+ [2004,5,8,1425,3,19],
+ [2004,5,19,1425,4,1],
+ [2004,5,30,1425,4,12],
+ [2004,6,11,1425,4,23],
+ [2004,6,22,1425,5,4],
+ [2004,7,2,1425,5,15],
+ [2004,7,13,1425,5,26],
+ [2004,7,24,1425,6,8],
+ [2004,8,4,1425,6,19],
+ [2004,8,15,1425,6,30],
+ [2004,8,26,1425,7,11],
+ [2004,9,7,1425,7,22],
+ [2004,9,18,1425,8,4],
+ [2004,9,29,1425,8,15],
+ [2004,10,9,1425,8,26],
+ [2004,10,20,1425,9,7],
+ [2004,11,1,1425,9,18],
+ [2004,11,12,1425,9,29],
+ [2004,11,23,1425,10,11],
+ [2005,0,3,1425,10,22],
+ [2005,0,14,1425,11,3],
+ [2005,0,25,1425,11,14],
+ [2005,1,5,1425,11,25],
+ [2005,1,16,1426,0,7],
+ [2005,1,27,1426,0,18],
+ [2005,2,10,1426,0,29],
+ [2005,2,21,1426,1,10],
+ [2005,3,1,1426,1,21],
+ [2005,3,12,1426,2,3],
+ [2005,3,23,1426,2,14],
+ [2005,4,4,1426,2,25],
+ [2005,4,15,1426,3,6],
+ [2005,4,26,1426,3,17],
+ [2005,5,6,1426,3,28],
+ [2005,5,17,1426,4,10],
+ [2005,5,28,1426,4,21],
+ [2005,6,9,1426,5,2],
+ [2005,6,20,1426,5,13],
+ [2005,6,31,1426,5,24],
+ [2005,7,11,1426,6,6],
+ [2005,7,22,1426,6,17],
+ [2005,8,2,1426,6,28],
+ [2005,8,13,1426,7,9],
+ [2005,8,24,1426,7,20],
+ [2005,9,5,1426,8,2],
+ [2005,9,16,1426,8,13],
+ [2005,9,27,1426,8,24],
+ [2005,10,7,1426,9,5],
+ [2005,10,18,1426,9,16],
+ [2005,10,29,1426,9,27],
+ [2005,11,10,1426,10,9],
+ [2005,11,21,1426,10,20],
+ [2006,0,1,1426,11,1],
+ [2006,0,12,1426,11,12],
+ [2006,0,23,1426,11,23],
+ [2006,1,3,1427,0,4],
+ [2006,1,14,1427,0,15],
+ [2006,1,25,1427,0,26],
+ [2006,2,8,1427,1,7],
+ [2006,2,19,1427,1,18],
+ [2006,2,30,1427,1,29],
+ [2006,3,10,1427,2,11],
+ [2006,3,21,1427,2,22],
+ [2006,4,2,1427,3,3],
+ [2006,4,13,1427,3,14],
+ [2006,4,24,1427,3,25],
+ [2006,5,4,1427,4,7],
+ [2006,5,15,1427,4,18],
+ [2006,5,26,1427,4,29],
+ [2006,6,7,1427,5,10],
+ [2006,6,18,1427,5,21],
+ [2006,6,29,1427,6,3],
+ [2006,7,9,1427,6,14],
+ [2006,7,20,1427,6,25],
+ [2006,7,31,1427,7,6],
+ [2006,8,11,1427,7,17],
+ [2006,8,22,1427,7,28],
+ [2006,9,3,1427,8,10],
+ [2006,9,14,1427,8,21],
+ [2006,9,25,1427,9,2],
+ [2006,10,5,1427,9,13],
+ [2006,10,16,1427,9,24],
+ [2006,10,27,1427,10,6],
+ [2006,11,8,1427,10,17],
+ [2006,11,19,1427,10,28],
+ [2006,11,30,1427,11,9],
+ [2007,0,10,1427,11,20],
+ [2007,0,21,1428,0,2],
+ [2007,1,1,1428,0,13],
+ [2007,1,12,1428,0,24],
+ [2007,1,23,1428,1,5],
+ [2007,2,6,1428,1,16],
+ [2007,2,17,1428,1,27],
+ [2007,2,28,1428,2,9],
+ [2007,3,8,1428,2,20],
+ [2007,3,19,1428,3,1],
+ [2007,3,30,1428,3,12],
+ [2007,4,11,1428,3,23],
+ [2007,4,22,1428,4,5],
+ [2007,5,2,1428,4,16],
+ [2007,5,13,1428,4,27],
+ [2007,5,24,1428,5,8],
+ [2007,6,5,1428,5,19],
+ [2007,6,16,1428,6,1],
+ [2007,6,27,1428,6,12],
+ [2007,7,7,1428,6,23],
+ [2007,7,18,1428,7,4],
+ [2007,7,29,1428,7,15],
+ [2007,8,9,1428,7,26],
+ [2007,8,20,1428,8,8],
+ [2007,9,1,1428,8,19],
+ [2007,9,12,1428,8,30],
+ [2007,9,23,1428,9,11],
+ [2007,10,3,1428,9,22],
+ [2007,10,14,1428,10,4],
+ [2007,10,25,1428,10,15],
+ [2007,11,6,1428,10,26],
+ [2007,11,17,1428,11,7],
+ [2007,11,28,1428,11,18],
+ [2008,0,8,1428,11,29],
+ [2008,0,19,1429,0,10],
+ [2008,0,30,1429,0,21],
+ [2008,1,10,1429,1,2],
+ [2008,1,21,1429,1,13],
+ [2008,2,3,1429,1,24],
+ [2008,2,14,1429,2,6],
+ [2008,2,25,1429,2,17],
+ [2008,3,5,1429,2,28],
+ [2008,3,16,1429,3,9],
+ [2008,3,27,1429,3,20],
+ [2008,4,8,1429,4,2],
+ [2008,4,19,1429,4,13],
+ [2008,4,30,1429,4,24],
+ [2008,5,10,1429,5,5],
+ [2008,5,21,1429,5,16],
+ [2008,6,2,1429,5,27],
+ [2008,6,13,1429,6,9],
+ [2008,6,24,1429,6,20],
+ [2008,7,4,1429,7,1],
+ [2008,7,15,1429,7,12],
+ [2008,7,26,1429,7,23],
+ [2008,8,6,1429,8,5],
+ [2008,8,17,1429,8,16],
+ [2008,8,28,1429,8,27],
+ [2008,9,9,1429,9,8],
+ [2008,9,20,1429,9,19],
+ [2008,9,31,1429,10,1],
+ [2008,10,11,1429,10,12],
+ [2008,10,22,1429,10,23],
+ [2008,11,3,1429,11,4],
+ [2008,11,14,1429,11,15],
+ [2008,11,25,1429,11,26],
+ [2009,0,5,1430,0,8],
+ [2009,0,16,1430,0,19],
+ [2009,0,27,1430,0,30],
+ [2009,1,7,1430,1,11],
+ [2009,1,18,1430,1,22],
+ [2009,2,1,1430,2,4],
+ [2009,2,12,1430,2,15],
+ [2009,2,23,1430,2,26],
+ [2009,3,3,1430,3,7],
+ [2009,3,14,1430,3,18],
+ [2009,3,25,1430,3,29],
+ [2009,4,6,1430,4,11],
+ [2009,4,17,1430,4,22],
+ [2009,4,28,1430,5,3],
+ [2009,5,8,1430,5,14],
+ [2009,5,19,1430,5,25],
+ [2009,5,30,1430,6,7],
+ [2009,6,11,1430,6,18],
+ [2009,6,22,1430,6,29],
+ [2009,7,2,1430,7,10],
+ [2009,7,13,1430,7,21],
+ [2009,7,24,1430,8,3],
+ [2009,8,4,1430,8,14],
+ [2009,8,15,1430,8,25],
+ [2009,8,26,1430,9,6],
+ [2009,9,7,1430,9,17],
+ [2009,9,18,1430,9,28],
+ [2009,9,29,1430,10,10],
+ [2009,10,9,1430,10,21],
+ [2009,10,20,1430,11,2],
+ [2009,11,1,1430,11,13],
+ [2009,11,12,1430,11,24],
+ [2009,11,23,1431,0,6],
+ [2010,0,3,1431,0,17],
+ [2010,0,14,1431,0,28],
+ [2010,0,25,1431,1,9],
+ [2010,1,5,1431,1,20],
+ [2010,1,16,1431,2,2],
+ [2010,1,27,1431,2,13],
+ [2010,2,10,1431,2,24],
+ [2010,2,21,1431,3,5],
+ [2010,3,1,1431,3,16],
+ [2010,3,12,1431,3,27],
+ [2010,3,23,1431,4,9],
+ [2010,4,4,1431,4,20],
+ [2010,4,15,1431,5,1],
+ [2010,4,26,1431,5,12],
+ [2010,5,6,1431,5,23],
+ [2010,5,17,1431,6,5],
+ [2010,5,28,1431,6,16],
+ [2010,6,9,1431,6,27],
+ [2010,6,20,1431,7,8],
+ [2010,6,31,1431,7,19],
+ [2010,7,11,1431,8,1],
+ [2010,7,22,1431,8,12],
+ [2010,8,2,1431,8,23],
+ [2010,8,13,1431,9,4],
+ [2010,8,24,1431,9,15],
+ [2010,9,5,1431,9,26],
+ [2010,9,16,1431,10,8],
+ [2010,9,27,1431,10,19],
+ [2010,10,7,1431,10,30],
+ [2010,10,18,1431,11,11],
+ [2010,10,29,1431,11,22],
+ [2010,11,10,1432,0,3],
+ [2010,11,21,1432,0,14],
+ [2011,0,1,1432,0,25],
+ [2011,0,12,1432,1,6],
+ [2011,0,23,1432,1,17],
+ [2011,1,3,1432,1,28],
+ [2011,1,14,1432,2,10],
+ [2011,1,25,1432,2,21],
+ [2011,2,8,1432,3,2],
+ [2011,2,19,1432,3,13],
+ [2011,2,30,1432,3,24],
+ [2011,3,10,1432,4,6],
+ [2011,3,21,1432,4,17],
+ [2011,4,2,1432,4,28],
+ [2011,4,13,1432,5,9],
+ [2011,4,24,1432,5,20],
+ [2011,5,4,1432,6,2],
+ [2011,5,15,1432,6,13],
+ [2011,5,26,1432,6,24],
+ [2011,6,7,1432,7,5],
+ [2011,6,18,1432,7,16],
+ [2011,6,29,1432,7,27],
+ [2011,7,9,1432,8,9],
+ [2011,7,20,1432,8,20],
+ [2011,7,31,1432,9,1],
+ [2011,8,11,1432,9,12],
+ [2011,8,22,1432,9,23],
+ [2011,9,3,1432,10,5],
+ [2011,9,14,1432,10,16],
+ [2011,9,25,1432,10,27],
+ [2011,10,5,1432,11,8],
+ [2011,10,16,1432,11,19],
+ [2011,10,27,1433,0,1],
+ [2011,11,8,1433,0,12],
+ [2011,11,19,1433,0,23],
+ [2011,11,30,1433,1,4],
+ [2012,0,10,1433,1,15],
+ [2012,0,21,1433,1,26],
+ [2012,1,1,1433,2,8],
+ [2012,1,12,1433,2,19],
+ [2012,1,23,1433,2,30],
+ [2012,2,5,1433,3,11],
+ [2012,2,16,1433,3,22],
+ [2012,2,27,1433,4,4],
+ [2012,3,7,1433,4,15],
+ [2012,3,18,1433,4,26],
+ [2012,3,29,1433,5,7],
+ [2012,4,10,1433,5,18],
+ [2012,4,21,1433,5,29],
+ [2012,5,1,1433,6,11],
+ [2012,5,12,1433,6,22],
+ [2012,5,23,1433,7,3],
+ [2012,6,4,1433,7,14],
+ [2012,6,15,1433,7,25],
+ [2012,6,26,1433,8,7],
+ [2012,7,6,1433,8,18],
+ [2012,7,17,1433,8,29],
+ [2012,7,28,1433,9,10],
+ [2012,8,8,1433,9,21],
+ [2012,8,19,1433,10,3],
+ [2012,8,30,1433,10,14],
+ [2012,9,11,1433,10,25],
+ [2012,9,22,1433,11,6],
+ [2012,10,2,1433,11,17],
+ [2012,10,13,1433,11,28],
+ [2012,10,24,1434,0,10],
+ [2012,11,5,1434,0,21],
+ [2012,11,16,1434,1,2],
+ [2012,11,27,1434,1,13],
+ [2013,0,7,1434,1,24],
+ [2013,0,18,1434,2,6],
+ [2013,0,29,1434,2,17],
+ [2013,1,9,1434,2,28],
+ [2013,1,20,1434,3,9],
+ [2013,2,3,1434,3,20],
+ [2013,2,14,1434,4,2],
+ [2013,2,25,1434,4,13],
+ [2013,3,5,1434,4,24],
+ [2013,3,16,1434,5,5],
+ [2013,3,27,1434,5,16],
+ [2013,4,8,1434,5,27],
+ [2013,4,19,1434,6,9],
+ [2013,4,30,1434,6,20],
+ [2013,5,10,1434,7,1],
+ [2013,5,21,1434,7,12],
+ [2013,6,2,1434,7,23],
+ [2013,6,13,1434,8,5],
+ [2013,6,24,1434,8,16],
+ [2013,7,4,1434,8,27],
+ [2013,7,15,1434,9,8],
+ [2013,7,26,1434,9,19],
+ [2013,8,6,1434,10,1],
+ [2013,8,17,1434,10,12],
+ [2013,8,28,1434,10,23],
+ [2013,9,9,1434,11,4],
+ [2013,9,20,1434,11,15],
+ [2013,9,31,1434,11,26],
+ [2013,10,11,1435,0,7],
+ [2013,10,22,1435,0,18],
+ [2013,11,3,1435,0,29],
+ [2013,11,14,1435,1,10],
+ [2013,11,25,1435,1,21],
+ [2014,0,5,1435,2,3],
+ [2014,0,16,1435,2,14],
+ [2014,0,27,1435,2,25],
+ [2014,1,7,1435,3,6],
+ [2014,1,18,1435,3,17],
+ [2014,2,1,1435,3,28],
+ [2014,2,12,1435,4,10],
+ [2014,2,23,1435,4,21],
+ [2014,3,3,1435,5,2],
+ [2014,3,14,1435,5,13],
+ [2014,3,25,1435,5,24],
+ [2014,4,6,1435,6,6],
+ [2014,4,17,1435,6,17],
+ [2014,4,28,1435,6,28],
+ [2014,5,8,1435,7,9],
+ [2014,5,19,1435,7,20],
+ [2014,5,30,1435,8,2],
+ [2014,6,11,1435,8,13],
+ [2014,6,22,1435,8,24],
+ [2014,7,2,1435,9,5],
+ [2014,7,13,1435,9,16],
+ [2014,7,24,1435,9,27],
+ [2014,8,4,1435,10,9],
+ [2014,8,15,1435,10,20],
+ [2014,8,26,1435,11,1],
+ [2014,9,7,1435,11,12],
+ [2014,9,18,1435,11,23],
+ [2014,9,29,1436,0,5],
+ [2014,10,9,1436,0,16],
+ [2014,10,20,1436,0,27],
+ [2014,11,1,1436,1,8],
+ [2014,11,12,1436,1,19],
+ [2014,11,23,1436,2,1],
+ [2015,0,3,1436,2,12],
+ [2015,0,14,1436,2,23],
+ [2015,0,25,1436,3,4],
+ [2015,1,5,1436,3,15],
+ [2015,1,16,1436,3,26],
+ [2015,1,27,1436,4,8],
+ [2015,2,10,1436,4,19],
+ [2015,2,21,1436,4,30],
+ [2015,3,1,1436,5,11],
+ [2015,3,12,1436,5,22],
+ [2015,3,23,1436,6,4],
+ [2015,4,4,1436,6,15],
+ [2015,4,15,1436,6,26],
+ [2015,4,26,1436,7,7],
+ [2015,5,6,1436,7,18],
+ [2015,5,17,1436,7,29],
+ [2015,5,28,1436,8,11],
+ [2015,6,9,1436,8,22],
+ [2015,6,20,1436,9,3],
+ [2015,6,31,1436,9,14],
+ [2015,7,11,1436,9,25],
+ [2015,7,22,1436,10,7],
+ [2015,8,2,1436,10,18],
+ [2015,8,13,1436,10,29],
+ [2015,8,24,1436,11,10],
+ [2015,9,5,1436,11,21],
+ [2015,9,16,1437,0,2],
+ [2015,9,27,1437,0,13],
+ [2015,10,7,1437,0,24],
+ [2015,10,18,1437,1,5],
+ [2015,10,29,1437,1,16],
+ [2015,11,10,1437,1,27],
+ [2015,11,21,1437,2,9],
+ [2016,0,1,1437,2,20],
+ [2016,0,12,1437,3,1],
+ [2016,0,23,1437,3,12],
+ [2016,1,3,1437,3,23],
+ [2016,1,14,1437,4,5],
+ [2016,1,25,1437,4,16],
+ [2016,2,7,1437,4,27],
+ [2016,2,18,1437,5,8],
+ [2016,2,29,1437,5,19],
+ [2016,3,9,1437,6,1],
+ [2016,3,20,1437,6,12],
+ [2016,4,1,1437,6,23],
+ [2016,4,12,1437,7,4],
+ [2016,4,23,1437,7,15],
+ [2016,5,3,1437,7,26],
+ [2016,5,14,1437,8,8],
+ [2016,5,25,1437,8,19],
+ [2016,6,6,1437,8,30],
+ [2016,6,17,1437,9,11],
+ [2016,6,28,1437,9,22],
+ [2016,7,8,1437,10,4],
+ [2016,7,19,1437,10,15],
+ [2016,7,30,1437,10,26],
+ [2016,8,10,1437,11,7],
+ [2016,8,21,1437,11,18],
+ [2016,9,2,1437,11,29],
+ [2016,9,13,1438,0,11],
+ [2016,9,24,1438,0,22],
+ [2016,10,4,1438,1,3],
+ [2016,10,15,1438,1,14],
+ [2016,10,26,1438,1,25],
+ [2016,11,7,1438,2,7],
+ [2016,11,18,1438,2,18],
+ [2016,11,29,1438,2,29],
+ [2017,0,9,1438,3,10],
+ [2017,0,20,1438,3,21],
+ [2017,0,31,1438,4,3],
+ [2017,1,11,1438,4,14],
+ [2017,1,22,1438,4,25],
+ [2017,2,5,1438,5,6],
+ [2017,2,16,1438,5,17],
+ [2017,2,27,1438,5,28],
+ [2017,3,7,1438,6,10],
+ [2017,3,18,1438,6,21],
+ [2017,3,29,1438,7,2],
+ [2017,4,10,1438,7,13],
+ [2017,4,21,1438,7,24],
+ [2017,5,1,1438,8,6],
+ [2017,5,12,1438,8,17],
+ [2017,5,23,1438,8,28],
+ [2017,6,4,1438,9,9],
+ [2017,6,15,1438,9,20],
+ [2017,6,26,1438,10,2],
+ [2017,7,6,1438,10,13],
+ [2017,7,17,1438,10,24],
+ [2017,7,28,1438,11,5],
+ [2017,8,8,1438,11,16],
+ [2017,8,19,1438,11,27],
+ [2017,8,30,1439,0,9],
+ [2017,9,11,1439,0,20],
+ [2017,9,22,1439,1,1],
+ [2017,10,2,1439,1,12],
+ [2017,10,13,1439,1,23],
+ [2017,10,24,1439,2,5],
+ [2017,11,5,1439,2,16],
+ [2017,11,16,1439,2,27],
+ [2017,11,27,1439,3,8],
+ [2018,0,7,1439,3,19],
+ [2018,0,18,1439,4,1],
+ [2018,0,29,1439,4,12],
+ [2018,1,9,1439,4,23],
+ [2018,1,20,1439,5,4],
+ [2018,2,3,1439,5,15],
+ [2018,2,14,1439,5,26],
+ [2018,2,25,1439,6,8],
+ [2018,3,5,1439,6,19],
+ [2018,3,16,1439,6,30],
+ [2018,3,27,1439,7,11],
+ [2018,4,8,1439,7,22],
+ [2018,4,19,1439,8,4],
+ [2018,4,30,1439,8,15],
+ [2018,5,10,1439,8,26],
+ [2018,5,21,1439,9,7],
+ [2018,6,2,1439,9,18],
+ [2018,6,13,1439,9,29],
+ [2018,6,24,1439,10,11],
+ [2018,7,4,1439,10,22],
+ [2018,7,15,1439,11,3],
+ [2018,7,26,1439,11,14],
+ [2018,8,6,1439,11,25],
+ [2018,8,17,1440,0,6],
+ [2018,8,28,1440,0,17],
+ [2018,9,9,1440,0,28],
+ [2018,9,20,1440,1,9],
+ [2018,9,31,1440,1,20],
+ [2018,10,11,1440,2,2],
+ [2018,10,22,1440,2,13],
+ [2018,11,3,1440,2,24],
+ [2018,11,14,1440,3,5],
+ [2018,11,25,1440,3,16],
+ [2019,0,5,1440,3,27],
+ [2019,0,16,1440,4,9],
+ [2019,0,27,1440,4,20],
+ [2019,1,7,1440,5,1],
+ [2019,1,18,1440,5,12],
+ [2019,2,1,1440,5,23],
+ [2019,2,12,1440,6,5],
+ [2019,2,23,1440,6,16],
+ [2019,3,3,1440,6,27],
+ [2019,3,14,1440,7,8],
+ [2019,3,25,1440,7,19],
+ [2019,4,6,1440,8,1],
+ [2019,4,17,1440,8,12],
+ [2019,4,28,1440,8,23],
+ [2019,5,8,1440,9,4],
+ [2019,5,19,1440,9,15],
+ [2019,5,30,1440,9,26],
+ [2019,6,11,1440,10,8],
+ [2019,6,22,1440,10,19],
+ [2019,7,2,1440,10,30],
+ [2019,7,13,1440,11,11],
+ [2019,7,24,1440,11,22],
+ [2019,8,4,1441,0,4],
+ [2019,8,15,1441,0,15],
+ [2019,8,26,1441,0,26],
+ [2019,9,7,1441,1,7],
+ [2019,9,18,1441,1,18],
+ [2019,9,29,1441,1,29],
+ [2019,10,9,1441,2,11],
+ [2019,10,20,1441,2,22],
+ [2019,11,1,1441,3,3],
+ [2019,11,12,1441,3,14],
+ [2019,11,23,1441,3,25],
+ [2020,0,3,1441,4,7],
+ [2020,0,14,1441,4,18],
+ [2020,0,25,1441,4,29],
+ [2020,1,5,1441,5,10],
+ [2020,1,16,1441,5,21],
+ [2020,1,27,1441,6,3],
+ [2020,2,9,1441,6,14],
+ [2020,2,20,1441,6,25],
+ [2020,2,31,1441,7,6],
+ [2020,3,11,1441,7,17],
+ [2020,3,22,1441,7,28],
+ [2020,4,3,1441,8,10],
+ [2020,4,14,1441,8,21],
+ [2020,4,25,1441,9,2],
+ [2020,5,5,1441,9,13],
+ [2020,5,16,1441,9,24],
+ [2020,5,27,1441,10,6],
+ [2020,6,8,1441,10,17],
+ [2020,6,19,1441,10,28],
+ [2020,6,30,1441,11,9],
+ [2020,7,10,1441,11,20],
+ [2020,7,21,1442,0,2],
+ [2020,8,1,1442,0,13],
+ [2020,8,12,1442,0,24],
+ [2020,8,23,1442,1,5],
+ [2020,9,4,1442,1,16],
+ [2020,9,15,1442,1,27],
+ [2020,9,26,1442,2,9],
+ [2020,10,6,1442,2,20],
+ [2020,10,17,1442,3,1],
+ [2020,10,28,1442,3,12],
+ [2020,11,9,1442,3,23],
+ [2020,11,20,1442,4,5],
+ [2020,11,31,1442,4,16],
+ [2021,0,11,1442,4,27],
+ [2021,0,22,1442,5,8],
+ [2021,1,2,1442,5,19],
+ [2021,1,13,1442,6,1],
+ [2021,1,24,1442,6,12],
+ [2021,2,7,1442,6,23],
+ [2021,2,18,1442,7,4],
+ [2021,2,29,1442,7,15],
+ [2021,3,9,1442,7,26],
+ [2021,3,20,1442,8,8],
+ [2021,4,1,1442,8,19],
+ [2021,4,12,1442,8,30],
+ [2021,4,23,1442,9,11],
+ [2021,5,3,1442,9,22],
+ [2021,5,14,1442,10,4],
+ [2021,5,25,1442,10,15],
+ [2021,6,6,1442,10,26],
+ [2021,6,17,1442,11,7],
+ [2021,6,28,1442,11,18],
+ [2021,7,8,1442,11,29],
+ [2021,7,19,1443,0,10],
+ [2021,7,30,1443,0,21],
+ [2021,8,10,1443,1,2],
+ [2021,8,21,1443,1,13],
+ [2021,9,2,1443,1,24],
+ [2021,9,13,1443,2,6],
+ [2021,9,24,1443,2,17],
+ [2021,10,4,1443,2,28],
+ [2021,10,15,1443,3,9],
+ [2021,10,26,1443,3,20],
+ [2021,11,7,1443,4,2],
+ [2021,11,18,1443,4,13],
+ [2021,11,29,1443,4,24],
+ [2022,0,9,1443,5,5],
+ [2022,0,20,1443,5,16],
+ [2022,0,31,1443,5,27],
+ [2022,1,11,1443,6,9],
+ [2022,1,22,1443,6,20],
+ [2022,2,5,1443,7,1],
+ [2022,2,16,1443,7,12],
+ [2022,2,27,1443,7,23],
+ [2022,3,7,1443,8,5],
+ [2022,3,18,1443,8,16],
+ [2022,3,29,1443,8,27],
+ [2022,4,10,1443,9,8],
+ [2022,4,21,1443,9,19],
+ [2022,5,1,1443,10,1],
+ [2022,5,12,1443,10,12],
+ [2022,5,23,1443,10,23],
+ [2022,6,4,1443,11,4],
+ [2022,6,15,1443,11,15],
+ [2022,6,26,1443,11,26],
+ [2022,7,6,1444,0,8],
+ [2022,7,17,1444,0,19],
+ [2022,7,28,1444,0,30],
+ [2022,8,8,1444,1,11],
+ [2022,8,19,1444,1,22],
+ [2022,8,30,1444,2,4],
+ [2022,9,11,1444,2,15],
+ [2022,9,22,1444,2,26],
+ [2022,10,2,1444,3,7],
+ [2022,10,13,1444,3,18],
+ [2022,10,24,1444,3,29],
+ [2022,11,5,1444,4,11],
+ [2022,11,16,1444,4,22],
+ [2022,11,27,1444,5,3],
+ [2023,0,7,1444,5,14],
+ [2023,0,18,1444,5,25],
+ [2023,0,29,1444,6,7],
+ [2023,1,9,1444,6,18],
+ [2023,1,20,1444,6,29],
+ [2023,2,3,1444,7,10],
+ [2023,2,14,1444,7,21],
+ [2023,2,25,1444,8,3],
+ [2023,3,5,1444,8,14],
+ [2023,3,16,1444,8,25],
+ [2023,3,27,1444,9,6],
+ [2023,4,8,1444,9,17],
+ [2023,4,19,1444,9,28],
+ [2023,4,30,1444,10,10],
+ [2023,5,10,1444,10,21],
+ [2023,5,21,1444,11,2],
+ [2023,6,2,1444,11,13],
+ [2023,6,13,1444,11,24],
+ [2023,6,24,1445,0,6],
+ [2023,7,4,1445,0,17],
+ [2023,7,15,1445,0,28],
+ [2023,7,26,1445,1,9],
+ [2023,8,6,1445,1,20],
+ [2023,8,17,1445,2,2],
+ [2023,8,28,1445,2,13],
+ [2023,9,9,1445,2,24],
+ [2023,9,20,1445,3,5],
+ [2023,9,31,1445,3,16],
+ [2023,10,11,1445,3,27],
+ [2023,10,22,1445,4,9],
+ [2023,11,3,1445,4,20],
+ [2023,11,14,1445,5,1],
+ [2023,11,25,1445,5,12],
+ [2024,0,5,1445,5,23],
+ [2024,0,16,1445,6,5],
+ [2024,0,27,1445,6,16],
+ [2024,1,7,1445,6,27],
+ [2024,1,18,1445,7,8],
+ [2024,1,29,1445,7,19],
+ [2024,2,11,1445,8,1],
+ [2024,2,22,1445,8,12],
+ [2024,3,2,1445,8,23],
+ [2024,3,13,1445,9,4],
+ [2024,3,24,1445,9,15],
+ [2024,4,5,1445,9,26],
+ [2024,4,16,1445,10,8],
+ [2024,4,27,1445,10,19],
+ [2024,5,7,1445,10,30],
+ [2024,5,18,1445,11,11],
+ [2010,2,22,1431,3,6]
+ ];
+
+ var idate, gdate;
+
+ dojo.forEach(dateTable, function(d, i) {
+
+ //console.debug(d, "at index", i);
+
+ gdate = new Date(d[0],d[1],d[2],15,15,10);
+
+ idate = new dojox.date.islamic.Date();
+ idate.fromGregorian(gdate);
+
+
+ t.is(0, dojox.date.islamic.compare(new dojox.date.islamic.Date(d[3], d[4], d[5], 15, 15, 10), idate, "date"));
+ });
+ }
+ },
+ {
+ name: "getDay",
+ runTest: function(t) {
+ var dateTable = [
+ [1431, 0, 11, 1],
+ [1431, 1, 3, 2],
+ [1431, 2, 10, 3],
+ [1431, 3, 23, 4],
+ [1431, 6, 21, 6],
+ [1431, 6, 22, 0],
+ [1431, 7, 15, 2],
+ [1202,1,1,1],
+ [1202,1,8,1],
+ [1202,1,15,1],
+ [1202,1,18,4],
+ [1202,1,19,5],
- [1202,1,20,5]
++ [1202,1,20,6]
+
+ ];
+ dojo.forEach(dateTable, function(d, i) {
+ var date = new dojox.date.islamic.Date(d[0], d[1], d[2]);
+ t.is(d[3], date.getDay());
+ });
+
+ }
+ },
+ {
+ name: "getDaysInIslamicMonth",
+ runTest: function(t) {
+
+ var dateTable = [
+ [1430, 1, 29],
+ [1420, 1, 29],
+ [1422, 5, 29],
+ [1431, 5, 29],
+ [1430, 2, 30],
+ [1431, 2, 30]
+ ];
+
+ dojo.forEach(dateTable, function(d, i) {
+ var date = new dojox.date.islamic.Date(d[0], d[1], 1);
+ t.is(d[2], dojox.date.islamic.getDaysInMonth(date));
+ });
+ }
+ },
+ {
+ name: "add_difference",
+ runTest: function(t) {
+ var start = [
+ [1420, 1, 1422, 1],
+ [1430, 2, 1435, 2],
+ [1433, 0, 1434, 0],
+ [1422, 2, 1420, 2],
+ [1429, 3, 1427, 3],
+ [1431, 4, 1431, 6],
+ [1429, 7, 1429, 5],
+ [1431, 3, 1431, 5],
+ [1431, 3, 1431, 0],
+ [1431, 1, 1431, 2],
+ [1431, 9, 1431, 8],
+ [1432, 7, 1432, 11],
+ [1433, 8, 1434, 0],
+ [1433, 0, 1432, 6],
+ [1434, 0, 1433, 11],
+ [1433, 0, 1432, 11],
+ [1432, 0, 1431, 11],
+ [1431, 0, 1430, 10]
+ ];
+ var add = [24, 60, 12, -24, -24, 2, -2, 2, -3, 1, -1, 4, 4, -6, -1, -1, -1, -2];
+
+ var dateHijriStart, dateHijriEnd, res, dateHijriRes;
+ dojo.forEach(start, function(s, i) {
+ dateHijriStart = new dojox.date.islamic.Date(s[0], s[1], 1);
+ dateHijriRes = dojox.date.islamic.add(dateHijriStart, "month", add[i]);
+
+ t.is(0, dateHijriRes.getMonth() - s[3]);
+ t.is(0, dateHijriRes.getFullYear() - s[2]);
+ });
+
+ //month difference
+ dojo.forEach(start, function(s, i) {
+ dateHijriRes = new dojox.date.islamic.Date(s[2], s[3], 1);
+ dateHijriStart = new dojox.date.islamic.Date(s[0], s[1], 1);
+ t.is(add[i], dojox.date.islamic.difference(dateHijriStart, dateHijriRes, "month"));
+ });
+ }
+ },
+ {
+ name: "consistency_of_add_and_difference",
+ runTest: function(t) {
+ var dateIslamic = new dojox.date.islamic.Date(1431, 4, 6);
+
+
+ var amounts = [2, 5, 6, 7, 8, 12, 18, 20, 24, 50, -3, -4, -5, -6, -7, -8, -9, -10, -50, 200, -200, 29, -29, 1, -1,
+ 23, 25];
+ var dateIslamicAdd;
+
+ dojo.forEach(amounts, function(amount, i) {
+ dateIslamicAdd = dojox.date.islamic.add(dateIslamic, "month", amount);
+ t.is(dojox.date.islamic.difference(dateIslamic, dateIslamicAdd, "month"), amount);
+
+ dateIslamicAdd = dojox.date.islamic.add(dateIslamic, "year", amount);
+ t.is(amount, dojox.date.islamic.difference(dateIslamic, dateIslamicAdd, "year"));
+
+ dateIslamicAdd = dojox.date.islamic.add(dateIslamic, "week", amount);
+ t.is(amount, dojox.date.islamic.difference(dateIslamic, dateIslamicAdd, "week"));
+
+ dateIslamicAdd = dojox.date.islamic.add(dateIslamic, "weekday", amount);
+ t.is(amount, dojox.date.islamic.difference(dateIslamic, dateIslamicAdd, "weekday"));
+
+ dateIslamicAdd = dojox.date.islamic.add(dateIslamic, "day", amount)
+ t.is(amount, dojox.date.islamic.difference(dateIslamic, dateIslamicAdd, "day"));
+
+ dateIslamicAdd = dojox.date.islamic.add(dateIslamic, "hour", amount);
+ t.is(amount, dojox.date.islamic.difference(dateIslamic, dateIslamicAdd, "hour"));
+
+ dateIslamicAdd = dojox.date.islamic.add(dateIslamic, "minute", amount);
+ t.is(amount, dojox.date.islamic.difference(dateIslamic, dateIslamicAdd, "minute"));
+
+ dateIslamicAdd = dojox.date.islamic.add(dateIslamic, "second", amount);
+ t.is(amount, dojox.date.islamic.difference(dateIslamic, dateIslamicAdd, "second"));
+
+ dateIslamicAdd = dojox.date.islamic.add(dateIslamic, "millisecond", amount);
+ t.is(amount, dojox.date.islamic.difference(dateIslamic, dateIslamicAdd, "millisecond"));
+ });
+
+ var dateIslamicDiff = new dojox.date.islamic.Date(1431, 4, 7);
+ t.is(1, dojox.date.islamic.difference(dateIslamic, dateIslamicDiff));
+ }
+ },
+ {
+ name: "getMonth_setMonth",
+ runTest: function(t) {
+ var dateIslamic = new dojox.date.islamic.Date(1420, 1, 1);
+ for (var year = 1420; year < 1430; year++) {
+ dateIslamic.setFullYear(year);
+ t.is(year, dateIslamic.getFullYear());
+ dateIslamic.setMonth(11);
+ t.is(11, dateIslamic.getMonth());
+ dateIslamic.setMonth(6);
+ t.is(6, dateIslamic.getMonth());
+
+ }
+ }
+ },
+ {
+ name: "parse_and_format",
+ runTest: function(t) {
+
+ //test Islamic and English locale
+
+ var dates = [
+ [1430, 5, 1],
+ [1428, 1, 28],
+ [1431, 5, 16],
+ [1431, 11, 2],
+ [1433, 0, 2]
+ ];
+
+ var dateIslamic, dateIslamic1;
+ dojo.forEach(dates, function(date, i) {
+ dateIslamic = new dojox.date.islamic.Date(date[0], date[1], date[2]);
+
+ var options = [{ formatLength: 'full', locale: 'ar' }, { formatLength: 'long', locale: 'ar' }, { formatLength: 'medium', locale: 'ar' }, { formatLength: 'short', locale: 'ar' },
+ { formatLength: 'full', locale: 'en' }, { formatLength: 'long', locale: 'en' }, { formatLength: 'medium', locale: 'en' }, { formatLength: 'short', locale: 'en'}];
+ dojo.forEach(options, function(opt, i) {
+ str = dojox.date.islamic.locale.format(dateIslamic, opt);
+ var option = "{" + opt + ", locale:'ar'}";
+ dateIslamic1 = dojox.date.islamic.locale.parse(str, opt);
+ t.is(0, dojo.date.compare(dateIslamic.toGregorian(), dateIslamic1.toGregorian(), 'date'));
+ });
+
+ var pattern = ['d M yy', 'dd/MM/yy h:m:s', 'dd#MM#yy HH$mm$ss', 'dd MMMM yyyy'];
+ dojo.forEach(pattern, function(pat, i) {
+ options = { datePattern: pat, selector: 'date', locale: 'ar' };
+ str = dojox.date.islamic.locale.format(dateIslamic, options);
+ dateIslamic1 = dojox.date.islamic.locale.parse(str, options);
+ t.is(0, dojo.date.compare(dateIslamic.toGregorian(), dateIslamic1.toGregorian(), 'date'));
+ });
+ });
+
+ dateIslamic = new dojox.date.islamic.Date(1431, 6, 3, 15, 3, 59);
+ pattern = 'HH$mm$ss';
+ options = { timePattern: pattern, selector: 'time' };
+ str = dojox.date.islamic.locale.format(dateIslamic, options);
+ dateIslamic1 = dojox.date.islamic.locale.parse(str, options);
+ var gregDate = dojo.date.locale.parse(str, options);
+ t.is(0, dojo.date.compare(gregDate, dateIslamic1.toGregorian(), 'time'));
+
+ pattern = "h:m:s";
+ options = { timePattern: pattern, selector: 'time' };
+ str = dojox.date.islamic.locale.format(dateIslamic, options);
+ t.is(str, "3:3:59");
+ }
+ },
+ {
+ name: "addMilliseconds",
+ runTest: function(t){
+ var islamicDates = [
+ [5771, 8, 21, 10, 30],
+ [5771, 8, 21, 2, 2],
+ [5771, 8, 21, 8, 10], // "absolute" index of month, non-leap year
+ [5771, 8, 21, 12, 59],
+ [5771, 8, 21, 3, 33]
+ ];
+
+ var dates = [
+ [1432, 8, 21, 10, 30],
+ [1432, 8, 21, 2, 2],
+ [1432, 8, 21, 8, 10], // "absolute" index of month, non-leap year
+ [1432, 8, 21, 12, 59],
+ [1432, 8, 21, 3, 33]
+ ];
+
+ var traceAttributes = function(date){
+ console.log("getHours():" + date.getHours()+" getMinutes():"+date.getMinutes()+" getSeconds():"+date.getSeconds()+" getMilliseconds():"+date.getMilliseconds());
+ };
+
+ var dateIslamic, date2;
+ dojo.forEach(islamicDates, function(date, i){
+ dateIslamic = new dojox.date.islamic.Date(date[0], date[1], date[2], date[3], date[4]);
+ date2 = new Date(dates[i][0], dates[i][1], dates[i][2], dates[i][3], dates[i][4]);
+
+ var newIslamicDate = dojox.date.islamic.add(dateIslamic, "millisecond", 1200);
+ var newDate = dojo.date.add(date2, "millisecond", 1200);
+ t.is(newIslamicDate.getHours(), newDate.getHours(), "Hours are different");
+ t.is(newIslamicDate.getMinutes(), newDate.getMinutes(), "Minutes are different");
+ t.is(newIslamicDate.getSeconds(), newDate.getSeconds(), "Seconds are different");
+ t.is(newIslamicDate.getMilliseconds(), newDate.getMilliseconds(), "Milliseconds are different");
+ //traceAttributes(newIslamicDate);
+
+ newIslamicDate = dojox.date.islamic.add(dateIslamic, "millisecond", 12022);
+ newDate = dojo.date.add(date2, "millisecond", 12022);
+ t.is(newIslamicDate.getHours(), newDate.getHours(), "Hours are different");
+ t.is(newIslamicDate.getMinutes(), newDate.getMinutes(), "Minutes are different");
+ t.is(newIslamicDate.getSeconds(), newDate.getSeconds(), "Seconds are different");
+ t.is(newIslamicDate.getMilliseconds(), newDate.getMilliseconds(), "Milliseconds are different");
+ //traceAttributes(newIslamicDate);
+
+ newIslamicDate = dojox.date.islamic.add(dateIslamic, "millisecond", 120422);
+ newDate = dojo.date.add(date2, "millisecond", 120422);
+ t.is(newIslamicDate.getHours(), newDate.getHours(), "Hours are different");
+ t.is(newIslamicDate.getMinutes(), newDate.getMinutes(), "Minutes are different");
+ t.is(newIslamicDate.getSeconds(), newDate.getSeconds(), "Seconds are different");
+ t.is(newIslamicDate.getMilliseconds(), newDate.getMilliseconds(), "Milliseconds are different");
+ //traceAttributes(newIslamicDate);
+
+ newIslamicDate = dojox.date.islamic.add(dateIslamic, "millisecond", 1204422);
+ newDate = dojo.date.add(date2, "millisecond", 1204422);
+ t.is(newIslamicDate.getHours(), newDate.getHours(), "Hours are different");
+ t.is(newIslamicDate.getMinutes(), newDate.getMinutes(), "Minutes are different");
+ t.is(newIslamicDate.getSeconds(), newDate.getSeconds(), "Seconds are different");
+ t.is(newIslamicDate.getMilliseconds(), newDate.getMilliseconds(), "Milliseconds are different");
+ //traceAttributes(newIslamicDate);
+ });
+ }
+ }
+ ]
+);
diff --cc dojox/dgauges/CONTRIBUTING.md
index 5ae012f,0000000..5ae012f
mode 100644,000000..100755
--- a/dojox/dgauges/CONTRIBUTING.md
+++ b/dojox/dgauges/CONTRIBUTING.md
diff --cc dojox/form/BusyButton.js
index 7242ebb,0000000..e2d78a7
mode 100644,000000..100644
--- a/dojox/form/BusyButton.js
+++ b/dojox/form/BusyButton.js
@@@ -1,146 -1,0 +1,157 @@@
+define([
+ "dojo/_base/lang",
+ "dojo/dom-attr",
+ "dojo/dom-class",
+ "dijit/form/Button",
+ "dijit/form/DropDownButton",
+ "dijit/form/ComboButton",
+ "dojo/i18n",
+ "dojo/i18n!dijit/nls/loading",
+ "dojo/_base/declare"
+], function(lang, domAttr, domClass, Button, DropDownButton, ComboButton, i18n, nlsLoading, declare){
+
+var _BusyButtonMixin = declare("dojox.form._BusyButtonMixin", null, {
+
+ // isBusy: Boolean
+ isBusy: false,
-
++
+ // busyLabel: String
+ // text while button is busy
+ busyLabel: "",
-
++
+ timeout: null, // timeout, should be controlled by xhr call
-
++
+ // useIcon: Boolean
+ // use a busy icon
+ useIcon: true,
+
+ postMixInProperties: function(){
+ this.inherited(arguments);
+ if(!this.busyLabel){
+ this.busyLabel = i18n.getLocalization("dijit", "loading", this.lang).loadingState;
+ }
+ },
+
+ postCreate: function(){
+ // summary:
+ // stores initial label and timeout for reference
+ this.inherited(arguments);
+ this._label = this.containerNode.innerHTML;
+ this._initTimeout = this.timeout;
+
+ // for initial busy buttons
+ if(this.isBusy){
+ this.makeBusy();
+ }
+ },
+
+ makeBusy: function(){
+ // summary:
+ // sets state from idle to busy
+ this.isBusy = true;
- this.set("disabled", true);
++
++ // Webkit does not submit the form if the submit button is disabled when
++ // clicked ( https://bugs.webkit.org/show_bug.cgi?id=14443 ), so disable the button later
++ if(this._disableHandle) {
++ this._disableHandle.remove();
++ }
++ this._disableHandle = this.defer(function() {
++ this.set("disabled", true);
++ });
+
+ this.setLabel(this.busyLabel, this.timeout);
+ },
+
+ cancel: function(){
+ // summary:
+ // if no timeout is set or for other reason the user can put the button back
+ // to being idle
++ if(this._disableHandle) {
++ this._disableHandle.remove();
++ }
+ this.set("disabled", false);
+ this.isBusy = false;
+ this.setLabel(this._label);
+ if(this._timeout){ clearTimeout(this._timeout); }
+ this.timeout = this._initTimeout;
+ },
+
+ resetTimeout: function(/*Int*/ timeout){
+ // summary:
+ // to reset existing timeout and setting a new timeout
+ if(this._timeout){
+ clearTimeout(this._timeout);
+ }
+
+ // new timeout
+ if(timeout){
+ this._timeout = setTimeout(lang.hitch(this, function(){
+ this.cancel();
+ }), timeout);
+ }else if(timeout == undefined || timeout === 0){
+ this.cancel();
+ }
+ },
+
+ setLabel: function(/*String*/ content, /*Int*/ timeout){
+ // summary:
+ // setting a label and optional timeout of the labels state
+
+ // this.inherited(arguments); FIXME: throws an Unknown runtime error
+
+ // Begin IE hack
+ this.label = content;
+ // remove children
+ while(this.containerNode.firstChild){
+ this.containerNode.removeChild(this.containerNode.firstChild);
+ }
- this.containerNode.innerHTML = this.label;
++ this.containerNode.appendChild(document.createTextNode(this.label));
+
+ if(this.showLabel == false && !domAttr.get(this.domNode, "title")){
+ this.titleNode.title=lang.trim(this.containerNode.innerText || this.containerNode.textContent || '');
+ }
+ // End IE hack
+
+ // setting timeout
+ if(timeout){
+ this.resetTimeout(timeout);
+ }else{
+ this.timeout = null;
+ }
+
+ // create optional busy image
+ if(this.useIcon && this.isBusy){
+ var node = new Image();
+ node.src = this._blankGif;
+ domAttr.set(node, "id", this.id+"_icon");
+ domClass.add(node, "dojoxBusyButtonIcon");
+ this.containerNode.appendChild(node);
+ }
+ },
+
+ _onClick: function(e){
+ // summary:
+ // on button click the button state gets changed
+
+ // only do something if button is not busy
+ if(!this.isBusy){
+ this.inherited(arguments); // calls onClick()
+ this.makeBusy();
+ }
+ }
+});
+
+var BusyButton = declare("dojox.form.BusyButton", [Button, _BusyButtonMixin], {
+ // summary:
- // BusyButton is a simple widget which provides implementing more
++ // BusyButton is a simple widget which provides implementing more
+ // user friendly form submission.
+ // description:
+ // When a form gets submitted by a user, many times it is recommended to disable
+ // the submit buttons to prevent double submission. BusyButton provides a simple set
+ // of features for this purpose
+
+});
+declare("dojox.form.BusyComboButton", [ComboButton, _BusyButtonMixin], {});
+declare("dojox.form.BusyDropDownButton", [DropDownButton, _BusyButtonMixin], {});
+return BusyButton;
+});
diff --cc dojox/form/FileInputAuto.js
index 9a07b23,0000000..cf731da
mode 100644,000000..100644
--- a/dojox/form/FileInputAuto.js
+++ b/dojox/form/FileInputAuto.js
@@@ -1,234 -1,0 +1,235 @@@
+define([
+ "dojo/_base/declare",
+ "dojo/_base/lang",
+ "dojo/_base/fx",
+ "dojo/_base/window",
+ "dojo/dom-style",
+ "dojo/_base/sniff",
+ "dojo/text!./resources/FileInputAuto.html",
+ "dojox/form/FileInput",
+ "dojo/io/iframe"
+],
+function(declare, lang, fx, win, domStyle, has, template, FileInput, ioIframe){
+
+var FileInputAuto = declare("dojox.form.FileInputAuto", FileInput,
+ {
+ // summary:
+ // An extension on FileInput providing background upload progress
+ //
+ // description:
+ // An extended version of FileInput - when the user focuses away from the input
+ // the selected file is posted via ioIframe to the url. example implementation
+ // comes with PHP solution for handling upload, and returning required data.
+ //
+ // notes: the return data from the io.iframe is used to populate the input element with
+ // data regarding the results. it will be a JSON object, like:
+ //
+ // | results = { size: "1024", filename: "file.txt" }
+ //
+ // all the parameters allowed to FileInput apply
+
+ // url: String
+ // the URL where our background FileUpload will be sent
+ url: "",
+
+ // blurDelay: Integer
+ // time in ms before an un-focused widget will wait before uploading the file to the url="" specified
+ // default: 2 seconds
+ blurDelay: 2000,
+
+ // duration: Integer
+ // The time in ms to use as the generic timing mechanism for the animations
+ // set to 1 or 0 for "immediate response"
+ duration: 500,
+
+ // uploadMessage: String
+ // FIXME: i18n somehow?
+ uploadMessage: "Uploading ...",
+
+ // triggerEvent: String
+ // Event which triggers the upload. Defaults to onblur, sending the file selected
+ // 'blurDelay' milliseconds after losing focus. Set to "onchange" with a low blurDelay
+ // to send files immediately after uploading.
+ triggerEvent: "onblur",
+
+ _sent: false,
+
+ // small template changes, new attachpoint: overlay
+ templateString: template,
+
+ onBeforeSend: function(){
+ // summary:
+ // Called immediately before a FileInput sends it's file via io.iframe.send.
+ // The return of this function is passed as the `content` member in the io.iframe IOArgs
+ // object.
+ return {};
+ },
+
+ startup: function(){
+ // summary:
+ // add our extra blur listeners
+ this._blurListener = this.connect(this.fileInput, this.triggerEvent, "_onBlur");
+ this._focusListener = this.connect(this.fileInput, "onfocus", "_onFocus");
+ this.inherited(arguments);
+ },
+
+ _onFocus: function(){
+ // summary:
+ // clear the upload timer
+ if(this._blurTimer){ clearTimeout(this._blurTimer); }
+ },
+
+ _onBlur: function(){
+ // summary:
+ // start the upload timer
+ if(this._blurTimer){ clearTimeout(this._blurTimer); }
+ if(!this._sent){
+ this._blurTimer = setTimeout(lang.hitch(this,"_sendFile"),this.blurDelay);
+ }
+ },
+
+ setMessage: function(/*String*/ title){
+ // summary:
+ // set the text of the progressbar
+
+ // innerHTML throws errors in IE! so use DOM manipulation instead
+ //this.overlay.innerHTML = title;
+ this.overlay.removeChild(this.overlay.firstChild);
+ this.overlay.appendChild(document.createTextNode(title));
+ },
+
+ _sendFile: function(/*Event*/ e){
+ // summary:
+ // triggers the chain of events needed to upload a file in the background.
+ if(this._sent || this._sending || !this.fileInput.value){ return; }
+
+ this._sending = true;
+
+ domStyle.set(this.fakeNodeHolder,"display","none");
+ domStyle.set(this.overlay,{
+ opacity:0,
+ display:"block"
+ });
+
+ this.setMessage(this.uploadMessage);
+
+ fx.fadeIn({ node: this.overlay, duration:this.duration }).play();
+
+ var _newForm;
+ if(has('ie') < 9 || (has('ie') && has('quirks'))){
+ // just to reiterate, IE is a steaming pile of code.
+ _newForm = document.createElement('<form enctype="multipart/form-data" method="post">');
+ _newForm.encoding = "multipart/form-data";
+
+ }else{
+ // this is how all other sane browsers do it
+ _newForm = document.createElement('form');
+ _newForm.setAttribute("enctype","multipart/form-data");
++ _newForm.setAttribute("method","post");
+ }
+ _newForm.appendChild(this.fileInput);
+ win.body().appendChild(_newForm);
+
+ ioIframe.send({
+ url: this.url,
+ form: _newForm,
+ handleAs: "json",
+ handle: lang.hitch(this,"_handleSend"),
+ content: this.onBeforeSend()
+ });
+ },
+
+ _handleSend: function(data,ioArgs){
+ // summary:
+ // The callback to toggle the progressbar, and fire the user-defined callback
+
+ // innerHTML throws errors in IE! so use DOM manipulation instead
+ this.overlay.removeChild(this.overlay.firstChild);
+
+ this._sent = true;
+ this._sending = false;
+ domStyle.set(this.overlay,{
+ opacity:0,
+ border:"none",
+ background:"none"
+ });
+
+ this.overlay.style.backgroundImage = "none";
+ this.fileInput.style.display = "none";
+ this.fakeNodeHolder.style.display = "none";
+ fx.fadeIn({ node:this.overlay, duration:this.duration }).play(250);
+
+ this.disconnect(this._blurListener);
+ this.disconnect(this._focusListener);
+
+ //remove the form used to send the request
+ win.body().removeChild(ioArgs.args.form);
+ this.fileInput = null;
+
+ this.onComplete(data,ioArgs,this);
+ },
+
+ reset: function(e){
+ // summary:
+ // accommodate our extra focusListeners
+ if(this._blurTimer){ clearTimeout(this._blurTimer); }
+
+ this.disconnect(this._blurListener);
+ this.disconnect(this._focusListener);
+
+ this.overlay.style.display = "none";
+ this.fakeNodeHolder.style.display = "";
+ this.inherited(arguments);
+ this._sent = false;
+ this._sending = false;
+ this._blurListener = this.connect(this.fileInput, this.triggerEvent,"_onBlur");
+ this._focusListener = this.connect(this.fileInput,"onfocus","_onFocus");
+ },
+
+ onComplete: function(data,ioArgs,widgetRef){
+ // summary:
+ // stub function fired when an upload has finished.
+ // data:
+ // the raw data found in the first [TEXTAREA] tag of the post url
+ // ioArgs:
+ // the Deferred data being passed from the handle: callback
+ // widgetRef:
+ // this widget pointer, so you can set this.overlay to a completed/error message easily
+ }
+});
+
+declare("dojox.form.FileInputBlind", FileInputAuto, {
+ // summary:
+ // An extended version of dojox.form.FileInputAuto
+ // that does not display an input node, but rather only a button
+ // and otherwise behaves just like FileInputAuto
+
+ startup: function(){
+ // summary:
+ // hide our fileInput input field
+ this.inherited(arguments);
+ this._off = domStyle.get(this.inputNode,"width");
+ this.inputNode.style.display = "none";
+ this._fixPosition();
+ },
+
+ _fixPosition: function(){
+ // summary:
+ // in this case, set the button under where the visible button is
+ if(has('ie')){
+ domStyle.set(this.fileInput,"width","1px");
+ }else{
+ domStyle.set(this.fileInput,"left","-"+(this._off)+"px");
+ }
+ },
+
+ reset: function(e){
+ // summary:
+ // onclick, we need to reposition our newly created input type="file"
+ this.inherited(arguments);
+ this._fixPosition();
+ }
+});
+
+return FileInputAuto;
+});
diff --cc dojox/form/tests/UploadFile.php.disabled
index 889ee71,0000000..6f8419a
mode 100644,000000..100644
--- a/dojox/form/tests/UploadFile.php.disabled
+++ b/dojox/form/tests/UploadFile.php.disabled
@@@ -1,328 -1,0 +1,328 @@@
+<?php
+// summary
+// Test file to handle image uploads (remove the image size check to upload non-images)
+//
+// This file handles both Flash and HTML uploads
+//
+// NOTE: This is obviously a PHP file, and thus you need PHP running for this to work
+// NOTE: Directories must have write permissions
+// NOTE: This code uses the GD library (to get image sizes), that sometimes is not pre-installed in a
+// standard PHP build.
+//
+require("cLOG.php");
+
+function findTempDirectory()
+ {
+ if(isset($_ENV["TMP"]) && is_writable($_ENV["TMP"])) return $_ENV["TMP"];
+ elseif( is_writable(ini_get('upload_tmp_dir'))) return ini_get('upload_tmp_dir');
+ elseif(isset($_ENV["TEMP"]) && is_writable($_ENV["TEMP"])) return $_ENV["TEMP"];
+ elseif(is_writable("/tmp")) return "/tmp";
+ elseif(is_writable("/windows/temp")) return "/windows/temp";
+ elseif(is_writable("/winnt/temp")) return "/winnt/temp";
+ else return null;
+ }
+function trace($txt, $isArray=false){
+ //creating a text file that we can log to
+ // this is helpful on a remote server if you don't
+ //have access to the log files
+ //
+ $log = new cLOG("../tests/upload.txt", false);
+ //$log->clear();
+ if($isArray){
+ $log->printr($txt);
+ }else{
+ $log->write($txt);
+ }
+
+ //echo "$txt<br>";
+}
+function getImageType($filename){
+ return strtolower(substr(strrchr($filename,"."),1));
+}
+trace("---------------------------------------------------------");
+
+//
+//
+// EDIT ME: According to your local directory structure.
+// NOTE: Folders must have write permissions
+//
+$upload_path = "../tests/uploads/"; // where image will be uploaded, relative to this file
+$download_path = "../tests/uploads/"; // same folder as above, but relative to the HTML file
+
+//
+// NOTE: maintain this path for JSON services
+//
+require("../../../dojo/tests/resources/JSON.php");
+$json = new Services_JSON();
+
+//
+// Determine if this is a Flash upload, or an HTML upload
+//
+//
+
+// First combine relavant postVars
+$postdata = array();
+$htmldata = array();
+$data = "";
+trace("POSTDATA: " . count($_FILES) . " FILES");
+foreach ($_POST as $nm => $val) {
+ $data .= $nm ."=" . $val . ","; // string for flash
+ $postdata[$nm] = $val; // array for html
+}
+
+trace($postdata, true);
+
+foreach ($_FILES as $nm => $val) {
+ trace(" file: ".$nm ."=" . $val);
+}
+
+foreach ($_GET as $nm => $val) {
+ trace($nm ."=" . $val);
+}
+
+$fieldName = "flashUploadFiles";//Filedata";
+
+if( isset($_FILES[$fieldName]) || isset($_FILES['uploadedfileFlash'])){
+ //
+ // If the data passed has $fieldName, then it's Flash.
+ // NOTE: "Filedata" is the default fieldname, but we're using a custom fieldname.
+ // The SWF passes one file at a time to the server, so the files come across looking
+ // very much like a single HTML file. The SWF remembers the data and returns it to
+ // Dojo as an array when all are complete.
+ //
+ trace("returnFlashdata....");
+
+ trace("");
+ trace("ID:");
+
+ trace("Flash POST:");
+ trace($_POST, true);
+
+
+ $returnFlashdata = true; //for dev
+
+ if( isset($_FILES[$fieldName])){
+ // backwards compat - FileUploader
+ trace("FILES:");
+ trace($_FILES[$fieldName], true);
+ $m = move_uploaded_file($_FILES[$fieldName]['tmp_name'], $upload_path . $_FILES[$fieldName]['name']);
+ $name = $_FILES[$fieldName]['name'];
+
+ }else{
+ // New fieldname - Uploader
+ trace("FILES:");
+ trace($_FILES['uploadedfileFlash'], true);
+ $m = move_uploaded_file($_FILES['uploadedfileFlash']['tmp_name'], $upload_path . $_FILES['uploadedfileFlash']['name']);
+ $name = $_FILES['uploadedfileFlash']['name'];
+
+ }
+
+ $file = $upload_path . $name;
+ try{
+ list($width, $height) = getimagesize($file);
+ } catch(Exception $e){
+ $width=0;
+ $height=0;
+ }
+ $type = getImageType($file);
+ trace("file: " . $file ." ".$type." ".$width);
+ // Flash gets a string back:
+
+ //exit;
+
+ $data .='file='.$file.',name='.$name.',width='.$width.',height='.$height.',type='.$type;
+ if($returnFlashdata){
+ trace("returnFlashdata:\n=======================");
+ trace($data);
+ trace("=======================");
+ // echo sends data to Flash:
+ echo($data);
+ // return is just to stop the script:
+ return;
+ }
+
+}elseif( isset($_FILES['uploadedfile0']) ){
+ //
+ // Multiple files have been passed from HTML
+ //
+ $cnt = 0;
+ trace("IFrame multiple POST:");
+ trace($postdata, true);
+
+ $_post = $htmldata;
+ $htmldata = array();
-
-
++
++
+
+ while(isset($_FILES['uploadedfile'.$cnt])){
+ trace("IFrame multiple POST");
+ $moved = move_uploaded_file($_FILES['uploadedfile'.$cnt]['tmp_name'], $upload_path . $_FILES['uploadedfile'.$cnt]['name']);
+ trace("moved:" . $moved ." ". $_FILES['uploadedfile'.$cnt]['name']);
+ if($moved){
+ $name = $_FILES['uploadedfile'.$cnt]['name'];
+ $file = $upload_path . $name;
+ $type = getImageType($file);
+ try{
+ list($width, $height) = getimagesize($file);
+ } catch(Exception $e){
+ $width=0;
+ $height=0;
+ }
+ trace("file: " . $file );
+
- $_post['file'] = $file;
++ $_post['file'] = $download_path . $name;
+ $_post['name'] = $name;
+ $_post['width'] = $width;
+ $_post['height'] = $height;
+ $_post['type'] = $type;
+ $_post['uploadType'] = $postdata['uploadType'];
+ $_post['size'] = filesize($file);
+ $_post['additionalParams'] = $postdata;
+ trace($_post['additionalParams'], true);
+
+ $htmldata[$cnt] = $_post;
-
++
+ foreach($postdata as $key => $value){
+ //$htmldata[ $key ] = $value;
+ }
+
+ }elseif(strlen($_FILES['uploadedfile'.$cnt]['name'])){
+ $htmldata[$cnt] = array("ERROR" => "File could not be moved: ".$_FILES['uploadedfile'.$cnt]['name']);
+ }
+ $cnt++;
+ }
+ trace("HTML multiple POST done:");
+ foreach($htmldata as $key => $value){
+ trace($value, true);
+ }
+
+}elseif( isset($_POST['uploadedfiles']) ){
+ trace("HTML5 multi file input... CAN'T ACCESS THIS OBJECT! (POST[uploadedfiles])");
+ trace(count($_POST['uploadedfiles'])." ");
+
+
+}elseif( isset($_FILES['uploadedfiles']) ){
+ //
+ // If the data passed has 'uploadedfiles' (plural), then it's an HTML5 multi file input.
+ //
+ $cnt = 0;
+ trace("HTML5 multi file input");
+ //trace($_FILES, true);
+ //print_r($_FILES);
+ $_post = $postdata;
+ trace("POST DATA:::");
+ trace($_post, true);
+ $htmldata = array();
+ $len = count($_FILES['uploadedfiles']['name']);
+ //
+ // Ugh. The array passed from HTML to PHP is fugly.
+ //
+
+ //print_r($_FILES['uploadedfiles']);
+
+ for($i=0;$i<$len;$i++){
+ $moved = move_uploaded_file($_FILES['uploadedfiles']['tmp_name'][$i], $upload_path . $_FILES['uploadedfiles']['name'][$i]);
+ trace("moved:" . $moved ." ". $_FILES['uploadedfiles']['name'][$i]);
+ if($moved){
+ $name = $_FILES['uploadedfiles']['name'][$i];
+ $file = $upload_path . $name;
+ $type = getImageType($file);
+ try{
+ list($width, $height) = getimagesize($file);
+ } catch(Exception $e){
+ error_log("NO EL MOVEO: " . $name);
+ $width=0;
+ $height=0;
+ $_post['filesInError'] = $name;
+ }
+
+ if(!$width){
+ $_post['filesInError'] = $name;
+ $width=0;
+ $height=0;
+ }
+ trace("file: " . $file ." size: " . $width." ".$height);
+
- $_post['file'] = $file;
++ $_post['file'] = $download_path . $name;
+ $_post['name'] = $name;
+ $_post['width'] = $width;
+ $_post['height'] = $height;
+ $_post['type'] = $type;
+ $_post['size'] = filesize($file);
+ //$_post['additionalParams'] = $postdata;
+ //trace($_post, true);
+
+ $htmldata[$cnt] = $_post;
+
+ }elseif(strlen($_FILES['uploadedfiles']['name'][$i])){
+ $htmldata[$cnt] = array("ERROR" => "File could not be moved: ".$_FILES['uploadedfiles']['name'][$i]);
+ }
+ $cnt++;
+ }
+
+ $data = $json->encode($htmldata);
+ trace($data);
+ print $data;
+ return $data;
+
+}elseif( isset($_FILES['uploadedfile']) ){
+ //
+ // If the data passed has 'uploadedfile', then it's HTML.
+ // There may be better ways to check this, but this *is* just a test file.
+ //
+ $m = move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $upload_path . $_FILES['uploadedfile']['name']);
+
+
+
+ trace("HTML single POST:");
+ trace($postdata, true);
+
+ $name = $_FILES['uploadedfile']['name'];
+ $file = $upload_path . $name;
+ $type = getImageType($file);
+ try{
+ list($width, $height) = getimagesize($file);
+ } catch(Exception $e){
+ $width=0;
+ $height=0;
+ }
+ trace("file: " . $file );
+
- $htmldata['file'] = $file;
++ $htmldata['file'] = $download_path . $name;
+ $htmldata['name'] = $name;
+ $htmldata['width'] = $width;
+ $htmldata['height'] = $height;
+ $htmldata['type'] = $type;
+ $htmldata['uploadType'] = $uploadType;
+ $htmldata['size'] = filesize($file);
+ $htmldata['additionalParams'] = $postdata;
-
++
+ $data = $json->encode($htmldata);
+ trace($data);
+ print $data;
+ return $data;
+
+
+}elseif(isset($_GET['rmFiles'])){
+ trace("DELETING FILES" . $_GET['rmFiles']);
+ $rmFiles = explode(";", $_GET['rmFiles']);
+ foreach($rmFiles as $f){
+ if($f && file_exists($f)){
+ trace("deleted:" . $f. ":" .unlink($f));
+ }
+ }
+
+}else{
+ trace("IMROPER DATA SENT... $_FILES:");
+ trace($_FILES);
+ $htmldata = array("ERROR" => "Improper data sent - no files found");
+}
+
+//HTML gets a json array back:
+$data = $json->encode($htmldata);
+trace("Json Data Returned:");
+trace($data);
+// in a text field:
+?>
+
+<textarea style="width:600px; height:150px;"><?php print $data; ?></textarea>
diff --cc dojox/layout/ContentPane.js
index 89177ea,0000000..875aabe
mode 100644,000000..100644
--- a/dojox/layout/ContentPane.js
+++ b/dojox/layout/ContentPane.js
@@@ -1,102 -1,0 +1,102 @@@
+define([
+ "dojo/_base/lang",
+ "dojo/_base/xhr",
+ "dijit/layout/ContentPane",
+ "dojox/html/_base",
+ "dojo/_base/declare"
+], function (lang, xhrUtil, ContentPane, htmlUtil, declare) {
+
+return declare("dojox.layout.ContentPane", ContentPane, {
+ // summary:
+ // An extended version of dijit.layout.ContentPane.
+ // Supports infile scripts and external ones declared by `<script src=''...>`
+ // relative path adjustments (content fetched from a different folder)
+ // `<style>` and `<link rel='stylesheet' href='..'>` tags,
+ // css paths inside cssText is adjusted (if you set adjustPaths = true)
+ //
+ // NOTE that dojo.require in script in the fetched file isn't recommended
+ // Many widgets need to be required at page load to work properly
+
+ // adjustPaths: Boolean
+ // Adjust relative paths in html string content to point to this page.
+ // Only useful if you grab content from a another folder then the current one
+ adjustPaths: false,
+
+ // cleanContent: Boolean
+ // Cleans content to make it less likely to generate DOM/JS errors.
+ // Useful if you send ContentPane a complete page, instead of a html fragment
+ // scans for:
+ //
+ // - title Node, remove
+ // - DOCTYPE tag, remove
+ cleanContent: false,
+
+ // renderStyles: Boolean
+ // trigger/load styles in the content
+ renderStyles: false,
+
+ // executeScripts: Boolean
+ // Execute (eval) scripts that is found in the content
+ executeScripts: true,
+
+ // scriptHasHooks: Boolean
+ // replace keyword '_container_' in scripts with 'dijit.byId(this.id)'
+ // NOTE this name might change in the near future
+ scriptHasHooks: false,
+
+ ioMethod: xhrUtil.get,
+
+ ioArgs: {},
+
+ onExecError: function(/*Event*/ e){
+ // summary:
+ // event callback, called on script error or on java handler error
+ // override and return your own html string if you want a some text
+ // displayed within the ContentPane
+ },
+
+ _setContent: function(cont){
+ // override dijit.layout.ContentPane._setContent, to enable path adjustments
+
+ var setter = this._contentSetter;
+ if(! (setter && setter instanceof htmlUtil._ContentSetter)) {
+ setter = this._contentSetter = new htmlUtil._ContentSetter({
+ node: this.containerNode,
+ _onError: lang.hitch(this, this._onError),
+ onContentError: lang.hitch(this, function(e){
+ // fires if a domfault occurs when we are appending this.errorMessage
+ // like for instance if domNode is a UL and we try append a DIV
+ var errMess = this.onContentError(e);
+ try{
+ this.containerNode.innerHTML = errMess;
+ }catch(e){
+ console.error('Fatal '+this.id+' could not change content due to '+e.message, e);
+ }
+ })/*,
+ _onError */
+ });
+ };
+
+ // stash the params for the contentSetter to allow inheritance to work for _setContent
+ this._contentSetterParams = {
+ adjustPaths: Boolean(this.adjustPaths && (this.href||this.referencePath)),
+ referencePath: this.href || this.referencePath,
+ renderStyles: this.renderStyles,
+ executeScripts: this.executeScripts,
+ scriptHasHooks: this.scriptHasHooks,
+ scriptHookReplacement: "dijit.byId('"+this.id+"')"
+ };
+
+ this.inherited("_setContent", arguments);
+ },
+ // could put back _renderStyles by wrapping/aliasing dojox.html._ContentSetter.prototype._renderStyles
+
+ destroy: function () {
+ var setter = this._contentSetter;
+ if (setter) {
- setter.teardown();
++ setter.tearDown();
+ }
+ this.inherited(arguments);
+ }
+});
+});
diff --cc dojox/layout/FloatingPane.js
index 5a5e0d2,0000000..45744f4
mode 100644,000000..100644
--- a/dojox/layout/FloatingPane.js
+++ b/dojox/layout/FloatingPane.js
@@@ -1,318 -1,0 +1,318 @@@
+define(["dojo/_base/kernel","dojo/_base/lang","dojo/_base/window","dojo/_base/declare",
+ "dojo/_base/fx","dojo/_base/connect","dojo/_base/array","dojo/_base/sniff",
+ "dojo/window","dojo/dom","dojo/dom-class","dojo/dom-geometry","dojo/dom-construct",
+ "dijit/_TemplatedMixin","dijit/_Widget","dijit/BackgroundIframe","dojo/dnd/Moveable",
+ "./ContentPane","./ResizeHandle","dojo/text!./resources/FloatingPane.html","./Dock"], function(
+ kernel, lang, winUtil, declare, baseFx, connectUtil, arrayUtil,
+ has, windowLib, dom, domClass, domGeom, domConstruct, TemplatedMixin, Widget, BackgroundIframe,
+ Moveable, ContentPane, ResizeHandle, template,Dock){
+
+kernel.experimental("dojox.layout.FloatingPane");
+var FloatingPane = declare("dojox.layout.FloatingPane", [ ContentPane, TemplatedMixin ],{
+ // summary:
+ // A non-modal Floating window.
+ // description:
+ // Makes a `dojox.layout.ContentPane` float and draggable by it's title [similar to TitlePane]
+ // and over-rides onClick to onDblClick for wipeIn/Out of containerNode
+ // provides minimize(dock) / show() and hide() methods, and resize [almost]
+
+ // closable: Boolean
+ // Allow closure of this Node
+ closable: true,
+
+ // dockable: Boolean
+ // Allow minimizing of pane if true
+ dockable: true,
+
+ // resizable: Boolean
+ // Allow resizing of pane true if true
+ resizable: false,
+
+ // maxable: Boolean
+ // Horrible param name for "Can you maximize this floating pane?"
+ maxable: false,
+
+ // resizeAxis: String
+ // One of: x | xy | y to limit pane's sizing direction
+ resizeAxis: "xy",
+
+ // title: String
+ // Title to use in the header
+ title: "",
+
+ // dockTo: DomNode?
+ // if empty, will create private layout.Dock that scrolls with viewport
+ // on bottom span of viewport.
+ dockTo: "",
+
+ // duration: Integer
+ // Time is MS to spend toggling in/out node
+ duration: 400,
+
+ /*=====
+ // iconSrc: String
+ // [not implemented yet] will be either icon in titlepane to left
+ // of Title, and/or icon show when docked in a fisheye-like dock
+ // or maybe dockIcon would be better?
+ iconSrc: null,
+ =====*/
+
+ // contentClass: String
+ // The className to give to the inner node which has the content
+ contentClass: "dojoxFloatingPaneContent",
+
+ // animation holders for toggle
+ _showAnim: null,
+ _hideAnim: null,
+ // node in the dock (if docked)
+ _dockNode: null,
+
+ // privates:
+ _restoreState: {},
+ _allFPs: [],
+ _startZ: 100,
+
+ templateString: template,
+
+ attributeMap: lang.delegate(Widget.prototype.attributeMap, {
+ title: { type:"innerHTML", node:"titleNode" }
+ }),
+
+ postCreate: function(){
+ this.inherited(arguments);
+ new Moveable(this.domNode,{ handle: this.focusNode });
+ //this._listener = dojo.subscribe("/dnd/move/start",this,"bringToTop");
+
+ if(!this.dockable){ this.dockNode.style.display = "none"; }
+ if(!this.closable){ this.closeNode.style.display = "none"; }
+ if(!this.maxable){
+ this.maxNode.style.display = "none";
+ this.restoreNode.style.display = "none";
+ }
+ if(!this.resizable){
+ this.resizeHandle.style.display = "none";
+ }else{
+ this.domNode.style.width = domGeom.getMarginBox(this.domNode).w + "px";
+ }
+ this._allFPs.push(this);
+ this.domNode.style.position = "absolute";
+
+ this.bgIframe = new BackgroundIframe(this.domNode);
+ this._naturalState = domGeom.position(this.domNode);
+ },
+
+ startup: function(){
+ if(this._started){ return; }
+
+ this.inherited(arguments);
+
+ if(this.resizable){
+ if(has("ie")){
+ this.canvas.style.overflow = "auto";
+ }else{
+ this.containerNode.style.overflow = "auto";
+ }
+
+ this._resizeHandle = new ResizeHandle({
+ targetId: this.id,
+ resizeAxis: this.resizeAxis
+ },this.resizeHandle);
+
+ }
+
+ if(this.dockable){
+ // FIXME: argh.
+ var tmpName = this.dockTo;
+
+ if(this.dockTo){
+ this.dockTo = dijit.byId(this.dockTo);
+ }else{
+ this.dockTo = dijit.byId('dojoxGlobalFloatingDock');
+ }
+
+ if(!this.dockTo){
+ var tmpId, tmpNode;
+ // we need to make our dock node, and position it against
+ // .dojoxDockDefault .. this is a lot. either dockto="node"
+ // and fail if node doesn't exist or make the global one
+ // once, and use it on empty OR invalid dockTo="" node?
+ if(tmpName){
+ tmpId = tmpName;
+ tmpNode = dom.byId(tmpName);
+ }else{
+ tmpNode = domConstruct.create('div', null, winUtil.body());
+ domClass.add(tmpNode,"dojoxFloatingDockDefault");
+ tmpId = 'dojoxGlobalFloatingDock';
+ }
+ this.dockTo = new Dock({ id: tmpId, autoPosition: "south" }, tmpNode);
+ this.dockTo.startup();
+ }
+
+ if((this.domNode.style.display == "none")||(this.domNode.style.visibility == "hidden")){
+ // If the FP is created dockable and non-visible, start up docked.
+ this.minimize();
+ }
+ }
+ this.connect(this.focusNode,"onmousedown","bringToTop");
+ this.connect(this.domNode, "onmousedown","bringToTop");
+
+ // Initial resize to give child the opportunity to lay itself out
+ this.resize(domGeom.position(this.domNode));
+
+ this._started = true;
+ },
+
+ setTitle: function(/* String */ title){
+ // summary:
+ // Update the Title bar with a new string
+ kernel.deprecated("pane.setTitle", "Use pane.set('title', someTitle)", "2.0");
+ this.set("title", title);
+ },
+
+ close: function(){
+ // summary:
+ // Close and destroy this widget
+ if(!this.closable){ return; }
+ connectUtil.unsubscribe(this._listener);
+ this.hide(lang.hitch(this,function(){
+ this.destroyRecursive();
+ }));
+ },
+
+ hide: function(/* Function? */ callback){
+ // summary:
+ // Close, but do not destroy this FloatingPane
+ baseFx.fadeOut({
+ node:this.domNode,
+ duration:this.duration,
+ onEnd: lang.hitch(this,function() {
+ this.domNode.style.display = "none";
+ this.domNode.style.visibility = "hidden";
+ if(this.dockTo && this.dockable){
+ this.dockTo._positionDock(null);
+ }
+ if(callback){
+ callback();
+ }
+ })
+ }).play();
+ },
+
+ show: function(/* Function? */callback){
+ // summary:
+ // Show the FloatingPane
+ var anim = baseFx.fadeIn({node:this.domNode, duration:this.duration,
+ beforeBegin: lang.hitch(this,function(){
+ this.domNode.style.display = "";
+ this.domNode.style.visibility = "visible";
+ if (this.dockTo && this.dockable) { this.dockTo._positionDock(null); }
+ if (typeof callback == "function") { callback(); }
+ this._isDocked = false;
+ if (this._dockNode) {
+ this._dockNode.destroy();
+ this._dockNode = null;
+ }
+ })
+ }).play();
+ // use w / h from content box dimensions and x / y from position
+ var contentBox = domGeom.getContentBox(this.domNode)
+ this.resize(lang.mixin(domGeom.position(this.domNode), {w: contentBox.w, h: contentBox.h}));
+ this._onShow(); // lazy load trigger
+ },
+
+ minimize: function(){
+ // summary:
+ // Hide and dock the FloatingPane
+ if(!this._isDocked){ this.hide(lang.hitch(this,"_dock")); }
+ },
+
+ maximize: function(){
+ // summary:
+ // Make this FloatingPane full-screen (viewport)
+ if(this._maximized){ return; }
+ this._naturalState = domGeom.position(this.domNode);
+ if(this._isDocked){
+ this.show();
+ setTimeout(lang.hitch(this,"maximize"),this.duration);
+ }
+ domClass.add(this.focusNode,"floatingPaneMaximized");
+ this.resize(windowLib.getBox());
+ this._maximized = true;
+ },
+
+ _restore: function(){
+ if(this._maximized){
+ this.resize(this._naturalState);
+ domClass.remove(this.focusNode,"floatingPaneMaximized");
+ this._maximized = false;
+ }
+ },
+
+ _dock: function(){
+ if(!this._isDocked && this.dockable){
+ this._dockNode = this.dockTo.addNode(this);
+ this._isDocked = true;
+ }
+ },
+
+ resize: function(/* Object */dim){
+ // summary:
+ // Size the FloatingPane and place accordingly
+ dim = dim || this._naturalState;
- this._currentState = dim;
++ this._naturalState = dim;
+
+ // From the ResizeHandle we only get width and height information
+ var dns = this.domNode.style;
+ if("t" in dim){ dns.top = dim.t + "px"; }
+ else if("y" in dim){ dns.top = dim.y + "px"; }
+ if("l" in dim){ dns.left = dim.l + "px"; }
+ else if("x" in dim){ dns.left = dim.x + "px"; }
+ dns.width = dim.w + "px";
+ dns.height = dim.h + "px";
+
+ // Now resize canvas
+ var mbCanvas = { l: 0, t: 0, w: dim.w, h: (dim.h - this.focusNode.offsetHeight) };
+ domGeom.setMarginBox(this.canvas, mbCanvas);
+
+ // If the single child can resize, forward resize event to it so it can
+ // fit itself properly into the content area
+ this._checkIfSingleChild();
+ if(this._singleChild && this._singleChild.resize){
+ this._singleChild.resize(mbCanvas);
+ }
+ },
+
+ bringToTop: function(){
+ // summary:
+ // bring this FloatingPane above all other panes
+ var windows = arrayUtil.filter(
+ this._allFPs,
+ function(i){
+ return i !== this;
+ },
+ this);
+ windows.sort(function(a, b){
+ return a.domNode.style.zIndex - b.domNode.style.zIndex;
+ });
+ windows.push(this);
+
+ arrayUtil.forEach(windows, function(w, x){
+ w.domNode.style.zIndex = this._startZ + (x * 2);
+ domClass.remove(w.domNode, "dojoxFloatingPaneFg");
+ }, this);
+ domClass.add(this.domNode, "dojoxFloatingPaneFg");
+ },
+
+ destroy: function(){
+ // summary:
+ // Destroy this FloatingPane completely
+ this._allFPs.splice(arrayUtil.indexOf(this._allFPs, this), 1);
+ if(this._resizeHandle){
+ this._resizeHandle.destroy();
+ }
+ this.inherited(arguments);
+ }
+});
+
+return FloatingPane;
+});
diff --cc dojox/mobile/IconItem.js
index 2190559,0000000..033789d
mode 100644,000000..100644
--- a/dojox/mobile/IconItem.js
+++ b/dojox/mobile/IconItem.js
@@@ -1,412 -1,0 +1,412 @@@
+define([
+ "dojo/_base/declare",
+ "dojo/_base/event",
+ "dojo/_base/lang",
+ "dojo/sniff",
+ "dojo/_base/window",
+ "dojo/dom-class",
+ "dojo/dom-construct",
+ "dojo/dom-geometry",
+ "dojo/dom-style",
+ "./_ItemBase",
+ "./Badge",
+ "./TransitionEvent",
+ "./iconUtils",
+ "./lazyLoadUtils",
+ "./viewRegistry",
+ "./_css3",
+ "dojo/has!dojo-bidi?dojox/mobile/bidi/IconItem"
+], function(declare, event, lang, has, win, domClass, domConstruct, domGeometry, domStyle, ItemBase, Badge, TransitionEvent, iconUtils, lazyLoadUtils, viewRegistry, css3, BidiIconItem){
+
+ // module:
+ // dojox/mobile/IconItem
+
+ var IconItem = declare(has("dojo-bidi") ? "dojox.mobile.NonBidiIconItem" : "dojox.mobile.IconItem", ItemBase, {
+ // summary:
+ // An icon item widget.
+ // description:
+ // IconItem represents an item that has an application component
+ // and its icon image. You can tap the icon to open the
+ // corresponding application component. You can also use the icon
+ // to move to a different view by specifying either of the moveTo,
+ // href or url parameters.
+
+ // lazy: String
+ // If true, the content of the widget, which includes dojo markup,
+ // is instantiated lazily. That is, only when the widget is opened
+ // by the user, the required modules are loaded and the content
+ // widgets are instantiated.
+ // This option works both in the sync and async loader mode.
+ lazy: false,
+
+ // requires: String
+ // Comma-separated required module names to be lazily loaded. This
+ // property is effective only when lazy=true. All the modules
+ // specified with data-dojo-type and their depending modules are
+ // automatically loaded by the IconItem when it is opened.
+ // However, if you need other extra modules to be loaded, use this parameter.
+ // This option works both in the sync and async loader mode.
+ requires: "",
+
+ // timeout: String
+ // Duration of highlight in seconds.
+ timeout: 10,
+
+ // content: String
+ // An HTML fragment to embed as icon content.
+ content: "",
+
+ // badge: String
+ // A text to show in a badge (ex. "55").
+ badge: "",
+
+ // badgeClass: String
+ // A class name of a DOM button for a badge.
+ badgeClass: "mblDomButtonRedBadge",
+
+ // deletable: Boolean
+ // If true, you can delete this IconItem by clicking on the delete
+ // icon during edit mode.
+ // If false, the delete icon is not displayed during edit mode so
+ // that it cannot be deleted.
+ deletable: true,
+
+ // deleteIcon: String
+ // A delete icon to display at the top-left corner of the item
+ // during edit mode. The value can be either a path for an image
+ // file or a class name of a DOM button.
+ deleteIcon: "",
+
+ // tag: String
+ // A name of the HTML tag to create as domNode.
+ tag: "li",
+
+ /* internal properties */
+ // Note these are overrides for similar properties defined in _ItemBase.
+ paramsToInherit: "transition,icon,deleteIcon,badgeClass,deleteIconTitle,deleteIconRole",
+ baseClass: "mblIconItem",
+ _selStartMethod: "touch",
+ _selEndMethod: "none",
+
+ destroy: function(){
+ if(this.badgeObj){
+ delete this.badgeObj;
+ }
+ this.inherited(arguments);
+ },
+
+ buildRendering: function(){
+ this.domNode = this.srcNodeRef || domConstruct.create(this.tag);
+
+ if(this.srcNodeRef){
+ // reparent
+ this._tmpNode = domConstruct.create("div");
+ for(var i = 0, len = this.srcNodeRef.childNodes.length; i < len; i++){
+ this._tmpNode.appendChild(this.srcNodeRef.firstChild);
+ }
+ }
+
+ this.iconDivNode = domConstruct.create("div", {className:"mblIconArea"}, this.domNode);
+ this.iconParentNode = domConstruct.create("div", {className:"mblIconAreaInner"}, this.iconDivNode);
+ this.labelNode = domConstruct.create("span", {className:"mblIconAreaTitle"}, this.iconDivNode);
+
+ this.inherited(arguments);
+ },
+
+ startup: function(){
+ if(this._started){ return; }
+
+ var p = this.getParent();
+ require([p.iconItemPaneClass], lang.hitch(this, function(module){
+ var w = this.paneWidget = new module(p.iconItemPaneProps);
+ this.containerNode = w.containerNode;
+ if(this._tmpNode){
+ // reparent
+ for(var i = 0, len = this._tmpNode.childNodes.length; i < len; i++){
+ w.containerNode.appendChild(this._tmpNode.firstChild);
+ }
+ this._tmpNode = null;
+ }
+ p.paneContainerWidget.addChild(w, this.getIndexInParent());
+ w.set("label", this.label);
+ this._clickCloseHandle = this.connect(w.closeIconNode, "onclick", "_closeIconClicked");
+ this._keydownCloseHandle = this.connect(w.closeIconNode, "onkeydown", "_closeIconClicked"); // for desktop browsers
+ }));
+
+ this.inherited(arguments);
+ if(!this._isOnLine){
+ this._isOnLine = true;
+ // retry applying the attribute for which the custom setter delays the actual
+ // work until _isOnLine is true.
+ this.set("icon", this._pendingIcon !== undefined ? this._pendingIcon : this.icon);
+ // Not needed anymore (this code executes only once per life cycle):
+ delete this._pendingIcon;
+ }
+ if(!this.icon && p.defaultIcon){
+ this.set("icon", p.defaultIcon);
+ }
+
+ this._dragstartHandle = this.connect(this.domNode, "ondragstart", event.stop);
+ this.connect(this.domNode, "onkeydown", "_onClick"); // for desktop browsers
+ },
+
+ highlight: function(/*Number?*/timeout){
+ // summary:
+ // Shakes the icon 10 seconds.
+ domClass.add(this.iconDivNode, "mblVibrate");
+ timeout = (timeout !== undefined) ? timeout : this.timeout;
+ if(timeout > 0){
+ var _this = this;
+ _this.defer(function(){
+ _this.unhighlight();
+ }, timeout*1000);
+ }
+ },
+
+ unhighlight: function(){
+ // summary:
+ // Stops shaking the icon.
+ if(!has("ie") && has("trident") === 7){
+ // Workaround on IE11: if just removing the style, the icon continues to shake
+ domStyle.set(this.iconDivNode, "animation-name", "");
+ }
+ domClass.remove(this.iconDivNode, "mblVibrate");
+ },
+
+ isOpen: function(e){
+ // summary:
+ // Returns true if the icon is open.
+ return this.paneWidget.isOpen();
+ },
+
+ _onClick: function(e){
+ // summary:
+ // Internal handler for click events.
+ // tags:
+ // private
+ if(this.getParent().isEditing || e && e.type === "keydown" && e.keyCode !== 13){ return; }
+ if(this.onClick(e) === false){ return; } // user's click action
+ this.defaultClickAction(e);
+ },
+
+ onClick: function(/*Event*/ /*===== e =====*/){
+ // summary:
+ // User-defined function to handle clicks.
+ // tags:
+ // callback
+ },
+
+ _onNewWindowOpened: function(e){
+ // Override from _ItemBase
+ this.set("selected", false);
+ },
+
+ _prepareForTransition: function(e, transOpts){
+ // Override from _ItemBase
+ if(transOpts){
+ this.defer(function(d){
+ this.set("selected", false);
+ }, 1500);
+ return true;
+ }else{
+ if(this.getParent().transition === "below" && this.isOpen()){
+ this.close();
+ }else{
+ this.open(e);
+ }
+ return false;
+ }
+ },
+
+ _closeIconClicked: function(e){
+ // summary:
+ // Internal handler for click events.
+ // tags:
+ // private
+ if(e){
+ if(e.type === "keydown" && e.keyCode !== 13){ return; }
+ if(this.closeIconClicked(e) === false){ return; } // user's click action
+ this.defer(function(d){ this._closeIconClicked(); });
+ return;
+ }
+ this.close();
+ },
+
+ closeIconClicked: function(/*Event*/ /*===== e =====*/){
+ // summary:
+ // User-defined function to handle clicks for the close icon.
+ // tags:
+ // callback
+ },
+
+ open: function(e){
+ // summary:
+ // Opens the icon content, or makes a transition.
+ var parent = this.getParent(); // IconContainer
+ if(this.transition === "below"){
+ if(parent.single){
+ parent.closeAll();
+ }
+ this._open_1();
+ }else{
+ parent._opening = this;
+ if(parent.single){
+ this.paneWidget.closeHeaderNode.style.display = "none";
+ if(!this.isOpen()){
+ parent.closeAll();
+ }
+ parent.appView._heading.set("label", this.label);
+ }
+ this.moveTo = parent.id + "_mblApplView";
+ new TransitionEvent(this.domNode, this.getTransOpts(), e).dispatch();
+ }
+ },
+
+ _open_1: function(){
+ // summary:
+ // Opens the icon content for the 'below' transition.
+ // tags:
+ // private
+ this.paneWidget.show();
+ this.unhighlight();
+ if(this.lazy){
+ lazyLoadUtils.instantiateLazyWidgets(this.containerNode, this.requires);
+ this.lazy = false;
+ }
+ this.scrollIntoView(this.paneWidget.domNode);
+ this.onOpen();
+ },
+
+ scrollIntoView: function(/*DomNode*/node){
+ // summary:
+ // Scrolls until the given node is in the view.
+ var s = viewRegistry.getEnclosingScrollable(node);
+ if(s){ // this node is placed inside scrollable
+ var dim = s.getDim();
+ if(dim.c.h >= dim.d.h){ // #16306: only if the content is larger than the display area
+ s.scrollIntoView(node, true);
+ }
+ }else{
+ win.global.scrollBy(0, domGeometry.position(node, false).y);
+ }
+ },
+
+ close: function(/*Boolean?*/noAnimation){
+ // summary:
+ // Closes the icon content.
+ if(!this.isOpen()){ return; }
+ this.set("selected", false);
+ if(has("css3-animations") && !noAnimation){
+ var contentNode = this.paneWidget.domNode;
+ if(this.getParent().transition == "below"){
+ domClass.add(contentNode, "mblCloseContent mblShrink");
+ var nodePos = domGeometry.position(contentNode, true);
+ var targetPos = domGeometry.position(this.domNode, true);
+ var origin = (targetPos.x + targetPos.w/2 - nodePos.x) + "px " + (targetPos.y + targetPos.h/2 - nodePos.y) + "px";
+ domStyle.set(contentNode, css3.add({}, { transformOrigin:origin }));
+ }else{
+ domClass.add(contentNode, "mblCloseContent mblShrink0");
+ }
+ }else{
+ this.paneWidget.hide();
+ }
+ this.onClose();
+ },
+
+ onOpen: function(){
+ // summary:
+ // Stub method to allow the application to connect.
+ },
+
+ onClose: function(){
+ // summary:
+ // Stub method to allow the application to connect.
+ },
+
+ _setLabelAttr: function(/*String*/text){
+ // tags:
+ // private
+ this.label = text;
+ var s = this._cv ? this._cv(text) : text;
+ this.labelNode.innerHTML = s;
+ if(this.paneWidget){
+ this.paneWidget.set("label", text);
+ }
+ },
+
+ _getBadgeAttr: function(){
+ // tags:
+ // private
+ return this.badgeObj ? this.badgeObj.getValue() : null;
+ },
+
+ _setBadgeAttr: function(/*String*/value){
+ // tags:
+ // private
+ if(!this.badgeObj){
+ this.badgeObj = new Badge({fontSize:14, className:this.badgeClass});
+ domStyle.set(this.badgeObj.domNode, {
+ position: "absolute",
+ top: "-2px",
+ right: "2px"
+ });
+ }
+ this.badgeObj.setValue(value);
+ if(value){
+ this.iconDivNode.appendChild(this.badgeObj.domNode);
+ }else{
+ this.iconDivNode.removeChild(this.badgeObj.domNode);
+ }
+ },
+
+ _setDeleteIconAttr: function(icon){
+ // tags:
+ // private
+ if(!this.getParent()){ return; } // icon may be invalid because inheritParams is not called yet
+
+ this._set("deleteIcon", icon);
+ icon = this.deletable ? icon : "";
+ this.deleteIconNode = iconUtils.setIcon(icon, this.deleteIconPos, this.deleteIconNode,
+ this.deleteIconTitle || this.alt, this.iconDivNode);
+ if(this.deleteIconNode){
+ domClass.add(this.deleteIconNode, "mblIconItemDeleteIcon");
+ if(this.deleteIconRole){
+ this.deleteIconNode.setAttribute("role", this.deleteIconRole);
+ }
+ }
+ },
+
+ _setContentAttr: function(/*String|DomNode*/data){
+ // tags:
+ // private
+ var root;
+ if(!this.paneWidget){
+ if(!this._tmpNode){
+ this._tmpNode = domConstruct.create("div");
+ }
+ root = this._tmpNode;
+ }else{
+ root = this.paneWidget.containerNode;
+ }
+
+ if(typeof data === "object"){
+ domConstruct.empty(root);
+ root.appendChild(data);
+ }else{
+ root.innerHTML = data;
+ }
+ },
+
+ _setSelectedAttr: function(/*Boolean*/selected){
+ // summary:
+ // Makes this widget in the selected or unselected state.
+ // tags:
+ // private
+ this.inherited(arguments);
- domStyle.set(this.iconNode, "opacity",
++ this.iconNode && domStyle.set(this.iconNode, "opacity",
+ selected ? this.getParent().pressedIconOpacity : 1);
+ }
+ });
+
+ return has("dojo-bidi") ? declare("dojox.mobile.IconItem", [IconItem, BidiIconItem]) : IconItem;
+});
diff --cc dojox/package.json
index 5709833,0000000..699c5b6
mode 100644,000000..100644
--- a/dojox/package.json
+++ b/dojox/package.json
@@@ -1,27 -1,0 +1,27 @@@
+{
+ "name": "dojox",
- "version":"1.10.1",
++ "version":"1.10.2",
+ "directories": {
+ "lib": "."
+ },
+ "main": "main",
+ "dependencies": {
- "dojo":"1.10.1",
- "dijit":"1.10.1"
++ "dojo":"1.10.2",
++ "dijit":"1.10.2"
+ },
+ "description": "Dojo eXtensions, a rollup of many useful sub-projects and varying states of maturity – from very stable and robust, to alpha and experimental. See individual projects contain README files for details.",
+ "licenses": [
+ {
+ "type": "AFLv2.1",
+ "url": "http://trac.dojotoolkit.org/browser/dojox/trunk/LICENSE#L43"
+ },
+ {
+ "type": "BSD",
+ "url": "http://trac.dojotoolkit.org/browser/dojox/trunk/LICENSE#L13"
+ }
+ ],
+ "bugs": "http://bugs.dojotoolkit.org/",
+ "keywords": ["JavaScript", "Dojo", "Toolkit", "DojoX"],
+ "homepage": "http://dojotoolkit.org/",
+ "dojoBuild": "dojox.profile.js"
+}
diff --cc dojox/widget/Toaster.js
index 29fc9ae,0000000..dba16b7
mode 100644,000000..100644
--- a/dojox/widget/Toaster.js
+++ b/dojox/widget/Toaster.js
@@@ -1,294 -1,0 +1,296 @@@
+define([
+ "dojo/_base/declare", // declare
+ "dojo/_base/lang", // lang.getObject...
+ "dojo/_base/connect", // connect.connect, connect.subscribe
+ "dojo/_base/fx", // fx.fadeOut
+ "dojo/dom-style", // domStyle.set
+ "dojo/dom-class", // domClass.add
+ "dojo/dom-geometry", // domGeometry.getMarginBox
+ "dijit/registry", // registry.getUniqueId()
+ "dijit/_WidgetBase",
+ "dijit/_TemplatedMixin",
+ "dijit/BackgroundIframe",
+ "dojo/fx",
+ "dojo/has",
+ "dojo/_base/window",
+ "dojo/window"
+], function(declare, lang, connect, baseFx, domStyle, domClass, domGeometry, registry, WidgetBase, Templated, BackgroundIframe, coreFx, has, baseWindow, window){
+
+ lang.getObject("dojox.widget", true);
-
++
+ var capitalize = function(/* String */w){
+ return w.substring(0,1).toUpperCase() + w.substring(1);
+ };
+
+ return declare("dojox.widget.Toaster", [WidgetBase, Templated], {
+ // summary:
+ // Message that slides in from the corner of the screen, used for notifications
+ // like "new email".
+
+ templateString: '<div class="dijitToasterClip" dojoAttachPoint="clipNode"><div class="dijitToasterContainer" dojoAttachPoint="containerNode" dojoAttachEvent="onclick:onSelect"><div class="dijitToasterContent" dojoAttachPoint="contentNode"></div></div></div>',
+
+ // messageTopic: String
+ // Name of topic; anything published to this topic will be displayed as a message.
+ // Message format is either String or an object like
+ // {message: "hello word", type: "error", duration: 500}
+ messageTopic: "",
+
+ // messageTypes: Enumeration
+ // Possible message types.
+ messageTypes: {
+ MESSAGE: "message",
+ WARNING: "warning",
+ ERROR: "error",
+ FATAL: "fatal"
+ },
+
+ // defaultType: String
+ // If message type isn't specified (see "messageTopic" parameter),
+ // then display message as this type.
+ // Possible values in messageTypes enumeration ("message", "warning", "error", "fatal")
+ defaultType: "message",
+
+ // positionDirection: String
+ // Position from which message slides into screen, one of
+ // ["br-up", "br-left", "bl-up", "bl-right", "tr-down", "tr-left", "tl-down", "tl-right"]
+ positionDirection: "br-up",
+
+ // positionDirectionTypes: Array
+ // Possible values for positionDirection parameter
+ positionDirectionTypes: ["br-up", "br-left", "bl-up", "bl-right", "tr-down", "tr-left", "tl-down", "tl-right"],
+
+ // duration: Integer
+ // Number of milliseconds to show message
+ duration: 2000,
+
+ // slideDuration: Integer
+ // Number of milliseconds for the slide animation, increasing will cause the Toaster
+ // to slide in more slowly.
+ slideDuration: 500,
+
+ // separator: String
+ // String used to separate messages if consecutive calls are made to setContent before previous messages go away
+ separator: "<hr></hr>",
+
+ postCreate: function(){
+ this.inherited(arguments);
+ this.hide();
+
+ // place node as a child of body for positioning
+ baseWindow.body().appendChild(this.domNode);
+
+ if(this.messageTopic){
+ connect.subscribe(this.messageTopic, this, "_handleMessage");
+ }
+ },
+
+ _handleMessage: function(/*String|Object*/message){
+ if(lang.isString(message)){
+ this.setContent(message);
+ }else{
+ this.setContent(message.message, message.type, message.duration);
+ }
+ },
+
+ setContent: function(/*String|Function*/message, /*String*/messageType, /*int?*/duration){
+ // summary:
+ // sets and displays the given message and show duration
+ // message:
+ // the message. If this is a function, it will be called with this toaster widget as the only argument.
+ // messageType:
+ // type of message; possible values in messageTypes enumeration ("message", "warning", "error", "fatal")
+ // duration:
+ // duration in milliseconds to display message before removing it. Widget has default value.
+ duration = duration||this.duration;
+ // sync animations so there are no ghosted fades and such
+ if(this.slideAnim){
+ if(this.slideAnim.status() != "playing"){
+ this.slideAnim.stop();
+ }
+ if(this.slideAnim.status() == "playing" || (this.fadeAnim && this.fadeAnim.status() == "playing")){
+ setTimeout(lang.hitch(this, function(){
+ this.setContent(message, messageType, duration);
+ }), 50);
+ return;
+ }
+ }
+
+ // determine type of content and apply appropriately
+ for(var type in this.messageTypes){
+ domClass.remove(this.containerNode, "dijitToaster" + capitalize(this.messageTypes[type]));
+ }
+
+ domStyle.set(this.containerNode, "opacity", 1);
+
+ this._setContent(message);
+
+ domClass.add(this.containerNode, "dijitToaster" + capitalize(messageType || this.defaultType));
+
+ // now do funky animation of widget appearing from
+ // bottom right of page and up
+ this.show();
+ var nodeSize = domGeometry.getMarginBox(this.containerNode);
+ this._cancelHideTimer();
+ if(this.isVisible){
+ this._placeClip();
+ //update hide timer if no sticky message in stack
+ if(!this._stickyMessage) {
+ this._setHideTimer(duration);
+ }
+ }else{
+ var style = this.containerNode.style;
+ var pd = this.positionDirection;
+ // sets up initial position of container node and slide-out direction
+ if(pd.indexOf("-up") >= 0){
+ style.left=0+"px";
+ style.top=nodeSize.h + 10 + "px";
+ }else if(pd.indexOf("-left") >= 0){
+ style.left=nodeSize.w + 10 +"px";
+ style.top=0+"px";
+ }else if(pd.indexOf("-right") >= 0){
+ style.left = 0 - nodeSize.w - 10 + "px";
+ style.top = 0+"px";
+ }else if(pd.indexOf("-down") >= 0){
+ style.left = 0+"px";
+ style.top = 0 - nodeSize.h - 10 + "px";
+ }else{
+ throw new Error(this.id + ".positionDirection is invalid: " + pd);
+ }
+ this.slideAnim = coreFx.slideTo({
+ node: this.containerNode,
+ top: 0, left: 0,
+ duration: this.slideDuration});
+ this.connect(this.slideAnim, "onEnd", function(nodes, anim){
+ //we build the fadeAnim here so we dont have to duplicate it later
+ // can't do a fadeHide because we're fading the
+ // inner node rather than the clipping node
+ this.fadeAnim = baseFx.fadeOut({
+ node: this.containerNode,
+ duration: 1000});
+ this.connect(this.fadeAnim, "onEnd", function(evt){
+ this.isVisible = false;
+ this.hide();
+ });
+ this._setHideTimer(duration);
+ this.connect(this, 'onSelect', function(evt){
+ this._cancelHideTimer();
+ //force clear sticky message
+ this._stickyMessage=false;
+ this.fadeAnim.play();
+ });
+
+ this.isVisible = true;
+ });
+ this.slideAnim.play();
+ }
+ },
+
+ _setContent: function(message){
+ if(lang.isFunction(message)){
+ message(this);
+ return;
+ }
+ if(message && this.isVisible){
+ message = this.contentNode.innerHTML + this.separator + message;
+ }
+ this.contentNode.innerHTML = message;
+ },
+ _cancelHideTimer:function(){
+ if (this._hideTimer){
+ clearTimeout(this._hideTimer);
+ this._hideTimer=null;
+ }
+ },
+
+ _setHideTimer:function(duration){
+ this._cancelHideTimer();
+ //if duration == 0 we keep the message displayed until clicked
+ if(duration>0){
+ this._cancelHideTimer();
+ this._hideTimer=setTimeout(lang.hitch(this, function(evt){
+ // we must hide the iframe in order to fade
+ // TODO: figure out how to fade with a BackgroundIframe
+ if(this.bgIframe && this.bgIframe.iframe){
+ this.bgIframe.iframe.style.display="none";
+ }
+ this._hideTimer=null;
+ //force clear sticky message
+ this._stickyMessage=false;
+ this.fadeAnim.play();
+ }), duration);
+ }
+ else
+ this._stickyMessage=true;
+ },
+
+ _placeClip: function(){
+ var view = window.getBox();
+
+ var nodeSize = domGeometry.getMarginBox(this.containerNode);
+
+ var style = this.clipNode.style;
+ // sets up the size of the clipping node
+ style.height = nodeSize.h+"px";
+ style.width = nodeSize.w+"px";
+
+ // sets up the position of the clipping node
+ var pd = this.positionDirection;
+ if(pd.match(/^t/)){
+ style.top = view.t+"px";
+ }else if(pd.match(/^b/)){
+ style.top = (view.h - nodeSize.h - 2 + view.t)+"px";
+ }
+ if(pd.match(/^[tb]r-/)){
+ style.left = (view.w - nodeSize.w - 1 - view.l)+"px";
+ }else if(pd.match(/^[tb]l-/)){
+ style.left = 0 + "px";
+ }else if(pd.match(/^[tb]c-/)){
+ style.left = Math.round((view.w - nodeSize.w - 1 - view.l)/2)+"px";
+ }
+
+ style.clip = "rect(0px, " + nodeSize.w + "px, " + nodeSize.h + "px, 0px)";
+ if(has("ie")){
+ if(!this.bgIframe){
- this.clipNode.id = registry.getUniqueId("dojox_widget_Toaster_clipNode");
++ if (!this.clipNode.id) {
++ this.clipNode.id = registry.getUniqueId("dojox_widget_Toaster_clipNode");
++ }
+ this.bgIframe = new BackgroundIframe(this.clipNode);
+ }
+ var iframe = this.bgIframe.iframe;
+ if(iframe){ iframe.style.display="block"; }
+ }
+ },
+
+ onSelect: function(/*Event*/e){
+ // summary:
+ // callback for when user clicks the message
+ },
+
+ show: function(){
+ // summary:'
+ // show the Toaster
+ domStyle.set(this.domNode, 'display', 'block');
+
+ this._placeClip();
+
+ if(!this._scrollConnected){
+ this._scrollConnected = connect.connect(window, "onscroll", this, this._placeClip);
+ }
+ },
+
+ hide: function(){
+ // summary:
+ // hide the Toaster
+
+ domStyle.set(this.domNode, 'display', 'none');
+
+ if(this._scrollConnected){
+ connect.disconnect(this._scrollConnected);
+ this._scrollConnected = false;
+ }
+
+ domStyle.set(this.containerNode, "opacity", 1);
+ }
+ });
+
+});
diff --cc util/build/buildControl.js
index 1eb84be,0000000..9a3613f
mode 100644,000000..100644
--- a/util/build/buildControl.js
+++ b/util/build/buildControl.js
@@@ -1,742 -1,0 +1,750 @@@
+define([
+ "require",
+ "dojo/_base/lang",
+ "./argv",
+ "./fs",
+ "./fileUtils",
+ "./buildControlDefault",
+ "./v1xProfiles",
+ "./stringify",
+ "./process",
+ "./messages",
+ "dojo/text!./help.txt"
+], function(require, lang, argv, fs, fileUtils, bc, v1xProfiles, stringify, process, messages, helpText){
+ //
+ // Process the arguments given on the command line to build up a profile object that is used to instruct and control
+ // the build process.
+ //
+ // This modules is a bit tedious. Is methodically goes through each option set, cleaning and conditioning user input making it
+ // easy to use for the remainder of the program. Readers are advised to tackle it top-to-bottom. There is no magic...just
+ // a whole bunch of imperative programming.
+ //
+
+ if(!isNaN(argv)){
+ // if argv is a number, then it's an exit code
+ bc.exitCode = argv;
+ return bc;
+ }
+
+ eval(require.scopeify("./fs, ./fileUtils, ./v1xProfiles"));
+ var
+ isString = function(it){
+ return typeof it === "string";
+ },
+
+ isNonemptyString = function(it){
+ return isString(it) && it.length;
+ },
+
+ isDefined = function(it){
+ return typeof it !="undefined";
+ },
+
+ cleanupFilenamePair = function(item, srcBasePath, destBasePath, hint){
+ var result;
+ if(isString(item)){
+ result = [computePath(item, srcBasePath), computePath(item, destBasePath)];
+ }else{
+ result = [computePath(item[0], srcBasePath), computePath(item[1], destBasePath)].concat(item.slice(2));
+ }
+ if(!isAbsolutePath(result[0]) || !isAbsolutePath(result[1])){
+ bc.log("inputInvalidPath", ["path", item, "hint", hint]);
+ }
+ return result;
+ },
+
+ slashTerminate = function(path){
+ return path + /\/$/.test(path) ? "" : "/";
+ },
+
+ isEmpty = function(it){
+ for(var p in it) return false;
+ return true;
+ },
+
+ cleanDeprecated = function(o, inputFile){
+ var deprecated = [];
+ for(p in o){
+ if(/^(log|loader|xdDojoPath|scopeDjConfig|xdScopeArgs|xdDojoScopeName|expandProvide|buildLayers|query|removeDefaultNameSpaces|addGuards)$/.test(p)){
+ deprecated.push(p);
+ bc.log("inputDeprecated", ["switch", p, inputFile]);
+ }
+ }
+ deprecated.forEach(function(p){
+ delete o[p];
+ });
+ },
+
+ mix = function(dest, src){
+ dest = dest || {};
+ src = src || {};
+ for(var p in src) dest[p] = src[p];
+ return dest;
+ },
+
+ mixPackage = function(packageInfo){
+ var name = packageInfo.name;
+ bc.packageMap[name] = mix(bc.packageMap[name], packageInfo);
+ },
+
+ // mix a profile object into the global profile object
+ mixProfileObject = function(src){
+ cleanDeprecated(src, src.selfFilename);
+
+ // the profile properties...
+ // paths, plugins, transforms, staticHasFeatures
+ // ...are mixed one level deep; messageCategories, messages, packages, and packagePaths require special handling; all others are over-written
+ // FIXME: the only way to modify the transformJobs vector is to create a whole new vector?
+ for(var p in src){
+ if(!/paths|plugins|messages|transforms|staticHasFeatures|packages|packagePaths|defaultConfig/.test(p)){
+ bc[p] = src[p];
+ }
+ }
+
+ // the one-level-deep mixers
+ ["paths","plugins","transforms","staticHasFeatures"].forEach(function(p){
+ bc[p] = mix(bc[p], src[p]);
+ });
+
+ // messages require special handling
+ if(src.messageCategories){
+ for(p in src.messageCategories){
+ bc.addCategory(p, src.messageCategories[p]);
+ }
+ }
+ (src.messages || []).forEach(function(item){bc.addMessage.apply(bc, item);});
+
+ // packagePaths and packages require special processing to get their contents into packageMap; do that first...
+ // process packagePaths before packages before packageMap since packagePaths is less specific than
+ // packages is less specific than packageMap. Notice that attempts to edit an already-existing package
+ // only edits specific package properties given (see mixPackage, above)
+ for(var base in src.packagePaths){
+ src.packagePaths[base].forEach(function(packageInfo){
+ if(isString(packageInfo)){
+ packageInfo = {name:packageInfo};
+ }
+ packageInfo.location = catPath(base, packageInfo.name);
+ mixPackage(packageInfo);
+ });
+ };
+ (src.packages || []).forEach(function(packageInfo){
+ if(isString(packageInfo)){
+ packageInfo = {name:packageInfo};
+ }
+ mixPackage(packageInfo);
+ });
+
+ // defaultConfig requires special handling
+ for(p in src.defaultConfig){
+ if(p=="hasCache"){
+ mix(bc.defaultConfig.hasCache, src.defaultConfig.hasCache);
+ }else{
+ bc.defaultConfig[p] = src.defaultConfig[p];
+ }
+ }
+ };
+
+ argv.args.profiles.forEach(function(item){
+ var temp = mix({}, item),
+ build = item.build;
+ delete temp.build;
+ mixProfileObject(temp);
+ build && mixProfileObject(build);
+ });
+
+ cleanDeprecated(argv.args, "command line");
+
+ // lastly, explicit command line switches override any evaluated profile objects
+ for(var argName in argv.args) if(argName!="profiles"){
+ bc[argName] = argv.args[argName];
+ }
+
+ // at this point the raw profile object has been fully initialized; clean it up and look for errors...
+ bc.basePath = computePath(bc.basePath, process.cwd());
+ var releaseDir = catPath(bc.releaseDir || "../release", bc.releaseName || "");
+ bc.destBasePath = computePath(releaseDir, bc.basePath);
+
+ // compute global copyright, if any
+ bc.copyright = isNonemptyString(bc.copyright) ? (maybeRead(computePath(bc.copyright, bc.basePath)) || bc.copyright) : "";
+ bc.copyrightLayers = !!bc.copyrightLayers;
+ bc.copyrightNonlayers = !!bc.copyrightNonlayers;
+
+ // compute files, dirs, and trees
+ (function(){
+ for(var property in {files:1, dirs:1, trees:1}){
+ if(bc[property] instanceof Array){
+ bc[property] = bc[property].map(function(item){
+ return cleanupFilenamePair(item, bc.basePath, bc.destBasePath, property);
+ });
+ }
+ }
+ })();
+
+ // cleanup the replacements (if any)
+ (function(){
+ var cleanSet = {}, src, dest;
+ for(src in bc.replacements){
+ cleanSet[computePath(src, bc.basePath)] = bc.replacements[src];
+ }
+ bc.replacements = cleanSet;
+ })();
+
+ // explicit mini and/or copyTests wins; explicit copyTests ignores explicit mini
+ if(!("mini" in bc)){
+ bc.mini = true;
+ }
+ if(!("copyTests" in bc)){
+ bc.copyTests = !bc.mini;
+ }
+ if(isString(bc.copyTests)){
+ bc.copyTests = bc.copyTests.toLowerCase();
+ }
+ if(bc.copyTests!="build"){
+ // convert to pure boolean
+ bc.copyTests = !!bc.copyTests;
+ }
+
+ function getDiscreteLocales(locale){
+ for(var locales = locale.split("-"), result = [], current = "", i = 0; i<locales.length; i++){
+ result.push(current += (i ? "-" : "") + locales[i]);
+ }
+ return result;
+ }
+ if(isString(bc.localeList)){
+ bc.localeList = bc.localeList.split(",");
+ if(bc.localeList.length){
+ bc.localeList = bc.localeList.map(function(locale){ return lang.trim(locale); });
+ }
+ }
+ if(bc.localeList && bc.localeList.length){
+ if(bc.localeList.indexOf("ROOT")==-1){
+ bc.localeList.push("ROOT");
+ }
+ var localeList = {};
+ bc.localeList.forEach(function(locale){
+ locale = lang.trim(locale);
+ localeList[locale] = getDiscreteLocales(locale);
+ });
+ bc.localeList.discreteLocales = localeList;
+ }else{
+ bc.localeList = false;
+ }
+
+
+ (function(){
+ function processPackage(pack){
+ var packName = pack.name,
+ basePath = pack.basePath || bc.basePath;
+ if(!pack.packageJson){
+ pack.packageJson = argv.readPackageJson(catPath(computePath(pack.location || ("./" + packName), basePath), "package.json"), "missingPackageJson");
+ }
+ var packageJson = pack.packageJson;
+ if(packageJson){
+ if(packageJson.version){
+ bc.log("packageVersion", ["package", packName, "version", packageJson.version]);
+
+ // new for 1.7, if version is not provided, the version of the dojo package is used
+ if(typeof bc.version=="undefined" && packName=="dojo"){
+ bc.version = packageJson.version;
+ }
+ }
+ if(packageJson.main && !pack.main){
+ pack.main= packageJson.main;
+ }
+ if(packageJson.directories && packageJson.directories.lib && !pack.location){
+ pack.location = catPath(getFilepath(packageJson.selfFilename), packageJson.directories.lib);
+ }
+ if("dojoBuild" in packageJson){
+ var defaultProfile = argv.readProfile("profile", catPath(getFilepath(packageJson.selfFilename), packageJson.dojoBuild));
+ for(var p in defaultProfile){
+ if(!(p in pack)){
+ pack[p] = defaultProfile[p];
+ }else if(p in {resourceTags:1}){
+ // these are mixed one level deep
+ // TODO: review all profile properties and see if there are any others than resourceTags that ought to go here
+ mix(pack[p], defaultProfile[p]);
+ }
+ }
+ }else{
+ bc.log("missingProfile", ["package", packageJson.name]);
+ }
+ }
+
+ // build up info to tell all about a package; all properties semantically identical to definitions used by dojo loader/bdLoad
+ pack.main = isString(pack.main) ? pack.main : "main";
+ if(pack.main.indexOf("./")==0){
+ pack.main = pack.main.substring(2);
+ }
+ if(pack.destMain && pack.destMain.indexOf("./")==0){
+ pack.destMain = pack.destMain.substring(2);
+ }
+
+ pack.location = computePath(pack.location || ("./" + packName), basePath);
+
+ pack.copyright = isNonemptyString(pack.copyright) ?
+ (maybeRead(computePath(pack.copyright, pack.location)) || maybeRead(computePath(pack.copyright, bc.basePath)) || pack.copyright) :
+ (pack.copyright ? bc.copyright : "");
+ pack.copyrightLayers = isDefined(pack.copyrightLayers) ? !!pack.copyrightLayers : bc.copyrightLayers;
+ pack.copyrightNonlayers = isDefined(pack.copyrightNonlayers) ? !!pack.copyrightNonlayers : bc.copyrightNonlayers;
+
+ // dest says where to output the compiled code stack
+ var destPack = bc.destPackages[packName] = {
+ name:pack.destName || packName,
+ main:pack.destMain || pack.main,
+ location:computePath(pack.destLocation || ("./" + (pack.destName || packName)), bc.destBasePath)
+ };
+ delete pack.destName;
+ delete pack.destMain;
+ delete pack.destLocation;
+
+
+ if(!pack.trees){
+ // copy the package tree; don't copy any hidden directorys (e.g., .git, .svn) or temp files
+ pack.trees = [[pack.location, destPack.location, /(\/\.)|(^\.)|(~$)/]];
+ } // else the user has provided explicit copy instructions
+
+ // filenames, dirs, trees just like global, except relative to the pack.(src|dest)Location
+ for(var property in {files:1, dirs:1, trees:1}){
+ pack[property] = (pack[property] || []).map(function(item){
+ return cleanupFilenamePair(item, pack.location, destPack.location, property + " in package " + packName);
+ });
+ }
+ }
+
+ // so far, we've been using bc.packageMap to accumulate package info as it is provided by packagePaths and/or packages
+ // in zero to many profile scripts. This routine moves each package config into bc.packages which is a map
+ // from package name to package config (this is different from the array the user uses to pass package config info). Along
+ // the way, each package config object is cleaned up and all default values are calculated.
+ bc.packages = bc.packageMap;
+ delete bc.packageMap;
+ bc.destPackages = {};
+ for(var packageName in bc.packages){
+ var pack = bc.packages[packageName];
+ pack.name = pack.name || packageName;
+ processPackage(pack);
+ }
+
+ // now that we know the dojo path, we can automatically add DOH, if required
+ if(bc.copyTests && !bc.packages.doh){
+ bc.packages.doh = {
+ name:"doh",
+ location:compactPath(bc.packages.dojo.location + "/../util/doh"),
+ destLocation:"util/doh"
+ };
+ processPackage(bc.packages.doh);
+ }
+
+ // get this done too...
+ require.computeAliases(bc.aliases, (bc.aliasesMap = []));
+ require.computeMapProg(bc.paths, (bc.pathsMapProg = []));
+
++ bc.mapProgs = [];
++ require.computeMapProg(bc.map, bc.mapProgs);
++ bc.mapProgs.forEach(function(item){
++ item[1] = require.computeMapProg(item[1], []);
++ if(item[0]=="*"){
++ bc.mapProgs.star = item;
++ }
++ });
++
+ // add some methods to bc to help with resolving AMD module info
+ bc.srcModules = {};
+ bc.destModules = {};
+
+ var trimLastChars = function(text, n){
+ return text.substring(0, text.length-n);
+ };
+
+ bc.getSrcModuleInfo = function(mid, referenceModule, ignoreFileType){
- // notice that aliases and paths are applied, but map is not (and never will be)
+ if(ignoreFileType){
- var result = require.getModuleInfo(mid+"/x", referenceModule, bc.packages, bc.srcModules, bc.basePath + "/", [], bc.pathsMapProg, bc.aliasesMap, true);
++ var result = require.getModuleInfo(mid+"/x", referenceModule, bc.packages, bc.srcModules, bc.basePath + "/", bc.mapProgs, bc.pathsMapProg, bc.aliasesMap, true);
+ result.mid = trimLastChars(result.mid, 2);
+ if(result.pid!==0){
+ // trim /x.js
+ result.url = trimLastChars(result.url, 5);
+ }
+ return result;
+ }else{
- return require.getModuleInfo(mid, referenceModule, bc.packages, bc.srcModules, bc.basePath + "/", [], bc.pathsMapProg, bc.aliasesMap, true);
++ return require.getModuleInfo(mid, referenceModule, bc.packages, bc.srcModules, bc.basePath + "/", bc.mapProgs, bc.pathsMapProg, bc.aliasesMap, true);
+ }
+ };
+
+
+ bc.getDestModuleInfo = function(mid, referenceModule, ignoreFileType){
+ // notice no mapping, paths, aliases in the dest getModuleInfo....just send stuff to where it should go without manipulation
+ if(ignoreFileType){
+ var result = require.getModuleInfo(mid+"/x", referenceModule, bc.destPackages, bc.destModules, bc.destBasePath + "/", [], [], [], true);
+ result.mid = trimLastChars(result.mid, 2);
+ if(result.pid!==0){
+ // trim /x.js
+ result.url = trimLastChars(result.url, 5);
+ }
+ return result;
+ }else{
+ return require.getModuleInfo(mid, referenceModule, bc.destPackages, bc.destModules, bc.destBasePath + "/", [], [], [], true);
+ }
+ };
+
+ bc.getAmdModule = function(
+ mid,
+ referenceModule
+ ){
+ var match = mid.match(/^([^\!]+)\!(.*)$/);
+ if(match){
+ var pluginModuleInfo = bc.getSrcModuleInfo(match[1], referenceModule),
+ pluginModule = pluginModuleInfo && bc.amdResources[pluginModuleInfo.mid],
+ pluginId = pluginModule && pluginModule.mid,
+ pluginProc = bc.plugins[pluginId];
+ if(!pluginModule){
+ return 0;
+ }else if(!pluginProc){
+ if(!pluginModule.noBuildResolver){
+ bc.log("missingPluginResolver", ["module", referenceModule.mid, "plugin", pluginId]);
+ }
+ return pluginModule;
+ }else{
+ // flatten the list of modules returned from the plugin
+ var modules = [].concat(pluginProc.start(match[2], referenceModule, bc));
+ return modules.concat.apply([], modules);
+ }
+ }else{
+ var moduleInfo = bc.getSrcModuleInfo(mid, referenceModule),
+ module = moduleInfo && bc.amdResources[moduleInfo.mid];
+ return module;
+ }
+ }
+
+ })();
+
+
+ if(bc.selectorEngine && bc.defaultConfig && bc.defaultConfig.hasCache){
+ bc.defaultConfig.hasCache["config-selectorEngine"] = bc.selectorEngine;
+ }
+
+ (function(){
+ // a layer is a module that should be written with all of its dependencies, as well as all modules given in
+ // the include vector together with their dependencies, excluding modules contained in the exclude vector and their dependencies
+ var layer, fixedLayers = {};
+ for(var mid in bc.layers){
+ layer = bc.layers[mid];
+ layer.exclude = layer.exclude || [];
+ layer.include = layer.include || [];
+ layer.boot = !!layer.boot;
+ layer.discard = !!layer.discard;
+ layer.compat = layer.compat!==undefined ? layer.compat : (bc.layerCompat ||"");
+ layer.noref = !!(layer.noref!==undefined ? layer.noref : (layer.compat=="1.6" ? true : bc.noref));
+
+ var tlm = mid.split("/")[0],
+ pack = bc.packages[tlm],
+ packLocation = pack && pack.location,
+ packCopyright = pack && pack.copyright,
+ packCopyrightLayers = pack && pack.copyrightLayers;
+ if(isNonemptyString(layer.copyright)){
+ // if relative, first try basePath, then try package location, otherwise, just use what's given
+ layer.copyright = (packLocation && maybeRead(computePath(layer.copyright, packLocation))) || maybeRead(computePath(layer.copyright, bc.basePath)) || layer.copyright;
+ }else if(isDefined(layer.copyright)){
+ // some kind of truthy other than a string
+ layer.copyright = layer.copyright ? (packCopyright || bc.copyright) : "";
+ }else{
+ layer.copyright = pack ? (packCopyrightLayers && (packCopyright || bc.copyright)) : (bc.copyrightLayers && bc.copyright);
+ }
+ if(!layer.copyright){
+ layer.copyright = "";
+ }
+ fixedLayers[mid] = layer;
+ }
+ bc.layers = fixedLayers;
+
+ // if (and only if) we're doing a build that includes the dojo tree, then ensure the loader layer is defined correctly
+ // and make sure all other layers exclude the loader unless they are marked with custome base
+ if(bc.packages.dojo){
+ if(!bc.layers["dojo/dojo"]){
+ bc.layers["dojo/dojo"] = {name:"dojo/dojo", copyright:bc.defaultCopyright + bc.defaultBuildNotice, include:["dojo/main"], exclude:[]};
+ }
+ for(var p in bc.layers){
+ layer = bc.layers[p];
+ if(p=="dojo/dojo"){
+ if(!layer.customBase){
+ // the purpose of the layer is to simply add some additional modules to a standard dojo boot
+ if(layer.include.indexOf("dojo/main")==-1){
+ layer.include.push("dojo/main");
+ }
+ }else{
+ // this is a custom base dojo.js; it's up the the user to say exactly what they want
+ }
+ }else{
+ if((layer.boot || !layer.customBase) && layer.exclude.indexOf("dojo/dojo")==-1){
+ // the layer has dojo/dojo if it is booting, or assumes dojo/dojo if its not explicitly saying customBase
+ layer.exclude.push("dojo/dojo");
+ }
+ // by definition...
+ layer.customBase = layer.boot;
+ }
+ }
+ }
+ })();
+
+
+ // for the static has flags, -1 means its not static; this gives a way of combining several static has flag sets
+ // and still allows later sets to delete flags set in earlier sets
+ var deleteStaticHasFlagSet = [];
+ for(var p in bc.staticHasFeatures) if(bc.staticHasFeatures[p]==-1) deleteStaticHasFlagSet.push(p);
+ deleteStaticHasFlagSet.forEach(function(flag){delete bc.staticHasFeatures[flag];});
+
+ if(bc.action){
+ bc.action.split(/\W|\s/).forEach(function(action){
+ action = action.match(/\s*(\S+)\s*/)[1];
+ switch(action){
+ case "check":
+ bc.check = true;
+ break;
+ case "clean":
+ bc.clean = true;
+ break;
+ case "release":
+ bc.release = true;
+ break;
+ default:
+ bc.log("inputUnknownAction", ["action", action]);
+ }
+ });
+ }
+
+ if(bc.clean){
+ bc.log("cleanRemoved");
+ }
+
+ // understand stripConsole from dojo 1.3 and before
+ var stripConsole = bc.stripConsole;
+ if(!stripConsole || stripConsole=="none"){
+ stripConsole = false;
+ }else if(stripConsole == "normal,warn"){
+ bc.log("inputDeprecatedStripConsole", ["deprecated", "normal,warn", "use", "warn"]);
+ stripConsole = "warn";
+ }else if(stripConsole == "normal,error"){
+ bc.log("inputDeprecatedStripConsole", ["deprecated", "normal,error", "use", "all"]);
+ stripConsole = "all";
+ }else if(!/normal|warn|all|none/.test(stripConsole)){
+ bc.log("inputUnknownStripConsole", ["value", stripConsole]);
+ }
+ bc.stripConsole = stripConsole;
+
+ function fixupOptimize(value){
+ if(value){
+ value = value + "";
+ value = value.toLowerCase();
+ if(!/^(((comments|shrinksafe)(\.keeplines)?)|(closure(\.keeplines)?|uglify(\.keeplines)?))$/.test(value)){
+ bc.log("inputUnknownOptimize", ["value", value]);
+ value = 0;
+ }else{
+ if(/shrinksafe/.test(value) && stripConsole){
+ value+= "." + stripConsole;
+ }
+ }
+ }
+ return value;
+ }
+ bc.optimize = fixupOptimize(bc.optimize);
+ bc.layerOptimize = fixupOptimize(bc.layerOptimize);
+
+ (function(){
+ var fixedScopeMap = {dojo:"dojo", dijit:"dijit", dojox:"dojox"};
+ (bc.scopeMap || []).forEach(function(pair){
+ if(!pair[1]){
+ delete fixedScopeMap[pair[0]];
+ }else{
+ fixedScopeMap[pair[0]] = pair[1];
+ }
+ });
+ bc.scopeMap = fixedScopeMap;
+
+ bc.scopeNames = [];
+ for(var p in fixedScopeMap){
+ bc.scopeNames.push(p);
+ }
+ })();
+
+ bc.internSkip = function(){return false;};
+ if(bc.internSkipList){
+ bc.internSkip = function(mid, referenceModule){
+ return bc.internSkipList.some(function(item){
+ var result = false;
+ if(item instanceof RegExp){
+ result = item.test(mid);
+ }else if(item instanceof Function){
+ result = item(mid, referenceModule);
+ }else{
+ result = item==mid;
+ }
+ if(result){
+ bc.log("internStrings", ["module", referenceModule.mid, "skipping", mid]);
+ }
+ return result;
+ });
+ };
+ }
+
+ // dump bc (if requested) before changing gate names to gate ids below
+ if(bc.check){
+ (function(){
+ var toDump = {
+ aliases:1,
+ basePath:1,
+ buildReportDir:1,
+ buildReportFilename:1,
+ closureCompilerPath:1,
+ copyright:1,
+ copyrightLayers:1,
+ copyrightNonlayers:1,
+ copyTests:1,
+ destBasePath:1,
+ destModules:1,
+ destPackages:1,
+ destPathTransforms:1,
+ dirs:1,
+ discoveryProcs:1,
+ files:1,
+ insertAbsMids:1,
+ internStringsSkipList:1,
+ layers:1,
+ localeList:1,
+ includeLocales: 1,
+ maxOptimizationProcesses:1,
+ mini:1,
+ optimize:1,
+ layerOptimize:1,
+ "package":1,
+ packages:1,
+ paths:1,
+ pathsMapProg:1,
+ plugins:1,
+ replacements:1,
+ startTimestamp:1,
+ staticHasFeatures:1,
+ stripConsole:1,
+ trees:1,
+ useSourceMaps:1
+ };
+ for(var p in toDump){
+ toDump[p] = bc[p];
+ }
+ bc.log("pacify", stringify(toDump));
+ })();
+ bc.release = 0;
+ }
+
+ if(bc.writeProfile){
+ // TODO
+ // fs.writeFileSync(bc.writeProfile, "dependencies = " + dojo.toJson(profileProperties, true), "utf8");
+ }
+
+ if(bc.debugCheck){
+ (function(){
+ var toDump = {};
+ for(var p in bc){
+ if(bc[p]!==messages[p] && typeof bc[p]!="function"){
+ toDump[p] = bc[p];
+ }
+ }
+ console.log("profile:");
+ console.log(stringify(toDump));
+ toDump = {};
+ for(p in require){
+ if(p!="modules" && p!="module" && p!="rawConfig" && typeof require[p]!="function"){
+ toDump[p] = require[p];
+ }
+ }
+ console.log("require config:");
+ console.log(stringify(toDump));
+ })();
+ bc.release = 0;
+ }
+
+ // clean up the gates and transforms
+ (function(){
+ // check that each transform references a valid gate
+ for(var gates = {}, i = 0; i<bc.gates.length; i++){
+ gates[bc.gates[i][1]] = i;
+ }
+ var
+ transforms = bc.transforms,
+ gateId;
+ for(var transformId in transforms){
+ // each item is a [AMD-MID, gateName] pair
+ gateId = gates[transforms[transformId][1]];
+ if(typeof gateId == "undefined"){
+ bc.log("inputUnknownGate", ["transform", transformId, "gate", transforms[transformId][1]]);
+ }else{
+ transforms[transformId][1] = gateId;
+ }
+ }
+ })();
+
+ // clean up the transformJobs
+ (function(){
+ // check that that each transformId referenced in transformJobs references an existing item in transforms
+ // ensure proper gate order of the transforms given in transformJobs; do not disturb order within a given
+ // gate--this is the purview of the user
+ var transforms = bc.transforms;
+ bc.transformJobs.forEach(function(item){
+ // item is a [predicate, vector of transformId] pairs
+ var error = false;
+ var tlist = item[1].map(function(id){
+ // item is a transformId
+ if(transforms[id]){
+ // return a [trandformId, gateId] pair
+ return [id, transforms[id][1]];
+ }else{
+ error = true;
+ bc.log("inputUnknownTransform", ["transform", id]);
+ return 0;
+ }
+ });
+ // tlist is a vector of [transformId, gateId] pairs than need to be checked for order
+ if(!error){
+ for(var i = 0, end = tlist.length - 1; i<end;){
+ if(tlist[i][1]>tlist[i+1][1]){
+ var t = tlist[i];
+ tlist[i] = tlist[i+1];
+ tlist[i+1] = t;
+ i && i--;
+ }else{
+ i++;
+ }
+ }
+ // now replace the vector of transformIds with the sorted list
+ item[1] = tlist;
+ }
+ });
+ })();
+
+ if(argv.args.unitTest=="dumpbc"){
+ console.log(stringify(bc) + "\n");
+ }
+
+ if(bc.quiet){
+ (function(){
+ var delSet = {};
+ for(var p in bc.pacifySet){
+ if(bc.messageMap[p][1]>199){
+ delSet[p] = 1;
+ }
+ }
+ for(p in delSet){
+ delete bc.pacifySet[p];
+ }
+ })();
+ }
+
+ if(bc.unitTestComputedProfile){
+ bc.unitTestComputedProfile();
+ // stop the build
+ bc.release = 0;
+ }
+
+ if(!bc.unitTestComputedProfile && !bc.check && !bc.debugCheck && !bc.clean && !bc.release){
+ bc.log("pacify", "Nothing to do; you must explicitly instruct the application to do something; use the option --help for help.");
+ }
+
+ return bc;
+});
diff --cc util/build/version.js
index c374154,0000000..d7e3ab3
mode 100644,000000..100644
--- a/util/build/version.js
+++ b/util/build/version.js
@@@ -1,13 -1,0 +1,13 @@@
+define([], function(){
+ var
- rev = "$Rev: 8416ffe $".match(/[0-9a-f]{7,}/),
++ rev = "$Rev: 00884aa $".match(/[0-9a-f]{7,}/),
+ version= {
- major: 1, minor: 10, patch: 1, flag: "",
++ major: 1, minor: 10, patch: 2, flag: "",
+ revision: rev ? rev[0] : NaN,
+ toString: function(){
+ var v= version;
+ return v.major + "." + v.minor + "." + v.patch + v.flag + " (" + v.revision + ")";
+ }
+ };
+ return version;
+});
diff --cc util/doh/_nodeRunner.js
index 0a7808b,0000000..c372b15
mode 100644,000000..100644
--- a/util/doh/_nodeRunner.js
+++ b/util/doh/_nodeRunner.js
@@@ -1,40 -1,0 +1,40 @@@
+define(["doh/runner", "require", "dojo/_base/config"], function(doh, require, config){
+ /*=====
+ return {
+ // summary:
+ // Module for running DOH tests in node (as opposed to a browser).
+ // Augments return value from doh/runner.
+ };
+ =====*/
+
+ doh.debug= console.log;
+ doh.error= console.log;
+
+ // Override the doh._report method to make it quit with an
+ // appropriate exit code in case of test failures.
+ var oldReport = doh._report;
+ doh._report = function(){
+ oldReport.apply(doh, arguments);
+ if(this._failureCount > 0 || this._errorCount > 0){
+ process.exit(1);
+ }
+ };
+
+ console.log("\n"+doh._line);
- console.log("The Dojo Unit Test Harness, $Rev: 8416ffe $");
++ console.log("The Dojo Unit Test Harness, $Rev: 00884aa $");
+ console.log("Copyright (c) 2011, The Dojo Foundation, All Rights Reserved");
+ console.log("Running with node.js");
+ for (var tests= [], args= config["commandLineArgs"], i= 0, arg; i<args.length; i++) {
+ arg= args[i];
+ if (arg.length==2 && arg[0]=="test") {
+ var test= arg[1];
+ console.log("loading test " + test);
+ tests.push(test);
+ }
+ }
+ console.log(doh._line, "\n");
+
+ require(tests, function() {
+ doh.run();
+ });
+});
diff --cc util/doh/_rhinoRunner.js
index 9bdb859,0000000..b729e82
mode 100644,000000..100644
--- a/util/doh/_rhinoRunner.js
+++ b/util/doh/_rhinoRunner.js
@@@ -1,39 -1,0 +1,39 @@@
+define(["doh/runner", "require", "dojo/_base/config"], function(doh, require, config){
+ /*=====
+ return {
+ // summary:
+ // Module for running DOH tests in rhino (as opposed to a browser).
+ // Augments return value from doh/runner.
+ };
+ =====*/
+
+ doh.debug= print;
+ doh.error= print;
+
+ // Override the doh._report method to make it quit with an
+ // appropriate exit code in case of test failures.
+ var oldReport = doh._report;
+ doh._report = function(){
+ oldReport.apply(doh, arguments);
+ if(this._failureCount > 0 || this._errorCount > 0){
+ quit(1);
+ }
+ };
+
+ print("\n"+doh._line);
- print("The Dojo Unit Test Harness, $Rev: 8416ffe $");
++ print("The Dojo Unit Test Harness, $Rev: 00884aa $");
+ print("Copyright (c) 2011, The Dojo Foundation, All Rights Reserved");
+ for (var tests= [], args= config["commandLineArgs"], i= 0, arg; i<args.length; i++) {
+ arg= (args[i]+"").split("=");
+ if (arg.length==2 && arg[0]=="test") {
+ var test= arg[1];
+ print("loading test " + test);
+ tests.push(test);
+ }
+ }
+ print(doh._line, "\n");
+
+ require(tests, function() {
+ doh.run();
+ });
+});
diff --cc util/doh/mobileRunner.html
index 1970596,0000000..349a139
mode 100644,000000..100644
--- a/util/doh/mobileRunner.html
+++ b/util/doh/mobileRunner.html
@@@ -1,88 -1,0 +1,88 @@@
+<!DOCTYPE html>
+<html style="height:100%;">
+ <head>
- <title>The Dojo Unit Test Harness, $Rev: 8416ffe $</title>
++ <title>The Dojo Unit Test Harness, $Rev: 00884aa $</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1"/>
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
+ <script type="text/javascript">
+ // workaround for bug in Safari 3. See #7189
+ if (/3[\.0-9]+ Safari/.test(navigator.appVersion))
+ {
+ window.console = {
+ origConsole: window.console,
+ log: function(s){
+ this.origConsole.log(s);
+ },
+ info: function(s){
+ this.origConsole.info(s);
+ },
+ error: function(s){
+ this.origConsole.error(s);
+ },
+ warn: function(s){
+ this.origConsole.warn(s);
+ }
+ };
+ }
+ </script>
+
+ <script type="text/javascript" src="_parseURLargs.js"></script>
+
+ <style type="text/css">
+ @import "../../dojo/resources/dojo.css";
+
+ #testLayout {
+ position: relative;
+ left: 0px;
+ top: 0px;
+ width: 100%;
+ height: 100%;
+ border: 1px solid black;
+ border: 0px;
+ }
+
+ .tabBody {
+ margin: 0px;
+ padding: 0px;
+ /*
+ border: 1px solid black;
+ */
+ background-color: #DEDEDE;
+ border: 0px;
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ overflow: auto;
+ }
+
+ #logBody {
+ padding-left: 5px;
+ padding-top: 5px;
+ font-family: Monaco, monospace;
+ font-size: 11px;
+ white-space: pre;
+ }
+
+
+ </style>
+ </head>
+ <body style="height: 100%;">
+ <div style="position: relative; width: 100%; height: 100%; top: 0px; left: 0px;">
+ <div class="tabBody"
+ style="z-index: 1;">
+ <pre id="logBody"></pre>
+ <div id="perfTestsBody" style="background-color: white;"></div>
+ </div>
+ <iframe id="testBody" class="tabBody"
+ style="z-index: -1;"></iframe>
+ <!--
+ src="http://redesign.dojotoolkit.org"></iframe>
+ -->
+ </div>
+ <span id="hiddenAudio"></span>
+ </body>
+</html>
+
diff --cc util/doh/package.json
index 2b7573d,0000000..1d6eae4
mode 100644,000000..100644
--- a/util/doh/package.json
+++ b/util/doh/package.json
@@@ -1,23 -1,0 +1,23 @@@
+{
+ "name": "doh",
- "version":"1.10.1",
++ "version":"1.10.2",
+ "directories": {
+ "lib": "."
+ },
+ "main": "main",
+ "description": "DOH is a unit test framework developed by the Dojo Toolkit Community.",
+ "licenses": [
+ {
+ "type": "AFLv2.1",
+ "url": "http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L43"
+ },
+ {
+ "type": "BSD",
+ "url": "http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L13"
+ }
+ ],
+ "bugs": "http://bugs.dojotoolkit.org/",
+ "keywords": ["JavaScript", "Dojo", "Toolkit", "DOH"],
+ "homepage": "http://dojotoolkit.org/",
+ "dojoBuild": "doh.profile.js"
+}
diff --cc util/doh/runner.html
index 25d5782,0000000..2fd95eb
mode 100644,000000..100644
--- a/util/doh/runner.html
+++ b/util/doh/runner.html
@@@ -1,251 -1,0 +1,251 @@@
+<!DOCTYPE html>
+<html style="height:100%;">
+ <head>
- <title>The Dojo Unit Test Harness, $Rev: 8416ffe $</title>
++ <title>The Dojo Unit Test Harness, $Rev: 00884aa $</title>
+
+ <script type="text/javascript" src="_parseURLargs.js"></script>
+
+ <style type="text/css">
+ @import "../../dojo/resources/dojo.css";
+ /*
+ body {
+ margin: 0px;
+ padding: 0px;
+ font-size: 13px;
+ color: #292929;
+ font-family: Myriad, Lucida Grande, Bitstream Vera Sans, Arial, Helvetica, sans-serif;
+ *font-size: small;
+ *font: x-small;
+ }
+
+ th, td {
+ font-size: 13px;
+ color: #292929;
+ font-family: Myriad, Lucida Grande, Bitstream Vera Sans, Arial, Helvetica, sans-serif;
+ font-weight: normal;
+ }
+
+ * body {
+ line-height: 1.25em;
+ }
+
+ table {
+ border-collapse: collapse;
+ }
+ */
+
+ #testLayout {
+ position: relative;
+ left: 0px;
+ top: 0px;
+ width: 100%;
+ height: 100%;
+ border: 1px solid black;
+ border: 0px;
+ }
+
+ .tabBody {
+ margin: 0px;
+ padding: 0px;
+ /*
+ border: 1px solid black;
+ */
+ background-color: #DEDEDE;
+ border: 0px;
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ overflow: auto;
+ }
+
+ #logBody {
+ padding-left: 5px;
+ padding-top: 5px;
+ font-family: Monaco, monospace;
+ font-size: 11px;
+ white-space: pre;
+ }
+
+ #progressOuter {
+ background-color: #e9e9e9;
+ background-image: -webkit-linear-gradient(rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%);
+ background-image: -moz-linear-gradient(rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%);
+ background-image: -ms-linear-gradient(rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%);
+ background-image: -o-linear-gradient(rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%);
+
+ height: 1em;
+ /*the following trick is necessary to prevent IE from wrapping the last piece of progress bar into a new line*/
+ _margin:1px;
+ _padding: -1px;
+ }
+
+ #progressOuter .success, #progressOuter .failure{
+ float: left;
+ height: 1em;
+ }
+
+ #play, #pause {
+ font-family: Arial;
+ font-size: 1.4em;
+ border: 1px solid #DEDEDE;
+ cursor: pointer;
+ padding-right: 0.5em;
+ }
+
+ .header {
+ border: 1px solid #DEDEDE;
+ }
+
+ button.tab {
+ border-width: 1px 1px 0px 1px;
+ border-style: solid;
+ border-color: #DEDEDE;
+ margin-right: 5px;
+ }
+
+ #testListContainer {
+ /*
+ border: 1px solid black;
+ */
+ position: relative;
+ height: 99%;
+ width: 100%;
+ overflow: auto;
+ }
+
+ #testList {
+ border-collapse: collapse;
+ position: absolute;
+ left: 0px;
+ width: 100%;
+ }
+
+ #testList td {
+ border-bottom: 1px solid #DEDEDE;
+ border-right : 1px solid #DEDEDE;
+ padding: 3px;
+ }
+
+ #testListHeader th {
+ border-bottom: 1px solid #DEDEDE;
+ border-right : 1px solid #DEDEDE;
+ padding: 3px;
+ font-weight: bolder;
+ font-style: italic;
+ }
+
+ #testList tfoot {
+ font-weight: bold;
+ }
+
+ #toggleButtons {
+ float: left;
+ background-color: #DEDEDE;
+ }
+
+ div.testGroupName {
+ position: absolute;
+ }
+
+ .inProgress {
+ background-color: #85afde;
+ }
+
+ .success {
+ background-color: #7cdea7;
+ }
+
+ .failure {
+ background-color: #de827b;
+ }
+ </style>
+ </head>
+ <body style="height: 100%;">
+ <table id="testLayout" cellpadding="0" cellspacing="0" style="margin: 0;">
+ <tr valign="top" height="40">
+ <td colspan="2" id="logoBar">
+ <h3 style="margin: 5px 5px 0px 5px; float: left;">D.O.H.: The Dojo Objective Harness</h3>
+ <img src="small_logo.png" height="40" style="margin: 0px 5px 0px 5px; float: right;">
+ <span style="margin: 10px 5px 0px 5px; float: right;">
+ <input type="checkbox" id="audio" name="audio">
+ <label for="audio">sounds?</label>
+ </span>
+ </td>
+ </tr>
+ <tr valign="top" height="10">
+ <td colspan="2"><div id="progressOuter" onclick="doh._jumpToSuite(arguments[0]);"></div></td>
+ </tr>
+ <tr valign="top" height="30">
+ <td width="30%" class="header">
+ <span id="toggleButtons" onclick="doh.togglePaused();">
+ <button id="play">►</button>
+ <button id="pause" style="display: none;">║</button>
+ </span>
+ <span id="runningStatus">
+ <span id="pausedMsg">Stopped</span>
+ <span id="playingMsg" style="display: none;">Tests Running</span>
+ </span>
+ </td>
+ <td width="*" class="header" valign="bottom">
+ <button class="tab" onclick="doh.showTestPage();">Test Page</button>
+ <button class="tab" onclick="doh.showLogPage();">Log</button>
+ <button class="tab" onclick="doh.showPerfTestsPage();">Performance Tests Results</button>
+ </td>
+ </tr>
+ <tr valign="top" style="border: 0; padding: 0; margin: 0;">
+ <td style="border: 0; padding: 0; margin: 0; height:100%;">
+ <div id="testListContainer">
+ <table cellpadding="0" cellspacing="0" border="0"
+ width="100%" id="testList" style="margin: 0;" onclick="doh._jumpToLog(arguments[0]);">
+ <thead>
+ <tr id="testListHeader" style="border: 0; padding: 0; margin: 0;" >
+ <th> </th>
+ <th width="20">
+ <input type="checkbox" checked
+ onclick="doh.toggleRunAll();">
+ </th>
+ <th width="*" style="text-align: left;">test</th>
+ <th width="50">time</th>
+ </tr>
+ </thead>
+ <tbody valign="top">
+ <tr id="groupTemplate" style="display: none;">
+ <td style="font-family: Arial; width: 15px;">►</td>
+ <td>
+ <input type="checkbox" checked>
+ </td>
+ <td>group name</td>
+ <td>10ms</td>
+ </tr>
+ <tr id="testTemplate" style="display: none;">
+ <td> </td>
+ <td> </td>
+ <td style="padding-left: 20px;">test name</td>
+ <td>10ms</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </td>
+ <td style="height: 100%;">
+ <div style="position: relative; width: 100%; height: 100%; top: 0px; left: 0px;">
+ <div class="tabBody"
+ style="z-index: 1;">
+ <pre id="logBody"></pre>
+ <div id="perfTestsBody" style="background-color: white;"></div>
+ </div>
+ <iframe id="testBody" class="tabBody"
+ style="z-index: -1;"></iframe>
+ <!--
+ src="http://redesign.dojotoolkit.org"></iframe>
+ -->
+ </div>
+ </td>
+ </tr>
+ </table>
+ <span id="hiddenAudio"></span>
+ </body>
+</html>
+
diff --cc util/package.json
index 219c210,0000000..a81bfed
mode 100644,000000..100644
--- a/util/package.json
+++ b/util/package.json
@@@ -1,20 -1,0 +1,20 @@@
+{
+ "name": "dojo-util",
- "version":"1.10.1",
++ "version":"1.10.2",
+ "licenses": [
+ {
+ "type": "AFLv2.1",
+ "url": "http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L43"
+ },
+ {
+ "type": "BSD",
+ "url": "http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L13"
+ }
+ ],
+ "description": "Dojo utilities including build system for optimizing JavaScript application performance, and DOH testing tool",
+ "bugs": {
+ "url": "http://bugs.dojotoolkit.org/"
+ },
+ "keywords": ["JavaScript", "Dojo", "Toolkit", "util"],
+ "homepage": "http://dojotoolkit.org/"
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/dojo.git
More information about the Pkg-javascript-commits
mailing list