123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512 |
- 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;
- }
- });
- });
|