require({cache:{ 'dojox/main':function(){ define(["dojo/_base/kernel"], function(dojo) { // module: // dojox/main /*===== return { // summary: // The dojox package main module; dojox package is somewhat unusual in that the main module currently just provides an empty object. // Apps should require modules from the dojox packages directly, rather than loading this module. }; =====*/ return dojo.dojox; }); }, 'dojox/mobile/_base':function(){ define([ "./common", "./View", "./Heading", "./RoundRect", "./RoundRectCategory", "./EdgeToEdgeCategory", "./RoundRectList", "./EdgeToEdgeList", "./ListItem", "./Container", "./Pane", "./Switch", "./ToolBarButton", "./ProgressIndicator" ], function(common, View, Heading, RoundRect, RoundRectCategory, EdgeToEdgeCategory, RoundRectList, EdgeToEdgeList, ListItem, Switch, ToolBarButton, ProgressIndicator){ // module: // dojox/mobile/_base /*===== return { // summary: // Includes the basic dojox/mobile modules: common, View, Heading, // RoundRect, RoundRectCategory, EdgeToEdgeCategory, RoundRectList, // EdgeToEdgeList, ListItem, Container, Pane, Switch, ToolBarButton, // and ProgressIndicator. }; =====*/ return common; }); }, 'dojox/mobile/common':function(){ define([ "dojo/_base/array", "dojo/_base/config", "dojo/_base/connect", "dojo/_base/lang", "dojo/_base/window", "dojo/_base/kernel", "dojo/dom", "dojo/dom-class", "dojo/dom-construct", "dojo/domReady", "dojo/ready", "dojo/touch", "dijit/registry", "./sniff", "./uacss" // (no direct references) ], function(array, config, connect, lang, win, kernel, dom, domClass, domConstruct, domReady, ready, touch, registry, has){ // module: // dojox/mobile/common var dm = lang.getObject("dojox.mobile", true); // tell dojo/touch to generate synthetic clicks immediately // and regardless of preventDefault() calls on touch events win.doc.dojoClick = true; /// ... but let user disable this by removing dojoClick from the document if(has("touch")){ // Do we need to send synthetic clicks when preventDefault() is called on touch events? // This is normally true on anything except Android 4.1+ and IE10+, but users reported // exceptions like Galaxy Note 2. So let's use a has("clicks-prevented") flag, and let // applications override it through data-dojo-config="has:{'clicks-prevented':true}" if needed. has.add("clicks-prevented", !(has("android") >= 4.1 || (has("ie") === 10) || (!has("ie") && has("trident") > 6))); if(has("clicks-prevented")){ dm._sendClick = function(target, e){ // dojo/touch will send a click if dojoClick is set, so don't do it again. for(var node = target; node; node = node.parentNode){ if(node.dojoClick){ return; } } var ev = win.doc.createEvent("MouseEvents"); ev.initMouseEvent("click", true, true, win.global, 1, e.screenX, e.screenY, e.clientX, e.clientY); target.dispatchEvent(ev); }; } } dm.getScreenSize = function(){ // summary: // Returns the dimensions of the browser window. return { h: win.global.innerHeight || win.doc.documentElement.clientHeight, w: win.global.innerWidth || win.doc.documentElement.clientWidth }; }; dm.updateOrient = function(){ // summary: // Updates the orientation specific CSS classes, 'dj_portrait' and // 'dj_landscape'. var dim = dm.getScreenSize(); domClass.replace(win.doc.documentElement, dim.h > dim.w ? "dj_portrait" : "dj_landscape", dim.h > dim.w ? "dj_landscape" : "dj_portrait"); }; dm.updateOrient(); dm.tabletSize = 500; dm.detectScreenSize = function(/*Boolean?*/force){ // summary: // Detects the screen size and determines if the screen is like // phone or like tablet. If the result is changed, // it sets either of the following css class to ``: // // - 'dj_phone' // - 'dj_tablet' // // and it publishes either of the following events: // // - '/dojox/mobile/screenSize/phone' // - '/dojox/mobile/screenSize/tablet' var dim = dm.getScreenSize(); var sz = Math.min(dim.w, dim.h); var from, to; if(sz >= dm.tabletSize && (force || (!this._sz || this._sz < dm.tabletSize))){ from = "phone"; to = "tablet"; }else if(sz < dm.tabletSize && (force || (!this._sz || this._sz >= dm.tabletSize))){ from = "tablet"; to = "phone"; } if(to){ domClass.replace(win.doc.documentElement, "dj_"+to, "dj_"+from); connect.publish("/dojox/mobile/screenSize/"+to, [dim]); } this._sz = sz; }; dm.detectScreenSize(); // dojox/mobile.hideAddressBarWait: Number // The time in milliseconds to wait before the fail-safe hiding address // bar runs. The value must be larger than 800. dm.hideAddressBarWait = typeof(config.mblHideAddressBarWait) === "number" ? config.mblHideAddressBarWait : 1500; dm.hide_1 = function(){ // summary: // Internal function to hide the address bar. // tags: // private scrollTo(0, 1); dm._hidingTimer = (dm._hidingTimer == 0) ? 200 : dm._hidingTimer * 2; setTimeout(function(){ // wait for a while for "scrollTo" to finish if(dm.isAddressBarHidden() || dm._hidingTimer > dm.hideAddressBarWait){ // Succeeded to hide address bar, or failed but timed out dm.resizeAll(); dm._hiding = false; }else{ // Failed to hide address bar, so retry after a while setTimeout(dm.hide_1, dm._hidingTimer); } }, 50); //50ms is an experiential value }; dm.hideAddressBar = function(/*Event?*/evt){ // summary: // Hides the address bar. // description: // Tries to hide the address bar a couple of times. The purpose is to do // it as quick as possible while ensuring the resize is done after the hiding // finishes. if(dm.disableHideAddressBar || dm._hiding){ return; } dm._hiding = true; dm._hidingTimer = has("ios") ? 200 : 0; // Need to wait longer in case of iPhone var minH = screen.availHeight; if(has('android')){ minH = outerHeight / devicePixelRatio; // On some Android devices such as Galaxy SII, minH might be 0 at this time. // In that case, retry again after a while. (200ms is an experiential value) if(minH == 0){ dm._hiding = false; setTimeout(function(){ dm.hideAddressBar(); }, 200); } // On some Android devices such as HTC EVO, "outerHeight/devicePixelRatio" // is too short to hide address bar, so make it high enough if(minH <= innerHeight){ minH = outerHeight; } // On Android 2.2/2.3, hiding address bar fails when "overflow:hidden" style is // applied to html/body element, so force "overflow:visible" style if(has('android') < 3){ win.doc.documentElement.style.overflow = win.body().style.overflow = "visible"; } } if(win.body().offsetHeight < minH){ // to ensure enough height for scrollTo to work win.body().style.minHeight = minH + "px"; dm._resetMinHeight = true; } setTimeout(dm.hide_1, dm._hidingTimer); }; dm.isAddressBarHidden = function(){ return pageYOffset === 1; }; dm.resizeAll = function(/*Event?*/evt, /*Widget?*/root){ // summary: // Calls the resize() method of all the top level resizable widgets. // description: // Finds all widgets that do not have a parent or the parent does not // have the resize() method, and calls resize() for them. // If a widget has a parent that has resize(), calling widget's // resize() is its parent's responsibility. // evt: // Native event object // root: // If specified, searches the specified widget recursively for top-level // resizable widgets. // root.resize() is always called regardless of whether root is a // top level widget or not. // If omitted, searches the entire page. if(dm.disableResizeAll){ return; } connect.publish("/dojox/mobile/resizeAll", [evt, root]); // back compat connect.publish("/dojox/mobile/beforeResizeAll", [evt, root]); if(dm._resetMinHeight){ win.body().style.minHeight = dm.getScreenSize().h + "px"; } dm.updateOrient(); dm.detectScreenSize(); var isTopLevel = function(w){ var parent = w.getParent && w.getParent(); return !!((!parent || !parent.resize) && w.resize); }; var resizeRecursively = function(w){ array.forEach(w.getChildren(), function(child){ if(isTopLevel(child)){ child.resize(); } resizeRecursively(child); }); }; if(root){ if(root.resize){ root.resize(); } resizeRecursively(root); }else{ array.forEach(array.filter(registry.toArray(), isTopLevel), function(w){ w.resize(); }); } connect.publish("/dojox/mobile/afterResizeAll", [evt, root]); }; dm.openWindow = function(url, target){ // summary: // Opens a new browser window with the given URL. win.global.open(url, target || "_blank"); }; dm._detectWindowsTheme = function(){ // summary: // Detects if the "windows" theme is used, // if it is used, set has("windows-theme") and // add the .windows_theme class on the document. // Avoid unwanted (un)zoom on some WP8 devices (at least Nokia Lumia 920) if(navigator.userAgent.match(/IEMobile\/10\.0/)){ domConstruct.create("style", {innerHTML: "@-ms-viewport {width: auto !important}"}, win.doc.head); } var setWindowsTheme = function(){ domClass.add(win.doc.documentElement, "windows_theme"); kernel.experimental("Dojo Mobile Windows theme", "Behavior and appearance of the Windows theme are experimental."); }; // First see if the "windows-theme" feature has already been set explicitly // in that case skip aut-detect var windows = has("windows-theme"); if(windows !== undefined){ if(windows){ setWindowsTheme(); } return; } // check css var i, j; var check = function(href){ // TODO: find a better regexp to match? if(href && href.indexOf("/windows/") !== -1){ has.add("windows-theme", true); setWindowsTheme(); return true; } return false; }; // collect @import var s = win.doc.styleSheets; for(i = 0; i < s.length; i++){ if(s[i].href){ continue; } var r = s[i].cssRules || s[i].imports; if(!r){ continue; } for(j = 0; j < r.length; j++){ if(check(r[j].href)){ return; } } } // collect var elems = win.doc.getElementsByTagName("link"); for(i = 0; i < elems.length; i++){ if(check(elems[i].href)){ return; } } }; if(config.mblApplyPageStyles !== false){ domClass.add(win.doc.documentElement, "mobile"); } if(has('chrome')){ // dojox/mobile does not load uacss (only _compat does), but we need dj_chrome. domClass.add(win.doc.documentElement, "dj_chrome"); } if(win.global._no_dojo_dm){ // deviceTheme seems to be loaded from a script tag (= non-dojo usage) var _dm = win.global._no_dojo_dm; for(var i in _dm){ dm[i] = _dm[i]; } dm.deviceTheme.setDm(dm); } // flag for Android transition animation flicker workaround has.add('mblAndroidWorkaround', config.mblAndroidWorkaround !== false && has('android') < 3, undefined, true); has.add('mblAndroid3Workaround', config.mblAndroid3Workaround !== false && has('android') >= 3, undefined, true); dm._detectWindowsTheme(); dm.setSelectable = function(/*Node*/node, /*Boolean*/selectable){ var nodes, i; node = dom.byId(node); if (has("ie") <= 9){ // (IE < 10) Fall back to setting/removing the // unselectable attribute on the element and all its children // except the input element (see https://bugs.dojotoolkit.org/ticket/13846) nodes = node.getElementsByTagName("*"); i = nodes.length; if(selectable){ node.removeAttribute("unselectable"); while(i--){ nodes[i].removeAttribute("unselectable"); } }else{ node.setAttribute("unselectable", "on"); while(i--){ if (nodes[i].tagName !== "INPUT"){ nodes[i].setAttribute("unselectable", "on"); } } } }else{ domClass.toggle(node, "unselectable", !selectable); } }; var touchActionProp = has("pointer-events") ? "touchAction" : has("MSPointer") ? "msTouchAction" : null; dm._setTouchAction = touchActionProp ? function(/*Node*/node, /*Boolean*/value){ node.style[touchActionProp] = value; } : function(){}; // Set the background style using dojo/domReady, not dojo/ready, to ensure it is already // set at widget initialization time. (#17418) domReady(function(){ if(config.mblApplyPageStyles !== false){ domClass.add(win.body(), "mblBackground"); } }); ready(function(){ dm.detectScreenSize(true); if(config.mblAndroidWorkaroundButtonStyle !== false && has('android')){ // workaround for the form button disappearing issue on Android 2.2-4.0 domConstruct.create("style", {innerHTML:"BUTTON,INPUT[type='button'],INPUT[type='submit'],INPUT[type='reset'],INPUT[type='file']::-webkit-file-upload-button{-webkit-appearance:none;} audio::-webkit-media-controls-play-button,video::-webkit-media-controls-play-button{-webkit-appearance:media-play-button;} video::-webkit-media-controls-fullscreen-button{-webkit-appearance:media-fullscreen-button;}"}, win.doc.head, "first"); } if(has('mblAndroidWorkaround')){ // add a css class to show view offscreen for android flicker workaround domConstruct.create("style", {innerHTML:".mblView.mblAndroidWorkaround{position:absolute;top:-9999px !important;left:-9999px !important;}"}, win.doc.head, "last"); } var f = dm.resizeAll; // Address bar hiding var isHidingPossible = navigator.appVersion.indexOf("Mobile") != -1 && // only mobile browsers // #17455: hiding Safari's address bar works in iOS < 7 but this is // no longer possible since iOS 7. Hence, exclude iOS 7 and later: !(has("ios") >= 7); // You can disable the hiding of the address bar with the following dojoConfig: // var dojoConfig = { mblHideAddressBar: false }; // If unspecified, the flag defaults to true. if((config.mblHideAddressBar !== false && isHidingPossible) || config.mblForceHideAddressBar === true){ dm.hideAddressBar(); if(config.mblAlwaysHideAddressBar === true){ f = dm.hideAddressBar; } } var ios6 = has("ios") >= 6; // Full-screen support for iOS6 or later if((has('android') || ios6) && win.global.onorientationchange !== undefined){ var _f = f; var curSize, curClientWidth, curClientHeight; if(ios6){ curClientWidth = win.doc.documentElement.clientWidth; curClientHeight = win.doc.documentElement.clientHeight; }else{ // Android // Call resize for the first resize event after orientationchange // because the size information may not yet be up to date when the // event orientationchange occurs. f = function(evt){ var _conn = connect.connect(null, "onresize", null, function(e){ connect.disconnect(_conn); _f(e); }); }; curSize = dm.getScreenSize(); }; // Android: Watch for resize events when the virtual keyboard is shown/hidden. // The heuristic to detect this is that the screen width does not change // and the height changes by more than 100 pixels. // // iOS >= 6: Watch for resize events when entering or existing the new iOS6 // full-screen mode. The heuristic to detect this is that clientWidth does not // change while the clientHeight does change. connect.connect(null, "onresize", null, function(e){ if(ios6){ var newClientWidth = win.doc.documentElement.clientWidth, newClientHeight = win.doc.documentElement.clientHeight; if(newClientWidth == curClientWidth && newClientHeight != curClientHeight){ // full-screen mode has been entered/exited (iOS6) _f(e); } curClientWidth = newClientWidth; curClientHeight = newClientHeight; }else{ // Android var newSize = dm.getScreenSize(); if(newSize.w == curSize.w && Math.abs(newSize.h - curSize.h) >= 100){ // keyboard has been shown/hidden (Android) _f(e); } curSize = newSize; } }); } connect.connect(null, win.global.onorientationchange !== undefined ? "onorientationchange" : "onresize", null, f); win.body().style.visibility = "visible"; }); // TODO: return functions declared above in this hash, rather than // dojox.mobile. /*===== return { // summary: // A common module for dojox/mobile. // description: // This module includes common utility functions that are used by // dojox/mobile widgets. Also, it provides functions that are commonly // necessary for mobile web applications, such as the hide address bar // function. }; =====*/ return dm; }); }, 'dojo/touch':function(){ define(["./_base/kernel", "./aspect", "./dom", "./dom-class", "./_base/lang", "./on", "./has", "./mouse", "./domReady", "./_base/window"], function(dojo, aspect, dom, domClass, lang, on, has, mouse, domReady, win){ // module: // dojo/touch var ios4 = has("ios") < 5; // Detect if platform supports Pointer Events, and if so, the names of the events (pointerdown vs. MSPointerDown). var hasPointer = has("pointer-events") || has("MSPointer"), pointer = (function () { var pointer = {}; for (var type in { down: 1, move: 1, up: 1, cancel: 1, over: 1, out: 1 }) { pointer[type] = has("MSPointer") ? "MSPointer" + type.charAt(0).toUpperCase() + type.slice(1) : "pointer" + type; } return pointer; })(); // Detect if platform supports the webkit touchstart/touchend/... events var hasTouch = has("touch-events"); // Click generation variables var clicksInited, clickTracker, useTarget = false, clickTarget, clickX, clickY, clickDx, clickDy, clickTime; // Time of most recent touchstart, touchmove, or touchend event var lastTouch; function dualEvent(mouseType, touchType, pointerType){ // Returns synthetic event that listens for both the specified mouse event and specified touch event. // But ignore fake mouse events that were generated due to the user touching the screen. if(hasPointer && pointerType){ // IE10+: MSPointer* events are designed to handle both mouse and touch in a uniform way, // so just use that regardless of hasTouch. return function(node, listener){ return on(node, pointerType, listener); }; }else if(hasTouch){ return function(node, listener){ var handle1 = on(node, touchType, function(evt){ listener.call(this, evt); // On slow mobile browsers (see https://bugs.dojotoolkit.org/ticket/17634), // a handler for a touch event may take >1s to run. That time shouldn't // be included in the calculation for lastTouch. lastTouch = (new Date()).getTime(); }), handle2 = on(node, mouseType, function(evt){ if(!lastTouch || (new Date()).getTime() > lastTouch + 1000){ listener.call(this, evt); } }); return { remove: function(){ handle1.remove(); handle2.remove(); } }; }; }else{ // Avoid creating listeners for touch events on performance sensitive older browsers like IE6 return function(node, listener){ return on(node, mouseType, listener); }; } } function marked(/*DOMNode*/ node){ // Search for node ancestor has been marked with the dojoClick property to indicate special processing. // Returns marked ancestor. do{ if(node.dojoClick !== undefined){ return node; } }while(node = node.parentNode); } function doClicks(e, moveType, endType){ // summary: // Setup touch listeners to generate synthetic clicks immediately (rather than waiting for the browser // to generate clicks after the double-tap delay) and consistently (regardless of whether event.preventDefault() // was called in an event listener. Synthetic clicks are generated only if a node or one of its ancestors has // its dojoClick property set to truthy. If a node receives synthetic clicks because one of its ancestors has its // dojoClick property set to truthy, you can disable synthetic clicks on this node by setting its own dojoClick property // to falsy. if(mouse.isRight(e)){ return; // avoid spurious dojoclick event on IE10+; right click is just for context menu } var markedNode = marked(e.target); clickTracker = !e.target.disabled && markedNode && markedNode.dojoClick; // click threshold = true, number, x/y object, or "useTarget" if(clickTracker){ useTarget = (clickTracker == "useTarget"); clickTarget = (useTarget?markedNode:e.target); if(useTarget){ // We expect a click, so prevent any other // default action on "touchpress" e.preventDefault(); } clickX = e.changedTouches ? e.changedTouches[0].pageX - win.global.pageXOffset : e.clientX; clickY = e.changedTouches ? e.changedTouches[0].pageY - win.global.pageYOffset : e.clientY; clickDx = (typeof clickTracker == "object" ? clickTracker.x : (typeof clickTracker == "number" ? clickTracker : 0)) || 4; clickDy = (typeof clickTracker == "object" ? clickTracker.y : (typeof clickTracker == "number" ? clickTracker : 0)) || 4; // add move/end handlers only the first time a node with dojoClick is seen, // so we don't add too much overhead when dojoClick is never set. if(!clicksInited){ clicksInited = true; function updateClickTracker(e){ if(useTarget){ clickTracker = dom.isDescendant( win.doc.elementFromPoint( (e.changedTouches ? e.changedTouches[0].pageX - win.global.pageXOffset : e.clientX), (e.changedTouches ? e.changedTouches[0].pageY - win.global.pageYOffset : e.clientY)), clickTarget); }else{ clickTracker = clickTracker && (e.changedTouches ? e.changedTouches[0].target : e.target) == clickTarget && Math.abs((e.changedTouches ? e.changedTouches[0].pageX - win.global.pageXOffset : e.clientX) - clickX) <= clickDx && Math.abs((e.changedTouches ? e.changedTouches[0].pageY - win.global.pageYOffset : e.clientY) - clickY) <= clickDy; } } win.doc.addEventListener(moveType, function(e){ if(mouse.isRight(e)){ return; // avoid spurious dojoclick event on IE10+; right click is just for context menu } updateClickTracker(e); if(useTarget){ // prevent native scroll event and ensure touchend is // fire after touch moves between press and release. e.preventDefault(); } }, true); win.doc.addEventListener(endType, function(e){ if(mouse.isRight(e)){ return; // avoid spurious dojoclick event on IE10+; right click is just for context menu } updateClickTracker(e); if(clickTracker){ clickTime = (new Date()).getTime(); var target = (useTarget?clickTarget:e.target); if(target.tagName === "LABEL"){ // when clicking on a label, forward click to its associated input if any target = dom.byId(target.getAttribute("for")) || target; } //some attributes can be on the Touch object, not on the Event: //http://www.w3.org/TR/touch-events/#touch-interface var src = (e.changedTouches) ? e.changedTouches[0] : e; function createMouseEvent(type){ //create the synthetic event. //http://www.w3.org/TR/DOM-Level-3-Events/#widl-MouseEvent-initMouseEvent var evt = document.createEvent("MouseEvents"); evt._dojo_click = true; evt.initMouseEvent(type, true, //bubbles true, //cancelable e.view, e.detail, src.screenX, src.screenY, src.clientX, src.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, 0, //button null //related target ); return evt; } var mouseDownEvt = createMouseEvent("mousedown"); var mouseUpEvt = createMouseEvent("mouseup"); var clickEvt = createMouseEvent("click"); setTimeout(function(){ on.emit(target, "mousedown", mouseDownEvt); on.emit(target, "mouseup", mouseUpEvt); on.emit(target, "click", clickEvt); // refresh clickTime in case app-defined click handler took a long time to run clickTime = (new Date()).getTime(); }, 0); } }, true); function stopNativeEvents(type){ win.doc.addEventListener(type, function(e){ // Stop native events when we emitted our own click event. Note that the native click may occur // on a different node than the synthetic click event was generated on. For example, // click on a menu item, causing the menu to disappear, and then (~300ms later) the browser // sends a click event to the node that was *underneath* the menu. So stop all native events // sent shortly after ours, similar to what is done in dualEvent. // The INPUT.dijitOffScreen test is for offscreen inputs used in dijit/form/Button, on which // we call click() explicitly, we don't want to stop this event. var target = e.target; if(clickTracker && !e._dojo_click && (new Date()).getTime() <= clickTime + 1000 && !(target.tagName == "INPUT" && domClass.contains(target, "dijitOffScreen"))){ e.stopPropagation(); e.stopImmediatePropagation && e.stopImmediatePropagation(); if(type == "click" && (target.tagName != "INPUT" || (target.type == "radio" && // #18352 Do not preventDefault for radios that are not dijit or // dojox/mobile widgets. // (The CSS class dijitCheckBoxInput holds for both checkboxes and radio buttons.) (domClass.contains(target, "dijitCheckBoxInput") || domClass.contains(target, "mblRadioButton"))) || (target.type == "checkbox" && // #18352 Do not preventDefault for checkboxes that are not dijit or // dojox/mobile widgets. (domClass.contains(target, "dijitCheckBoxInput") || domClass.contains(target, "mblCheckBox")))) && target.tagName != "TEXTAREA" && target.tagName != "AUDIO" && target.tagName != "VIDEO"){ // preventDefault() breaks textual s on android, keyboard doesn't popup, // but it is still needed for checkboxes and radio buttons, otherwise in some cases // the checked state becomes inconsistent with the widget's state e.preventDefault(); } } }, true); } stopNativeEvents("click"); // We also stop mousedown/up since these would be sent well after with our "fast" click (300ms), // which can confuse some dijit widgets. stopNativeEvents("mousedown"); stopNativeEvents("mouseup"); } } } var hoveredNode; if(has("touch")){ if(hasPointer){ // MSPointer (IE10+) already has support for over and out, so we just need to init click support domReady(function(){ win.doc.addEventListener(pointer.down, function(evt){ doClicks(evt, pointer.move, pointer.up); }, true); }); }else{ domReady(function(){ // Keep track of currently hovered node hoveredNode = win.body(); // currently hovered node win.doc.addEventListener("touchstart", function(evt){ lastTouch = (new Date()).getTime(); // Precede touchstart event with touch.over event. DnD depends on this. // Use addEventListener(cb, true) to run cb before any touchstart handlers on node run, // and to ensure this code runs even if the listener on the node does event.stop(). var oldNode = hoveredNode; hoveredNode = evt.target; on.emit(oldNode, "dojotouchout", { relatedTarget: hoveredNode, bubbles: true }); on.emit(hoveredNode, "dojotouchover", { relatedTarget: oldNode, bubbles: true }); doClicks(evt, "touchmove", "touchend"); // init click generation }, true); function copyEventProps(evt){ // Make copy of event object and also set bubbles:true. Used when calling on.emit(). var props = lang.delegate(evt, { bubbles: true }); if(has("ios") >= 6){ // On iOS6 "touches" became a non-enumerable property, which // is not hit by for...in. Ditto for the other properties below. props.touches = evt.touches; props.altKey = evt.altKey; props.changedTouches = evt.changedTouches; props.ctrlKey = evt.ctrlKey; props.metaKey = evt.metaKey; props.shiftKey = evt.shiftKey; props.targetTouches = evt.targetTouches; } return props; } on(win.doc, "touchmove", function(evt){ lastTouch = (new Date()).getTime(); var newNode = win.doc.elementFromPoint( evt.pageX - (ios4 ? 0 : win.global.pageXOffset), // iOS 4 expects page coords evt.pageY - (ios4 ? 0 : win.global.pageYOffset) ); if(newNode){ // Fire synthetic touchover and touchout events on nodes since the browser won't do it natively. if(hoveredNode !== newNode){ // touch out on the old node on.emit(hoveredNode, "dojotouchout", { relatedTarget: newNode, bubbles: true }); // touchover on the new node on.emit(newNode, "dojotouchover", { relatedTarget: hoveredNode, bubbles: true }); hoveredNode = newNode; } // Unlike a listener on "touchmove", on(node, "dojotouchmove", listener) fires when the finger // drags over the specified node, regardless of which node the touch started on. if(!on.emit(newNode, "dojotouchmove", copyEventProps(evt))){ // emit returns false when synthetic event "dojotouchmove" is cancelled, so we prevent the // default behavior of the underlying native event "touchmove". evt.preventDefault(); } } }); // Fire a dojotouchend event on the node where the finger was before it was removed from the screen. // This is different than the native touchend, which fires on the node where the drag started. on(win.doc, "touchend", function(evt){ lastTouch = (new Date()).getTime(); var node = win.doc.elementFromPoint( evt.pageX - (ios4 ? 0 : win.global.pageXOffset), // iOS 4 expects page coords evt.pageY - (ios4 ? 0 : win.global.pageYOffset) ) || win.body(); // if out of the screen on.emit(node, "dojotouchend", copyEventProps(evt)); }); }); } } //device neutral events - touch.press|move|release|cancel/over/out var touch = { press: dualEvent("mousedown", "touchstart", pointer.down), move: dualEvent("mousemove", "dojotouchmove", pointer.move), release: dualEvent("mouseup", "dojotouchend", pointer.up), cancel: dualEvent(mouse.leave, "touchcancel", hasPointer ? pointer.cancel : null), over: dualEvent("mouseover", "dojotouchover", pointer.over), out: dualEvent("mouseout", "dojotouchout", pointer.out), enter: mouse._eventHandler(dualEvent("mouseover","dojotouchover", pointer.over)), leave: mouse._eventHandler(dualEvent("mouseout", "dojotouchout", pointer.out)) }; /*===== touch = { // summary: // This module provides unified touch event handlers by exporting // press, move, release and cancel which can also run well on desktop. // Based on http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html // Also, if the dojoClick property is set to truthy on a DOM node, dojo/touch generates // click events immediately for this node and its descendants (except for descendants that // have a dojoClick property set to falsy), to avoid the delay before native browser click events, // and regardless of whether evt.preventDefault() was called in a touch.press event listener. // // example: // Used with dojo/on // | define(["dojo/on", "dojo/touch"], function(on, touch){ // | on(node, touch.press, function(e){}); // | on(node, touch.move, function(e){}); // | on(node, touch.release, function(e){}); // | on(node, touch.cancel, function(e){}); // example: // Used with touch.* directly // | touch.press(node, function(e){}); // | touch.move(node, function(e){}); // | touch.release(node, function(e){}); // | touch.cancel(node, function(e){}); // example: // Have dojo/touch generate clicks without delay, with a default move threshold of 4 pixels // | node.dojoClick = true; // example: // Have dojo/touch generate clicks without delay, with a move threshold of 10 pixels horizontally and vertically // | node.dojoClick = 10; // example: // Have dojo/touch generate clicks without delay, with a move threshold of 50 pixels horizontally and 10 pixels vertically // | node.dojoClick = {x:50, y:5}; // example: // Disable clicks without delay generated by dojo/touch on a node that has an ancestor with property dojoClick set to truthy // | node.dojoClick = false; press: function(node, listener){ // summary: // Register a listener to 'touchstart'|'mousedown' for the given node // node: Dom // Target node to listen to // listener: Function // Callback function // returns: // A handle which will be used to remove the listener by handle.remove() }, move: function(node, listener){ // summary: // Register a listener that fires when the mouse cursor or a finger is dragged over the given node. // node: Dom // Target node to listen to // listener: Function // Callback function // returns: // A handle which will be used to remove the listener by handle.remove() }, release: function(node, listener){ // summary: // Register a listener to releasing the mouse button while the cursor is over the given node // (i.e. "mouseup") or for removing the finger from the screen while touching the given node. // node: Dom // Target node to listen to // listener: Function // Callback function // returns: // A handle which will be used to remove the listener by handle.remove() }, cancel: function(node, listener){ // summary: // Register a listener to 'touchcancel'|'mouseleave' for the given node // node: Dom // Target node to listen to // listener: Function // Callback function // returns: // A handle which will be used to remove the listener by handle.remove() }, over: function(node, listener){ // summary: // Register a listener to 'mouseover' or touch equivalent for the given node // node: Dom // Target node to listen to // listener: Function // Callback function // returns: // A handle which will be used to remove the listener by handle.remove() }, out: function(node, listener){ // summary: // Register a listener to 'mouseout' or touch equivalent for the given node // node: Dom // Target node to listen to // listener: Function // Callback function // returns: // A handle which will be used to remove the listener by handle.remove() }, enter: function(node, listener){ // summary: // Register a listener to mouse.enter or touch equivalent for the given node // node: Dom // Target node to listen to // listener: Function // Callback function // returns: // A handle which will be used to remove the listener by handle.remove() }, leave: function(node, listener){ // summary: // Register a listener to mouse.leave or touch equivalent for the given node // node: Dom // Target node to listen to // listener: Function // Callback function // returns: // A handle which will be used to remove the listener by handle.remove() } }; =====*/ 1 && (dojo.touch = touch); return touch; }); }, 'dijit/registry':function(){ define([ "dojo/_base/array", // array.forEach array.map "dojo/_base/window", // win.body "./main" // dijit._scopeName ], function(array, win, dijit){ // module: // dijit/registry var _widgetTypeCtr = {}, hash = {}; var registry = { // summary: // Registry of existing widget on page, plus some utility methods. // length: Number // Number of registered widgets length: 0, add: function(widget){ // summary: // Add a widget to the registry. If a duplicate ID is detected, a error is thrown. // widget: dijit/_WidgetBase // Any dijit/_WidgetBase subclass. if(hash[widget.id]){ throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered"); } hash[widget.id] = widget; this.length++; }, remove: function(/*String*/ id){ // summary: // Remove a widget from the registry. Does not destroy the widget; simply // removes the reference. if(hash[id]){ delete hash[id]; this.length--; } }, byId: function(/*String|Widget*/ id){ // summary: // Find a widget by it's id. // If passed a widget then just returns the widget. return typeof id == "string" ? hash[id] : id; // dijit/_WidgetBase }, byNode: function(/*DOMNode*/ node){ // summary: // Returns the widget corresponding to the given DOMNode return hash[node.getAttribute("widgetId")]; // dijit/_WidgetBase }, toArray: function(){ // summary: // Convert registry into a true Array // // example: // Work with the widget .domNodes in a real Array // | array.map(registry.toArray(), function(w){ return w.domNode; }); var ar = []; for(var id in hash){ ar.push(hash[id]); } return ar; // dijit/_WidgetBase[] }, getUniqueId: function(/*String*/widgetType){ // summary: // Generates a unique id for a given widgetType var id; do{ id = widgetType + "_" + (widgetType in _widgetTypeCtr ? ++_widgetTypeCtr[widgetType] : _widgetTypeCtr[widgetType] = 0); }while(hash[id]); return dijit._scopeName == "dijit" ? id : dijit._scopeName + "_" + id; // String }, findWidgets: function(root, skipNode){ // summary: // Search subtree under root returning widgets found. // Doesn't search for nested widgets (ie, widgets inside other widgets). // root: DOMNode // Node to search under. // skipNode: DOMNode // If specified, don't search beneath this node (usually containerNode). var outAry = []; function getChildrenHelper(root){ for(var node = root.firstChild; node; node = node.nextSibling){ if(node.nodeType == 1){ var widgetId = node.getAttribute("widgetId"); if(widgetId){ var widget = hash[widgetId]; if(widget){ // may be null on page w/multiple dojo's loaded outAry.push(widget); } }else if(node !== skipNode){ getChildrenHelper(node); } } } } getChildrenHelper(root); return outAry; }, _destroyAll: function(){ // summary: // Code to destroy all widgets and do other cleanup on page unload // Clean up focus manager lingering references to widgets and nodes dijit._curFocus = null; dijit._prevFocus = null; dijit._activeStack = []; // Destroy all the widgets, top down array.forEach(registry.findWidgets(win.body()), function(widget){ // Avoid double destroy of widgets like Menu that are attached to
// even though they are logically children of other widgets. if(!widget._destroyed){ if(widget.destroyRecursive){ widget.destroyRecursive(); }else if(widget.destroy){ widget.destroy(); } } }); }, getEnclosingWidget: function(/*DOMNode*/ node){ // summary: // Returns the widget whose DOM tree contains the specified DOMNode, or null if // the node is not contained within the DOM tree of any widget while(node){ var id = node.nodeType == 1 && node.getAttribute("widgetId"); if(id){ return hash[id]; } node = node.parentNode; } return null; }, // In case someone needs to access hash. // Actually, this is accessed from WidgetSet back-compatibility code _hash: hash }; dijit.registry = registry; return registry; }); }, 'dijit/main':function(){ define([ "dojo/_base/kernel" ], function(dojo){ // module: // dijit/main /*===== return { // summary: // The dijit package main module. // Deprecated. Users should access individual modules (ex: dijit/registry) directly. }; =====*/ return dojo.dijit; }); }, 'dojox/mobile/sniff':function(){ define([ "dojo/_base/kernel", "dojo/sniff" ], function(kernel, has){ kernel.deprecated("dojox/mobile/sniff", "Use dojo/sniff instead", "2.0"); // TODO: remove this in 2.0 has.add("iphone", has("ios")); /*===== return { // summary: // Deprecated: use dojo/sniff instead. // On iOS, dojox/mobile/sniff sets "iphone" to the same value as "ios" // for compatibility with earlier versions, but this should be considered deprecated. // In future versions, "iphone" will be set only when running on an iPhone (not iPad on iPod). }; =====*/ return has; }); }, 'dojox/mobile/uacss':function(){ define([ "dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/window", "./sniff" ], function(dojo, lang, win, has){ var html = win.doc.documentElement; html.className = lang.trim(html.className + " " + [ has('bb') ? "dj_bb" : "", has('android') ? "dj_android" : "", has("ios") ? "dj_ios" : "", has("ios") >= 6 ? "dj_ios6" : "", has("ios") ? "dj_iphone" : "", // TODO: remove for 2.0 has('ipod') ? "dj_ipod" : "", has('ipad') ? "dj_ipad" : "", has('ie') ? "dj_ie": "" ].join(" ").replace(/ +/g," ")); /*===== return { // summary: // Requiring this module adds CSS classes to your document's ` tag: // // - "dj_android" when running on Android; // - "dj_bb" when running on BlackBerry; // - "dj_ios" when running on iOS (iPhone, iPad or iPod); // - "dj_ios6" when running on iOS6+; this class is intended for the iphone theme to detect if it must use the iOS 6 variant of the theme. Currently applies on iOS 6 or later. // - "dj_iphone" when running on iPhone, iPad or iPod (Note: will be changed in future versions to be set only on iPhone); // - "dj_ipod" when running on iPod; // - "dj_ipad" when running on iPad. }; =====*/ return dojo; }); }, 'dojox/mobile/View':function(){ define([ "dojo/_base/array", "dojo/_base/config", "dojo/_base/connect", "dojo/_base/declare", "dojo/_base/lang", "dojo/sniff", "dojo/_base/window", "dojo/_base/Deferred", "dojo/dom", "dojo/dom-class", "dojo/dom-construct", "dojo/dom-geometry", "dojo/dom-style", "dijit/registry", "dijit/_Contained", "dijit/_Container", "dijit/_WidgetBase", "./ViewController", // to load ViewController for you (no direct references) "./common", "./transition", "./viewRegistry", "./_css3" ], function(array, config, connect, declare, lang, has, win, Deferred, dom, domClass, domConstruct, domGeometry, domStyle, registry, Contained, Container, WidgetBase, ViewController, common, transitDeferred, viewRegistry, css3){ // module: // dojox/mobile/View var dm = lang.getObject("dojox.mobile", true); return declare("dojox.mobile.View", [WidgetBase, Container, Contained], { // summary: // A container widget for any HTML element and/or Dojo widgets // description: // View is a container widget for any HTML element and/or Dojo widgets. // As a Dojo widget container it can itself contain View widgets // forming a set of nested views. A Dojo Mobile application is usually // made of multiple View widgets and the user can navigate through // the views back and forth with animated transition effects. // // When using several sibling views (direct children of the same // element), you can use the 'selected' attribute to define whether // the view should be displayed when the application is launched. // If no view has selected=true, the first sibling view is displayed // at startup time. // selected: Boolean // If true, the view is displayed at startup time. selected: false, // keepScrollPos: Boolean // If true, the scroll position is kept when transition occurs between views. keepScrollPos: true, // tag: String // The name of the HTML tag to create as domNode. The default value is "div". tag: "div", /* internal properties */ baseClass: "mblView", constructor: function(/*Object*/params, /*DomNode?*/node){ // summary: // Creates a new instance of the class. // params: // Contains the parameters. // node: // The DOM node. If none is specified, it is automatically created. if(node){ dom.byId(node).style.visibility = "hidden"; } }, destroy: function(){ viewRegistry.remove(this.id); this.inherited(arguments); }, buildRendering: function(){ if(!this.templateString){ // Create root node if it wasn't created by _TemplatedMixin this.domNode = this.containerNode = this.srcNodeRef || domConstruct.create(this.tag); } this._animEndHandle = this.connect(this.domNode, css3.name("animationEnd"), "onAnimationEnd"); this._animStartHandle = this.connect(this.domNode, css3.name("animationStart"), "onAnimationStart"); if(!config.mblCSS3Transition){ this._transEndHandle = this.connect(this.domNode, css3.name("transitionEnd"), "onAnimationEnd"); } if(has('mblAndroid3Workaround')){ // workaround for the screen flicker issue on Android 3.x/4.0 // applying "-webkit-transform-style:preserve-3d" to domNode can avoid // transition animation flicker domStyle.set(this.domNode, css3.name("transformStyle"), "preserve-3d"); } viewRegistry.add(this); this.inherited(arguments); }, startup: function(){ if(this._started){ return; } // Determine which view among the siblings should be visible. // Priority: // 1. fragment id in the url (ex. #view1,view2) // 2. this.selected // 3. the first view if(this._visible === undefined){ var views = this.getSiblingViews(); var ids = location.hash && location.hash.substring(1).split(/,/); var fragView, selectedView, firstView; array.forEach(views, function(v, i){ if(array.indexOf(ids, v.id) !== -1){ fragView = v; } if(i == 0){ firstView = v; } if(v.selected){ selectedView = v; } v._visible = false; }, this); (fragView || selectedView || firstView)._visible = true; } if(this._visible){ // The 2nd arg is not to hide its sibling views so that they can be // correctly initialized. this.show(true, true); // Defer firing events to let user connect to events just after creation // TODO: revisit this for 2.0 this.defer(function(){ this.onStartView(); connect.publish("/dojox/mobile/startView", [this]); }); } if(this.domNode.style.visibility === "hidden"){ // this check is to avoid screen flickers this.domNode.style.visibility = "inherit"; } // Need to call inherited first - so that child widgets get started // up correctly this.inherited(arguments); var parent = this.getParent(); if(!parent || !parent.resize){ // top level widget this.resize(); } if(!this._visible){ // hide() should be called last so that child widgets can be // initialized while they are visible. this.hide(); } }, resize: function(){ // summary: // Calls resize() of each child widget. array.forEach(this.getChildren(), function(child){ if(child.resize){ child.resize(); } }); }, onStartView: function(){ // summary: // Stub function to connect to from your application. // description: // Called only when this view is shown at startup time. }, onBeforeTransitionIn: function(moveTo, dir, transition, context, method){ // summary: // Stub function to connect to from your application. // description: // Called before the arriving transition occurs. }, onAfterTransitionIn: function(moveTo, dir, transition, context, method){ // summary: // Stub function to connect to from your application. // description: // Called after the arriving transition occurs. }, onBeforeTransitionOut: function(moveTo, dir, transition, context, method){ // summary: // Stub function to connect to from your application. // description: // Called before the leaving transition occurs. }, onAfterTransitionOut: function(moveTo, dir, transition, context, method){ // summary: // Stub function to connect to from your application. // description: // Called after the leaving transition occurs. }, _clearClasses: function(/*DomNode*/node){ // summary: // Clean up the domNode classes that were added while making a transition. // description: // Remove all the "mbl" prefixed classes except mbl*View. if(!node){ return; } var classes = []; array.forEach(lang.trim(node.className||"").split(/\s+/), function(c){ if(c.match(/^mbl\w*View$/) || c.indexOf("mbl") === -1){ classes.push(c); } }, this); node.className = classes.join(' '); }, _fixViewState: function(/*DomNode*/toNode){ // summary: // Sanity check for view transition states. // description: // Sometimes uninitialization of Views fails after making view transition, // and that results in failure of subsequent view transitions. // This function does the uninitialization for all the sibling views. var nodes = this.domNode.parentNode.childNodes; for(var i = 0; i < nodes.length; i++){ var n = nodes[i]; if(n.nodeType === 1 && domClass.contains(n, "mblView")){ this._clearClasses(n); } } this._clearClasses(toNode); // just in case toNode is a sibling of an ancestor. // #16337 // Uninitialization may fail to clear _inProgress when multiple // performTransition calls occur in a short duration of time. var toWidget = registry.byNode(toNode); if(toWidget){ toWidget._inProgress = false; } }, convertToId: function(moveTo){ if(typeof(moveTo) == "string"){ // removes a leading hash mark (#) and params if exists // ex. "#bar&myParam=0003" -> "bar" return moveTo.replace(/^#?([^&?]+).*/, "$1"); } return moveTo; }, _isBookmarkable: function(detail){ return detail.moveTo && (config.mblForceBookmarkable || detail.moveTo.charAt(0) === '#') && !detail.hashchange; }, performTransition: function(/*String*/moveTo, /*Number*/transitionDir, /*String*/transition, /*Object|null*/context, /*String|Function*/method /*...*/){ // summary: // Function to perform the various types of view transitions, such as fade, slide, and flip. // moveTo: String // The id of the transition destination view which resides in // the current page. // If the value has a hash sign ('#') before the id // (e.g. #view1) and the dojo/hash module is loaded by the user // application, the view transition updates the hash in the // browser URL so that the user can bookmark the destination // view. In this case, the user can also use the browser's // back/forward button to navigate through the views in the // browser history. // If null, transitions to a blank view. // If '#', returns immediately without transition. // transitionDir: Number // The transition direction. If 1, transition forward. If -1, transition backward. // For example, the slide transition slides the view from right to left when transitionDir == 1, // and from left to right when transitionDir == -1. // transition: String // A type of animated transition effect. You can choose from // the standard transition types, "slide", "fade", "flip", or // from the extended transition types, "cover", "coverv", // "dissolve", "reveal", "revealv", "scaleIn", "scaleOut", // "slidev", "swirl", "zoomIn", "zoomOut", "cube", and // "swap". If "none" is specified, transition occurs // immediately without animation. // context: Object // The object that the callback function will receive as "this". // method: String|Function // A callback function that is called when the transition has finished. // A function reference, or name of a function in context. // tags: // public // // example: // Transition backward to a view whose id is "foo" with the slide animation. // | performTransition("foo", -1, "slide"); // // example: // Transition forward to a blank view, and then open another page. // | performTransition(null, 1, "slide", null, function(){location.href = href;}); if(this._inProgress){ return; } // transition is in progress this._inProgress = true; // normalize the arg var detail, optArgs; if(moveTo && typeof(moveTo) === "object"){ detail = moveTo; optArgs = transitionDir; // array }else{ detail = { moveTo: moveTo, transitionDir: transitionDir, transition: transition, context: context, method: method }; optArgs = []; for(var i = 5; i < arguments.length; i++){ optArgs.push(arguments[i]); } } // save the parameters this._detail = detail; this._optArgs = optArgs; this._arguments = [ detail.moveTo, detail.transitionDir, detail.transition, detail.context, detail.method ]; if(detail.moveTo === "#"){ return; } var toNode; if(detail.moveTo){ toNode = this.convertToId(detail.moveTo); }else{ if(!this._dummyNode){ this._dummyNode = win.doc.createElement("div"); win.body().appendChild(this._dummyNode); } toNode = this._dummyNode; } if(this.addTransitionInfo && typeof(detail.moveTo) == "string" && this._isBookmarkable(detail)){ this.addTransitionInfo(this.id, detail.moveTo, {transitionDir:detail.transitionDir, transition:detail.transition}); } var fromNode = this.domNode; var fromTop = fromNode.offsetTop; toNode = this.toNode = dom.byId(toNode); if(!toNode){ console.log("dojox/mobile/View.performTransition: destination view not found: "+detail.moveTo); return; } toNode.style.visibility = "hidden"; toNode.style.display = ""; this._fixViewState(toNode); var toWidget = registry.byNode(toNode); if(toWidget){ // Now that the target view became visible, it's time to run resize() if(config.mblAlwaysResizeOnTransition || !toWidget._resized){ common.resizeAll(null, toWidget); toWidget._resized = true; } if(detail.transition && detail.transition != "none"){ // Temporarily add padding to align with the fromNode while transition toWidget._addTransitionPaddingTop(fromTop); } toWidget.load && toWidget.load(); // for ContentView toWidget.movedFrom = fromNode.id; } if(has('mblAndroidWorkaround') && !config.mblCSS3Transition && detail.transition && detail.transition != "none"){ // workaround for the screen flicker issue on Android 2.2/2.3 // apply "-webkit-transform-style:preserve-3d" to both toNode and fromNode // to make them 3d-transition-ready state just before transition animation domStyle.set(toNode, css3.name("transformStyle"), "preserve-3d"); domStyle.set(fromNode, css3.name("transformStyle"), "preserve-3d"); // show toNode offscreen to avoid flicker when switching "display" and "visibility" styles domClass.add(toNode, "mblAndroidWorkaround"); } this.onBeforeTransitionOut.apply(this, this._arguments); connect.publish("/dojox/mobile/beforeTransitionOut", [this].concat(lang._toArray(this._arguments))); if(toWidget){ // perform view transition keeping the scroll position if(this.keepScrollPos && !this.getParent()){ var scrollTop = win.body().scrollTop || win.doc.documentElement.scrollTop || win.global.pageYOffset || 0; fromNode._scrollTop = scrollTop; var toTop = (detail.transitionDir == 1) ? 0 : (toNode._scrollTop || 0); toNode.style.top = "0px"; if(scrollTop > 1 || toTop !== 0){ fromNode.style.top = toTop - scrollTop + "px"; // address bar hiding does not work on iOS 7+. if(!(has("ios") >= 7) && config.mblHideAddressBar !== false){ this.defer(function(){ // iPhone needs setTimeout (via defer) win.global.scrollTo(0, (toTop || 1)); }); } } }else{ toNode.style.top = "0px"; } toWidget.onBeforeTransitionIn.apply(toWidget, this._arguments); connect.publish("/dojox/mobile/beforeTransitionIn", [toWidget].concat(lang._toArray(this._arguments))); } toNode.style.display = "none"; toNode.style.visibility = "inherit"; common.fromView = this; common.toView = toWidget; this._doTransition(fromNode, toNode, detail.transition, detail.transitionDir); }, _addTransitionPaddingTop: function(/*String|Integer*/ value){ // add padding top to the view in order to get alignment during the transition this.containerNode.style.paddingTop = value + "px"; }, _removeTransitionPaddingTop: function(){ // remove padding top from the view after the transition this.containerNode.style.paddingTop = ""; }, _toCls: function(s){ // convert from transition name to corresponding class name // ex. "slide" -> "mblSlide" return "mbl"+s.charAt(0).toUpperCase() + s.substring(1); }, _doTransition: function(fromNode, toNode, transition, transitionDir){ var rev = (transitionDir == -1) ? " mblReverse" : ""; toNode.style.display = ""; if(!transition || transition == "none"){ this.domNode.style.display = "none"; this.invokeCallback(); }else if(config.mblCSS3Transition){ //get dojox/css3/transit first Deferred.when(transitDeferred, lang.hitch(this, function(transit){ //follow the style of .mblView.mblIn in View.css //need to set the toNode to absolute position var toPosition = domStyle.get(toNode, "position"); domStyle.set(toNode, "position", "absolute"); Deferred.when(transit(fromNode, toNode, {transition: transition, reverse: (transitionDir===-1)?true:false}),lang.hitch(this,function(){ domStyle.set(toNode, "position", toPosition); // Reset the temporary padding on toNode toNode.style.paddingTop = ""; this.invokeCallback(); })); })); }else{ if(transition.indexOf("cube") != -1){ if(has('ipad')){ domStyle.set(toNode.parentNode, {webkitPerspective:1600}); }else if(has("ios")){ domStyle.set(toNode.parentNode, {webkitPerspective:800}); } } var s = this._toCls(transition); if(has('mblAndroidWorkaround')){ // workaround for the screen flicker issue on Android 2.2 // applying transition css classes just after setting toNode.style.display = "" // causes flicker, so wait for a while using setTimeout (via defer) var _this = this; _this.defer(function(){ domClass.add(fromNode, s + " mblOut" + rev); domClass.add(toNode, s + " mblIn" + rev); domClass.remove(toNode, "mblAndroidWorkaround"); // remove offscreen style _this.defer(function(){ domClass.add(fromNode, "mblTransition"); domClass.add(toNode, "mblTransition"); }, 30); // 30 = 100 - 70, to make total delay equal to 100ms }, 70); // 70ms is experiential value }else{ domClass.add(fromNode, s + " mblOut" + rev); domClass.add(toNode, s + " mblIn" + rev); this.defer(function(){ domClass.add(fromNode, "mblTransition"); domClass.add(toNode, "mblTransition"); }, 100); } // set transform origin var fromOrigin = "50% 50%"; var toOrigin = "50% 50%"; var scrollTop, posX, posY; if(transition.indexOf("swirl") != -1 || transition.indexOf("zoom") != -1){ if(this.keepScrollPos && !this.getParent()){ scrollTop = win.body().scrollTop || win.doc.documentElement.scrollTop || win.global.pageYOffset || 0; }else{ scrollTop = -domGeometry.position(fromNode, true).y; } posY = win.global.innerHeight / 2 + scrollTop; fromOrigin = "50% " + posY + "px"; toOrigin = "50% " + posY + "px"; }else if(transition.indexOf("scale") != -1){ var viewPos = domGeometry.position(fromNode, true); posX = ((this.clickedPosX !== undefined) ? this.clickedPosX : win.global.innerWidth / 2) - viewPos.x; if(this.keepScrollPos && !this.getParent()){ scrollTop = win.body().scrollTop || win.doc.documentElement.scrollTop || win.global.pageYOffset || 0; }else{ scrollTop = -viewPos.y; } posY = ((this.clickedPosY !== undefined) ? this.clickedPosY : win.global.innerHeight / 2) + scrollTop; fromOrigin = posX + "px " + posY + "px"; toOrigin = posX + "px " + posY + "px"; } domStyle.set(fromNode, css3.add({}, {transformOrigin:fromOrigin})); domStyle.set(toNode, css3.add({}, {transformOrigin:toOrigin})); } }, onAnimationStart: function(e){ // summary: // A handler that is called when transition animation starts. }, onAnimationEnd: function(e){ // summary: // A handler that is called after transition animation ends. var name = e.animationName || e.target.className; if(name.indexOf("Out") === -1 && name.indexOf("In") === -1 && name.indexOf("Shrink") === -1){ return; } var isOut = false; if(domClass.contains(this.domNode, "mblOut")){ isOut = true; this.domNode.style.display = "none"; domClass.remove(this.domNode, [this._toCls(this._detail.transition), "mblIn", "mblOut", "mblReverse"]); }else{ // Reset the temporary padding this._removeTransitionPaddingTop(); } domStyle.set(this.domNode, css3.add({}, {transformOrigin:""})); if(name.indexOf("Shrink") !== -1){ var li = e.target; li.style.display = "none"; domClass.remove(li, "mblCloseContent"); // If target is placed inside scrollable, need to call onTouchEnd // to adjust scroll position var p = viewRegistry.getEnclosingScrollable(this.domNode); p && p.onTouchEnd(); } if(isOut){ this.invokeCallback(); } this._clearClasses(this.domNode); // clear the clicked position this.clickedPosX = this.clickedPosY = undefined; if(name.indexOf("Cube") !== -1 && name.indexOf("In") !== -1 && has("ios")){ this.domNode.parentNode.style[css3.name("perspective")] = ""; } }, invokeCallback: function(){ // summary: // A function to be called after performing a transition to // call a specified callback. this.onAfterTransitionOut.apply(this, this._arguments); connect.publish("/dojox/mobile/afterTransitionOut", [this].concat(this._arguments)); var toWidget = registry.byNode(this.toNode); if(toWidget){ toWidget.onAfterTransitionIn.apply(toWidget, this._arguments); connect.publish("/dojox/mobile/afterTransitionIn", [toWidget].concat(this._arguments)); toWidget.movedFrom = undefined; if(this.setFragIds && this._isBookmarkable(this._detail)){ this.setFragIds(toWidget); // setFragIds is defined in bookmarkable.js } } if(has('mblAndroidWorkaround')){ // workaround for the screen flicker issue on Android 2.2/2.3 // remove "-webkit-transform-style" style after transition finished // to avoid side effects such as input field auto-scrolling issue // use setTimeout (via defer) to avoid flicker in case of ScrollableView this.defer(function(){ if(toWidget){ domStyle.set(this.toNode, css3.name("transformStyle"), ""); } domStyle.set(this.domNode, css3.name("transformStyle"), ""); }); } var c = this._detail.context, m = this._detail.method; if(c || m){ if(!m){ m = c; c = null; } c = c || win.global; if(typeof(m) == "string"){ c[m].apply(c, this._optArgs); }else if(typeof(m) == "function"){ m.apply(c, this._optArgs); } } this._detail = this._optArgs = this._arguments = undefined; this._inProgress = false; }, isVisible: function(/*Boolean?*/checkAncestors){ // summary: // Return true if this view is visible // checkAncestors: // If true, in addition to its own visibility, also checks the // ancestors visibility to see if the view is actually being // shown or not. var visible = function(node){ return domStyle.get(node, "display") !== "none"; }; if(checkAncestors){ for(var n = this.domNode; n.tagName !== "BODY"; n = n.parentNode){ if(!visible(n)){ return false; } } return true; }else{ return visible(this.domNode); } }, getShowingView: function(){ // summary: // Find the currently showing view from my sibling views. // description: // Note that depending on the ancestor views' visibility, // the found view may not be actually shown. var nodes = this.domNode.parentNode.childNodes; for(var i = 0; i < nodes.length; i++){ var n = nodes[i]; if(n.nodeType === 1 && domClass.contains(n, "mblView") && n.style.display !== "none"){ return registry.byNode(n); } } return null; }, getSiblingViews: function(){ // summary: // Returns an array of the sibling views. if(!this.domNode.parentNode){ return [this]; } return array.map(array.filter(this.domNode.parentNode.childNodes, function(n){ return n.nodeType === 1 && domClass.contains(n, "mblView"); }), function(n){ return registry.byNode(n); }); }, show: function(/*Boolean?*/noEvent, /*Boolean?*/doNotHideOthers){ // summary: // Shows this view without a transition animation. var out = this.getShowingView(); if(!noEvent){ if(out){ out.onBeforeTransitionOut(out.id); connect.publish("/dojox/mobile/beforeTransitionOut", [out, out.id]); } this.onBeforeTransitionIn(this.id); connect.publish("/dojox/mobile/beforeTransitionIn", [this, this.id]); } if(doNotHideOthers){ this.domNode.style.display = ""; }else{ array.forEach(this.getSiblingViews(), function(v){ v.domNode.style.display = (v === this) ? "" : "none"; }, this); } this.load && this.load(); // for ContentView if(!noEvent){ if(out){ out.onAfterTransitionOut(out.id); connect.publish("/dojox/mobile/afterTransitionOut", [out, out.id]); } this.onAfterTransitionIn(this.id); connect.publish("/dojox/mobile/afterTransitionIn", [this, this.id]); } }, hide: function(){ // summary: // Hides this view without a transition animation. this.domNode.style.display = "none"; } }); }); }, 'dijit/_Contained':function(){ define([ "dojo/_base/declare", // declare "./registry" // registry.getEnclosingWidget(), registry.byNode() ], function(declare, registry){ // module: // dijit/_Contained return declare("dijit._Contained", null, { // summary: // Mixin for widgets that are children of a container widget // example: // | // make a basic custom widget that knows about its parents // | declare("my.customClass",[dijit._WidgetBase, dijit._Contained],{}); _getSibling: function(/*String*/ which){ // summary: // Returns next or previous sibling // which: // Either "next" or "previous" // tags: // private var p = this.getParent(); return (p && p._getSiblingOfChild && p._getSiblingOfChild(this, which == "previous" ? -1 : 1)) || null; // dijit/_WidgetBase }, getPreviousSibling: function(){ // summary: // Returns null if this is the first child of the parent, // otherwise returns the next element sibling to the "left". return this._getSibling("previous"); // dijit/_WidgetBase }, getNextSibling: function(){ // summary: // Returns null if this is the last child of the parent, // otherwise returns the next element sibling to the "right". return this._getSibling("next"); // dijit/_WidgetBase }, getIndexInParent: function(){ // summary: // Returns the index of this widget within its container parent. // It returns -1 if the parent does not exist, or if the parent // is not a dijit/_Container var p = this.getParent(); if(!p || !p.getIndexOfChild){ return -1; // int } return p.getIndexOfChild(this); // int } }); }); }, 'dijit/_Container':function(){ define([ "dojo/_base/array", // array.forEach array.indexOf "dojo/_base/declare", // declare "dojo/dom-construct", // domConstruct.place "dojo/_base/kernel" // kernel.deprecated ], function(array, declare, domConstruct, kernel){ // module: // dijit/_Container return declare("dijit._Container", null, { // summary: // Mixin for widgets that contain HTML and/or a set of widget children. buildRendering: function(){ this.inherited(arguments); if(!this.containerNode){ // All widgets with descendants must set containerNode. // NB: this code doesn't quite work right because for TabContainer it runs before // _TemplatedMixin::buildRendering(), and thus // sets this.containerNode to this.domNode, later to be overridden by the assignment in the template. this.containerNode = this.domNode; } }, addChild: function(/*dijit/_WidgetBase*/ widget, /*int?*/ insertIndex){ // summary: // Makes the given widget a child of this widget. // description: // Inserts specified child widget's dom node as a child of this widget's // container node, and possibly does other processing (such as layout). // I want to just call domConstruct.place(widget.domNode, this.containerNode, insertIndex), but the counting // is thrown off by text nodes and comment nodes that show up when constructed by markup. // In the future consider stripping those nodes on construction, either in the parser or this widget code. var refNode = this.containerNode; if(insertIndex > 0){ // Old-school way to get nth child; dojo.query would be easier but _Container was weened from dojo.query // in #10087 to minimize download size. Not sure if that's still and issue with new smaller dojo/query. refNode = refNode.firstChild; while(insertIndex > 0){ if(refNode.nodeType == 1){ insertIndex--; } refNode = refNode.nextSibling; } if(refNode){ insertIndex = "before"; }else{ // to support addChild(child, n-1) where there are n children (should add child at end) refNode = this.containerNode; insertIndex = "last"; } } domConstruct.place(widget.domNode, refNode, insertIndex); // If I've been started but the child widget hasn't been started, // start it now. Make sure to do this after widget has been // inserted into the DOM tree, so it can see that it's being controlled by me, // so it doesn't try to size itself. if(this._started && !widget._started){ widget.startup(); } }, removeChild: function(/*Widget|int*/ widget){ // summary: // Removes the passed widget instance from this widget but does // not destroy it. You can also pass in an integer indicating // the index within the container to remove (ie, removeChild(5) removes the sixth widget). if(typeof widget == "number"){ widget = this.getChildren()[widget]; } if(widget){ var node = widget.domNode; if(node && node.parentNode){ node.parentNode.removeChild(node); // detach but don't destroy } } }, hasChildren: function(){ // summary: // Returns true if widget has child widgets, i.e. if this.containerNode contains widgets. return this.getChildren().length > 0; // Boolean }, _getSiblingOfChild: function(/*dijit/_WidgetBase*/ child, /*int*/ dir){ // summary: // Get the next or previous widget sibling of child // dir: // if 1, get the next sibling // if -1, get the previous sibling // tags: // private var children = this.getChildren(), idx = array.indexOf(children, child); // int return children[idx + dir]; }, getIndexOfChild: function(/*dijit/_WidgetBase*/ child){ // summary: // Gets the index of the child in this container or -1 if not found return array.indexOf(this.getChildren(), child); // int } }); }); }, 'dijit/_WidgetBase':function(){ define([ "require", // require.toUrl "dojo/_base/array", // array.forEach array.map "dojo/aspect", "dojo/_base/config", // config.blankGif "dojo/_base/connect", // connect.connect "dojo/_base/declare", // declare "dojo/dom", // dom.byId "dojo/dom-attr", // domAttr.set domAttr.remove "dojo/dom-class", // domClass.add domClass.replace "dojo/dom-construct", // domConstruct.destroy domConstruct.place "dojo/dom-geometry", // isBodyLtr "dojo/dom-style", // domStyle.set, domStyle.get "dojo/has", "dojo/_base/kernel", "dojo/_base/lang", // mixin(), isArray(), etc. "dojo/on", "dojo/ready", "dojo/Stateful", // Stateful "dojo/topic", "dojo/_base/window", // win.body() "./Destroyable", "dojo/has!dojo-bidi?./_BidiMixin", "./registry" // registry.getUniqueId(), registry.findWidgets() ], function(require, array, aspect, config, connect, declare, dom, domAttr, domClass, domConstruct, domGeometry, domStyle, has, kernel, lang, on, ready, Stateful, topic, win, Destroyable, _BidiMixin, registry){ // module: // dijit/_WidgetBase // Flag to make dijit load modules the app didn't explicitly request, for backwards compatibility has.add("dijit-legacy-requires", !kernel.isAsync); // Flag to enable support for textdir attribute has.add("dojo-bidi", false); // For back-compat, remove in 2.0. if(has("dijit-legacy-requires")){ ready(0, function(){ var requires = ["dijit/_base/manager"]; require(requires); // use indirection so modules not rolled into a build }); } // Nested hash listing attributes for each tag, all strings in lowercase. // ex: {"div": {"style": true, "tabindex" true}, "form": { ... var tagAttrs = {}; function getAttrs(obj){ var ret = {}; for(var attr in obj){ ret[attr.toLowerCase()] = true; } return ret; } function nonEmptyAttrToDom(attr){ // summary: // Returns a setter function that copies the attribute to this.domNode, // or removes the attribute from this.domNode, depending on whether the // value is defined or not. return function(val){ domAttr[val ? "set" : "remove"](this.domNode, attr, val); this._set(attr, val); }; } function isEqual(a, b){ // summary: // Function that determines whether two values are identical, // taking into account that NaN is not normally equal to itself // in JS. return a === b || (/* a is NaN */ a !== a && /* b is NaN */ b !== b); } var _WidgetBase = declare("dijit._WidgetBase", [Stateful, Destroyable], { // summary: // Future base class for all Dijit widgets. // description: // Future base class for all Dijit widgets. // _Widget extends this class adding support for various features needed by desktop. // // Provides stubs for widget lifecycle methods for subclasses to extend, like postMixInProperties(), buildRendering(), // postCreate(), startup(), and destroy(), and also public API methods like set(), get(), and watch(). // // Widgets can provide custom setters/getters for widget attributes, which are called automatically by set(name, value). // For an attribute XXX, define methods _setXXXAttr() and/or _getXXXAttr(). // // _setXXXAttr can also be a string/hash/array mapping from a widget attribute XXX to the widget's DOMNodes: // // - DOM node attribute // | _setFocusAttr: {node: "focusNode", type: "attribute"} // | _setFocusAttr: "focusNode" (shorthand) // | _setFocusAttr: "" (shorthand, maps to this.domNode) // Maps this.focus to this.focusNode.focus, or (last example) this.domNode.focus // // - DOM node innerHTML // | _setTitleAttr: { node: "titleNode", type: "innerHTML" } // Maps this.title to this.titleNode.innerHTML // // - DOM node innerText // | _setTitleAttr: { node: "titleNode", type: "innerText" } // Maps this.title to this.titleNode.innerText // // - DOM node CSS class // | _setMyClassAttr: { node: "domNode", type: "class" } // Maps this.myClass to this.domNode.className // // - Toggle DOM node CSS class // | _setMyClassAttr: { node: "domNode", type: "toggleClass" } // Toggles myClass on this.domNode by this.myClass // // If the value of _setXXXAttr is an array, then each element in the array matches one of the // formats of the above list. // // If the custom setter is null, no action is performed other than saving the new value // in the widget (in this). // // If no custom setter is defined for an attribute, then it will be copied // to this.focusNode (if the widget defines a focusNode), or this.domNode otherwise. // That's only done though for attributes that match DOMNode attributes (title, // alt, aria-labelledby, etc.) // id: [const] String // A unique, opaque ID string that can be assigned by users or by the // system. If the developer passes an ID which is known not to be // unique, the specified ID is ignored and the system-generated ID is // used instead. id: "", _setIdAttr: "domNode", // to copy to this.domNode even for auto-generated id's // lang: [const] String // Rarely used. Overrides the default Dojo locale used to render this widget, // as defined by the [HTML LANG](http://www.w3.org/TR/html401/struct/dirlang.html#adef-lang) attribute. // Value must be among the list of locales specified during by the Dojo bootstrap, // formatted according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt) (like en-us). lang: "", // set on domNode even when there's a focus node. but don't set lang="", since that's invalid. _setLangAttr: nonEmptyAttrToDom("lang"), // dir: [const] String // Bi-directional support, as defined by the [HTML DIR](http://www.w3.org/TR/html401/struct/dirlang.html#adef-dir) // attribute. Either left-to-right "ltr" or right-to-left "rtl". If undefined, widgets renders in page's // default direction. dir: "", // set on domNode even when there's a focus node. but don't set dir="", since that's invalid. _setDirAttr: nonEmptyAttrToDom("dir"), // to set on domNode even when there's a focus node // class: String // HTML class attribute "class": "", _setClassAttr: { node: "domNode", type: "class" }, // Override automatic assigning type --> focusNode, it causes exception on IE6-8. // Instead, type must be specified as ${type} in the template, as part of the original DOM. _setTypeAttr: null, // style: String||Object // HTML style attributes as cssText string or name/value hash style: "", // title: String // HTML title attribute. // // For form widgets this specifies a tooltip to display when hovering over // the widget (just like the native HTML title attribute). // // For TitlePane or for when this widget is a child of a TabContainer, AccordionContainer, // etc., it's used to specify the tab label, accordion pane title, etc. In this case it's // interpreted as HTML. title: "", // tooltip: String // When this widget's title attribute is used to for a tab label, accordion pane title, etc., // this specifies the tooltip to appear when the mouse is hovered over that text. tooltip: "", // baseClass: [protected] String // Root CSS class of the widget (ex: dijitTextBox), used to construct CSS classes to indicate // widget state. baseClass: "", // srcNodeRef: [readonly] DomNode // pointer to original DOM node srcNodeRef: null, // domNode: [readonly] DomNode // This is our visible representation of the widget! Other DOM // Nodes may by assigned to other properties, usually through the // template system's data-dojo-attach-point syntax, but the domNode // property is the canonical "top level" node in widget UI. domNode: null, // containerNode: [readonly] DomNode // Designates where children of the source DOM node will be placed. // "Children" in this case refers to both DOM nodes and widgets. // For example, for myWidget: // // |