diff options
Diffstat (limited to 'js/dojo/dojox/mobile/View.js')
| -rw-r--r-- | js/dojo/dojox/mobile/View.js | 513 |
1 files changed, 513 insertions, 0 deletions
diff --git a/js/dojo/dojox/mobile/View.js b/js/dojo/dojox/mobile/View.js new file mode 100644 index 0000000..4230200 --- /dev/null +++ b/js/dojo/dojox/mobile/View.js @@ -0,0 +1,513 @@ +//>>built +define("dojox/mobile/View", [ + "dojo/_base/kernel", // to test dojo.hash + "dojo/_base/array", + "dojo/_base/config", + "dojo/_base/connect", + "dojo/_base/declare", + "dojo/_base/lang", + "dojo/_base/sniff", + "dojo/_base/window", + "dojo/_base/Deferred", + "dojo/dom", + "dojo/dom-class", + "dojo/dom-geometry", + "dojo/dom-style", +// "dojo/hash", // optionally prereq'ed + "dijit/registry", // registry.byNode + "dijit/_Contained", + "dijit/_Container", + "dijit/_WidgetBase", + "./ViewController", // to load ViewController for you (no direct references) + "./transition" +], function(dojo, array, config, connect, declare, lang, has, win, Deferred, dom, domClass, domGeometry, domStyle, registry, Contained, Container, WidgetBase, ViewController, transitDeferred){ + +/*===== + var Contained = dijit._Contained; + var Container = dijit._Container; + var WidgetBase = dijit._WidgetBase; + var ViewController = dojox.mobile.ViewController; +=====*/ + + // module: + // dojox/mobile/View + // summary: + // A widget that represents a view that occupies the full screen + + var dm = lang.getObject("dojox.mobile", true); + + return declare("dojox.mobile.View", [WidgetBase, Container, Contained], { + // summary: + // A widget that represents a view that occupies the full screen + // description: + // View acts as a container for any HTML and/or widgets. An entire + // HTML page can have multiple View widgets and the user can + // navigate through the views back and forth without page + // transitions. + + // selected: Boolean + // If true, the view is displayed at startup time. + selected: false, + + // keepScrollPos: Boolean + // If true, the scroll position is kept between views. + keepScrollPos: true, + + constructor: function(params, node){ + if(node){ + dom.byId(node).style.visibility = "hidden"; + } + this._aw = has("android") >= 2.2 && has("android") < 3; // flag for android animation workaround + }, + + buildRendering: function(){ + this.domNode = this.containerNode = this.srcNodeRef || win.doc.createElement("DIV"); + this.domNode.className = "mblView"; + this.connect(this.domNode, "webkitAnimationEnd", "onAnimationEnd"); + this.connect(this.domNode, "webkitAnimationStart", "onAnimationStart"); + if(!config['mblCSS3Transition']){ + this.connect(this.domNode, "webkitTransitionEnd", "onAnimationEnd"); + } + var id = location.href.match(/#(\w+)([^\w=]|$)/) ? RegExp.$1 : null; + + this._visible = this.selected && !id || this.id == id; + + if(this.selected){ + dm._defaultView = this; + } + }, + + startup: function(){ + if(this._started){ return; } + var siblings = []; + var children = this.domNode.parentNode.childNodes; + var visible = false; + // check if a visible view exists + for(var i = 0; i < children.length; i++){ + var c = children[i]; + if(c.nodeType === 1 && domClass.contains(c, "mblView")){ + siblings.push(c); + visible = visible || registry.byNode(c)._visible; + } + } + var _visible = this._visible; + // if no visible view exists, make the first view visible + if(siblings.length === 1 || (!visible && siblings[0] === this.domNode)){ + _visible = true; + } + var _this = this; + setTimeout(function(){ // necessary to render the view correctly + if(!_visible){ + _this.domNode.style.display = "none"; + }else{ + dm.currentView = _this; //TODO:1.8 reconsider this. currentView may not have a currently showing view when views are nested. + _this.onStartView(); + connect.publish("/dojox/mobile/startView", [_this]); + } + if(_this.domNode.style.visibility != "visible"){ // this check is to avoid screen flickers + _this.domNode.style.visibility = "visible"; + } + var parent = _this.getParent && _this.getParent(); + if(!parent || !parent.resize){ // top level widget + _this.resize(); + } + }, has("ie") ? 100 : 0); // give IE a little time to complete drawing + this.inherited(arguments); + }, + + 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. + }, + + _saveState: function(moveTo, dir, transition, context, method){ + this._context = context; + this._method = method; + if(transition == "none"){ + transition = null; + } + this._moveTo = moveTo; + this._dir = dir; + this._transition = transition; + this._arguments = lang._toArray(arguments); + this._args = []; + if(context || method){ + for(var i = 5; i < arguments.length; i++){ + this._args.push(arguments[i]); + } + } + }, + + _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")){ + n.className = "mblView"; //TODO: Should remove classes one by one. This would clear user defined classes or even mblScrollableView. + } + } + toNode.className = "mblView"; // just in case toNode is a sibling of an ancestor. + }, + + convertToId: function(moveTo){ + if(typeof(moveTo) == "string"){ + // removes a leading hash mark (#) and params if exists + // ex. "#bar&myParam=0003" -> "bar" + moveTo.match(/^#?([^&?]+)/); + return RegExp.$1; + } + return moveTo; + }, + + performTransition: function(/*String*/moveTo, /*Number*/dir, /*String*/transition, + /*Object|null*/context, /*String|Function*/method /*optional args*/){ + // 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. + // dir: 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 dir == 1, + // and from left to right when dir == -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". 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 been 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(moveTo === "#"){ return; } + if(dojo.hash){ + if(typeof(moveTo) == "string" && moveTo.charAt(0) == '#' && !dm._params){ + dm._params = []; + for(var i = 0; i < arguments.length; i++){ + dm._params.push(arguments[i]); + } + dojo.hash(moveTo); + return; + } + } + this._saveState.apply(this, arguments); + var toNode; + if(moveTo){ + toNode = this.convertToId(moveTo); + }else{ + if(!this._dummyNode){ + this._dummyNode = win.doc.createElement("DIV"); + win.body().appendChild(this._dummyNode); + } + toNode = this._dummyNode; + } + 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: "+moveTo); return; } + toNode.style.visibility = this._aw ? "visible" : "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){ + dm.resizeAll(null, toWidget); + toWidget._resized = true; + } + + if(transition && transition != "none"){ + // Temporarily add padding to align with the fromNode while transition + toWidget.containerNode.style.paddingTop = fromTop + "px"; + } + + toWidget.movedFrom = fromNode.id; + } + + this.onBeforeTransitionOut.apply(this, arguments); + connect.publish("/dojox/mobile/beforeTransitionOut", [this].concat(lang._toArray(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 = (dir == 1) ? 0 : (toNode._scrollTop || 0); + toNode.style.top = "0px"; + if(scrollTop > 1 || toTop !== 0){ + fromNode.style.top = toTop - scrollTop + "px"; + if(config["mblHideAddressBar"] !== false){ + setTimeout(function(){ // iPhone needs setTimeout + win.global.scrollTo(0, (toTop || 1)); + }, 0); + } + } + }else{ + toNode.style.top = "0px"; + } + toWidget.onBeforeTransitionIn.apply(toWidget, arguments); + connect.publish("/dojox/mobile/beforeTransitionIn", [toWidget].concat(lang._toArray(arguments))); + } + if(!this._aw){ + toNode.style.display = "none"; + toNode.style.visibility = "visible"; + } + + if(dm._iw && dm.scrollable){ // Workaround for iPhone flicker issue (only when scrollable.js is loaded) + var ss = dm.getScreenSize(); + // Show cover behind the view. + // cover's z-index is set to -10000, lower than z-index value specified in transition css. + win.body().appendChild(dm._iwBgCover); + domStyle.set(dm._iwBgCover, { + position: "absolute", + top: "0px", + left: "0px", + height: (ss.h + 1) + "px", // "+1" means the height of scrollTo(0,1) + width: ss.w + "px", + backgroundColor: domStyle.get(win.body(), "background-color"), + zIndex: -10000, + display: "" + }); + // Show toNode behind the cover. + domStyle.set(toNode, { + position: "absolute", + zIndex: -10001, + visibility: "visible", + display: "" + }); + // setTimeout seems to be necessary to avoid flicker. + // Also the duration of setTimeout should be long enough to avoid flicker. + // 0 is not effective. 50 sometimes causes flicker. + setTimeout(lang.hitch(this, function(){ + this._doTransition(fromNode, toNode, transition, dir); + }), 80); + }else{ + this._doTransition(fromNode, toNode, transition, dir); + } + }, + _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, dir){ + var rev = (dir == -1) ? " mblReverse" : ""; + if(dm._iw && dm.scrollable){ // Workaround for iPhone flicker issue (only when scrollable.js is loaded) + // Show toNode after flicker ends + domStyle.set(toNode, { + position: "", + zIndex: "" + }); + // Remove cover + win.body().removeChild(dm._iwBgCover); + }else if(!this._aw){ + 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: (dir===-1)?true:false}),lang.hitch(this,function(){ + domStyle.set(toNode, "position", toPosition); + this.invokeCallback(); + })); + })); + }else{ + var s = this._toCls(transition); + domClass.add(fromNode, s + " mblOut" + rev); + domClass.add(toNode, s + " mblIn" + rev); + setTimeout(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, {webkitTransformOrigin:fromOrigin}); + domStyle.set(toNode, {webkitTransformOrigin:toOrigin}); + } + dm.currentView = registry.byNode(toNode); + }, + + onAnimationStart: function(e){ + }, + + + onAnimationEnd: function(e){ + 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._transition), "mblIn", "mblOut", "mblReverse"]); + }else{ + // Reset the temporary padding + this.containerNode.style.paddingTop = ""; + } + domStyle.set(this.domNode, {webkitTransformOrigin:""}); + if(name.indexOf("Shrink") !== -1){ + var li = e.target; + li.style.display = "none"; + domClass.remove(li, "mblCloseContent"); + } + if(isOut){ + this.invokeCallback(); + } + // this.domNode may be destroyed as a result of invoking the callback, + // so check for that before accessing it. + this.domNode && (this.domNode.className = "mblView"); + + // clear the clicked position + this.clickedPosX = this.clickedPosY = undefined; + }, + + invokeCallback: function(){ + 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; + } + + var c = this._context, m = this._method; + if(!c && !m){ return; } + if(!m){ + m = c; + c = null; + } + c = c || win.global; + if(typeof(m) == "string"){ + c[m].apply(c, this._args); + }else{ + m.apply(c, this._args); + } + }, + + getShowingView: function(){ + // summary: + // Find the currently showing view from my sibling views. + // description: + // Note that dojox.mobile.currentView is the last shown view. + // If the page consists of a splitter, there are multiple showing 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") && domStyle.get(n, "display") !== "none"){ + return registry.byNode(n); + } + } + return null; + }, + + show: function(){ + // summary: + // Shows this view without a transition animation. + var view = this.getShowingView(); + if(view){ + view.domNode.style.display = "none"; // from-style + } + this.domNode.style.display = ""; // to-style + dm.currentView = this; + } + }); +}); |
