diff options
Diffstat (limited to 'js/dojo-1.7.2/dojox/app/scene.js')
| -rw-r--r-- | js/dojo-1.7.2/dojox/app/scene.js | 589 |
1 files changed, 589 insertions, 0 deletions
diff --git a/js/dojo-1.7.2/dojox/app/scene.js b/js/dojo-1.7.2/dojox/app/scene.js new file mode 100644 index 0000000..3d01413 --- /dev/null +++ b/js/dojo-1.7.2/dojox/app/scene.js @@ -0,0 +1,589 @@ +//>>built +define("dojox/app/scene", ["dojo/_base/kernel", + "dojo/_base/declare", + "dojo/_base/connect", + "dojo/_base/array", + "dojo/_base/Deferred", + "dojo/_base/lang", + "dojo/_base/sniff", + "dojo/dom-style", + "dojo/dom-geometry", + "dojo/dom-class", + "dojo/dom-construct", + "dojo/dom-attr", + "dojo/query", + "dijit", + "dojox", + "dijit/_WidgetBase", + "dijit/_TemplatedMixin", + "dijit/_WidgetsInTemplateMixin", + "dojox/css3/transit", + "./animation", + "./model", + "./view", + "./bind"], + function(dojo,declare,connect, array,deferred,dlang,has,dstyle,dgeometry,cls,dconstruct,dattr,query,dijit,dojox,WidgetBase,Templated,WidgetsInTemplate,transit, anim, model, baseView, bind){ + + var marginBox2contentBox = function(/*DomNode*/ node, /*Object*/ mb){ + // summary: + // Given the margin-box size of a node, return its content box size. + // Functions like dojo.contentBox() but is more reliable since it doesn't have + // to wait for the browser to compute sizes. + var cs = dstyle.getComputedStyle(node); + var me = dgeometry.getMarginExtents(node, cs); + var pb = dgeometry.getPadBorderExtents(node, cs); + return { + l: dstyle.toPixelValue(node, cs.paddingLeft), + t: dstyle.toPixelValue(node, cs.paddingTop), + w: mb.w - (me.w + pb.w), + h: mb.h - (me.h + pb.h) + }; + }; + + var capitalize = function(word){ + return word.substring(0,1).toUpperCase() + word.substring(1); + }; + + var size = function(widget, dim){ + // size the child + var newSize = widget.resize ? widget.resize(dim) : dgeometry.setMarginBox(widget.domNode, dim); + // record child's size + if(newSize){ + // if the child returned it's new size then use that + dojo.mixin(widget, newSize); + }else{ + // otherwise, call marginBox(), but favor our own numbers when we have them. + // the browser lies sometimes + dojo.mixin(widget, dgeometry.getMarginBox(widget.domNode)); + + dojo.mixin(widget, dim); + } + }; + + return declare("dojox.app.scene", [dijit._WidgetBase, dijit._TemplatedMixin, dijit._WidgetsInTemplateMixin], { + isContainer: true, + widgetsInTemplate: true, + defaultView: "default", + + selectedChild: null, + baseClass: "scene mblView", + isFullScreen: false, + defaultViewType: baseView, + + //Temporary work around for getting a null when calling getParent + getParent: function(){return null;}, + + + constructor: function(params,node){ + this.children={}; + if(params.parent){ + this.parent=params.parent + } + if(params.app){ + this.app = params.app; + } + }, + + buildRendering: function(){ + this.inherited(arguments); + dstyle.set(this.domNode, {width: "100%", "height": "100%"}); + cls.add(this.domNode,"dijitContainer"); + }, + + splitChildRef: function(childId){ + var id = childId.split(","); + if (id.length>0){ + var to = id.shift(); + }else{ + console.warn("invalid child id passed to splitChildRef(): ", childId); + } + + return { + id:to || this.defaultView, + next: id.join(',') + } + }, + + loadChild: function(childId,subIds){ + // if no childId, load the default view + if (!childId) { + var parts = this.defaultView ? this.defaultView.split(",") : "default"; + childId = parts.shift(); + subIds = parts.join(','); + } + + var cid = this.id+"_" + childId; + if (this.children[cid]){ + return this.children[cid]; + } + + if (this.views&& this.views[childId]){ + var conf = this.views[childId]; + if (!conf.dependencies){conf.dependencies=[];} + var deps = conf.template? conf.dependencies.concat(["dojo/text!app/"+conf.template]) : + conf.dependencies.concat([]); + + var def = new deferred(); + if (deps.length>0) { + require(deps,function(){ + def.resolve.call(def, arguments); + }); + }else{ + def.resolve(true); + } + + var loadChildDeferred = new deferred(); + var self = this; + deferred.when(def, function(){ + var ctor; + if (conf.type){ + ctor=dojo.getObject(conf.type); + }else if (self.defaultViewType){ + ctor=self.defaultViewType; + }else{ + throw Error("Unable to find appropriate ctor for the base child class"); + } + + var params = dojo.mixin({}, conf, { + id: self.id + "_" + childId, + templateString: conf.template?arguments[0][arguments[0].length-1]:"<div></div>", + parent: self, + app: self.app + }) + if (subIds){ + params.defaultView=subIds; + } + var child = new ctor(params); + //load child's model if it is not loaded before + if(!child.loadedModels){ + child.loadedModels = model(conf.models, self.loadedModels) + //TODO need to find out a better way to get all bindable controls in a view + bind([child], child.loadedModels); + } + var addResult = self.addChild(child); + //publish /app/loadchild event + //application can subscript this event to do user define operation like select TabBarButton, add dynamic script text etc. + connect.publish("/app/loadchild", [child]); + + var promise; + + subIds = subIds.split(','); + if ((subIds[0].length > 0) && (subIds.length > 1)) {//TODO join subIds + promise = child.loadChild(subIds[0], subIds[1]); + } + else + if (subIds[0].length > 0) { + promise = child.loadChild(subIds[0], ""); + } + + dojo.when(promise, function(){ + loadChildDeferred.resolve(addResult) + }); + }); + return loadChildDeferred; + } + + throw Error("Child '" + childId + "' not found."); + }, + + resize: function(changeSize,resultSize){ + var node = this.domNode; + + // set margin box size, unless it wasn't specified, in which case use current size + if(changeSize){ + dgeometry.setMarginBox(node, changeSize); + + // set offset of the node + if(changeSize.t){ node.style.top = changeSize.t + "px"; } + if(changeSize.l){ node.style.left = changeSize.l + "px"; } + } + + // If either height or width wasn't specified by the user, then query node for it. + // But note that setting the margin box and then immediately querying dimensions may return + // inaccurate results, so try not to depend on it. + var mb = resultSize || {}; + dojo.mixin(mb, changeSize || {}); // changeSize overrides resultSize + if( !("h" in mb) || !("w" in mb) ){ + mb = dojo.mixin(dgeometry.getMarginBox(node), mb); // just use dojo.marginBox() to fill in missing values + } + + // Compute and save the size of my border box and content box + // (w/out calling dojo.contentBox() since that may fail if size was recently set) + var cs = dstyle.getComputedStyle(node); + var me = dgeometry.getMarginExtents(node, cs); + var be = dgeometry.getBorderExtents(node, cs); + var bb = (this._borderBox = { + w: mb.w - (me.w + be.w), + h: mb.h - (me.h + be.h) + }); + var pe = dgeometry.getPadExtents(node, cs); + this._contentBox = { + l: dstyle.toPixelValue(node, cs.paddingLeft), + t: dstyle.toPixelValue(node, cs.paddingTop), + w: bb.w - pe.w, + h: bb.h - pe.h + }; + + // Callback for widget to adjust size of its children + this.layout(); + }, + + layout: function(){ + var fullScreenScene,children,hasCenter; + //console.log("fullscreen: ", this.selectedChild && this.selectedChild.isFullScreen); + if (this.selectedChild && this.selectedChild.isFullScreen) { + console.warn("fullscreen sceen layout"); + /* + fullScreenScene=true; + children=[{domNode: this.selectedChild.domNode,region: "center"}]; + dojo.query("> [region]",this.domNode).forEach(function(c){ + if(this.selectedChild.domNode!==c.domNode){ + dojo.style(c.domNode,"display","none"); + } + }) + */ + }else{ + children = query("> [region]", this.domNode).map(function(node){ + var w = dijit.getEnclosingWidget(node); + if (w){return w;} + + return { + domNode: node, + region: dattr.get(node,"region") + } + + }); + if (this.selectedChild){ + children = array.filter(children, function(c){ + if (c.region=="center" && this.selectedChild && this.selectedChild.domNode!==c.domNode){ + dstyle.set(c.domNode,"zIndex",25); + dstyle.set(c.domNode,'display','none'); + return false; + }else if (c.region!="center"){ + dstyle.set(c.domNode,"display",""); + dstyle.set(c.domNode,"zIndex",100); + } + + return c.domNode && c.region; + },this); + + // this.selectedChild.region="center"; + // dojo.attr(this.selectedChild.domNode,"region","center"); + // dojo.style(this.selectedChild.domNode, "display",""); + // dojo.style(this.selectedChild.domNode,"zIndex",50); + + // children.push({domNode: this.selectedChild.domNode, region: "center"}); + // children.push(this.selectedChild); + // console.log("children: ", children); + }else{ + array.forEach(children, function(c){ + if (c && c.domNode && c.region=="center"){ + dstyle.set(c.domNode,"zIndex",25); + dstyle.set(c.domNode,'display','none'); + } + }); + } + + } + // We don't need to layout children if this._contentBox is null for the operation will do nothing. + if (this._contentBox) { + this.layoutChildren(this.domNode, this._contentBox, children); + } + array.forEach(this.getChildren(), function(child){ + if (!child._started && child.startup){ + child.startup(); + } + + }); + + }, + + + layoutChildren: function(/*DomNode*/ container, /*Object*/ dim, /*Widget[]*/ children, + /*String?*/ changedRegionId, /*Number?*/ changedRegionSize){ + // summary + // Layout a bunch of child dom nodes within a parent dom node + // container: + // parent node + // dim: + // {l, t, w, h} object specifying dimensions of container into which to place children + // children: + // an array of Widgets or at least objects containing: + // * domNode: pointer to DOM node to position + // * region or layoutAlign: position to place DOM node + // * resize(): (optional) method to set size of node + // * id: (optional) Id of widgets, referenced from resize object, below. + // changedRegionId: + // If specified, the slider for the region with the specified id has been dragged, and thus + // the region's height or width should be adjusted according to changedRegionSize + // changedRegionSize: + // See changedRegionId. + + // copy dim because we are going to modify it + dim = dojo.mixin({}, dim); + + cls.add(container, "dijitLayoutContainer"); + + // Move "client" elements to the end of the array for layout. a11y dictates that the author + // needs to be able to put them in the document in tab-order, but this algorithm requires that + // client be last. TODO: move these lines to LayoutContainer? Unneeded other places I think. + children = array.filter(children, function(item){ return item.region != "center" && item.layoutAlign != "client"; }) + .concat(array.filter(children, function(item){ return item.region == "center" || item.layoutAlign == "client"; })); + + // set positions/sizes + array.forEach(children, function(child){ + var elm = child.domNode, + pos = (child.region || child.layoutAlign); + + // set elem to upper left corner of unused space; may move it later + var elmStyle = elm.style; + elmStyle.left = dim.l+"px"; + elmStyle.top = dim.t+"px"; + elmStyle.position = "absolute"; + + cls.add(elm, "dijitAlign" + capitalize(pos)); + + // Size adjustments to make to this child widget + var sizeSetting = {}; + + // Check for optional size adjustment due to splitter drag (height adjustment for top/bottom align + // panes and width adjustment for left/right align panes. + if(changedRegionId && changedRegionId == child.id){ + sizeSetting[child.region == "top" || child.region == "bottom" ? "h" : "w"] = changedRegionSize; + } + + // set size && adjust record of remaining space. + // note that setting the width of a <div> may affect its height. + if(pos == "top" || pos == "bottom"){ + sizeSetting.w = dim.w; + size(child, sizeSetting); + dim.h -= child.h; + if(pos == "top"){ + dim.t += child.h; + }else{ + elmStyle.top = dim.t + dim.h + "px"; + } + }else if(pos == "left" || pos == "right"){ + sizeSetting.h = dim.h; + size(child, sizeSetting); + dim.w -= child.w; + if(pos == "left"){ + dim.l += child.w; + }else{ + elmStyle.left = dim.l + dim.w + "px"; + } + }else if(pos == "client" || pos == "center"){ + size(child, dim); + } + }); + }, + + getChildren: function(){ + return this._supportingWidgets; + }, + + startup: function(){ + if(this._started){ return; } + this._started=true; + + var parts = this.defaultView?this.defaultView.split(","):"default"; + var toId, subIds; + toId= parts.shift(); + subIds = parts.join(','); + + if(this.views[this.defaultView] && this.views[this.defaultView]["defaultView"]){ + subIds = this.views[this.defaultView]["defaultView"]; + } + + if(this.models && !this.loadedModels){ + //if there is this.models config data and the models has not been loaded yet, + //load models at here using the configuration data and load model logic in model.js + this.loadedModels = model(this.models); + bind(this.getChildren(), this.loadedModels); + } + + //startup assumes all children are loaded into DOM before startup is called + //startup will only start the current available children. + var cid = this.id + "_" + toId; + if (this.children[cid]) { + var next = this.children[cid]; + + this.set("selectedChild", next); + + // If I am a not being controlled by a parent layout widget... + var parent = this.getParent && this.getParent(); + if (!(parent && parent.isLayoutContainer)) { + // Do recursive sizing and layout of all my descendants + // (passing in no argument to resize means that it has to glean the size itself) + this.resize(); + + // Since my parent isn't a layout container, and my style *may be* width=height=100% + // or something similar (either set directly or via a CSS class), + // monitor when my size changes so that I can re-layout. + // For browsers where I can't directly monitor when my size changes, + // monitor when the viewport changes size, which *may* indicate a size change for me. + this.connect(has("ie") ? this.domNode : dojo.global, 'onresize', function(){ + // Using function(){} closure to ensure no arguments to resize. + this.resize(); + }); + + } + + array.forEach(this.getChildren(), function(child){ + child.startup(); + }); + + //transition to _startView + if (this._startView && (this._startView != this.defaultView)) { + this.transition(this._startView, {}); + } + } + }, + + addChild: function(widget){ + cls.add(widget.domNode, this.baseClass + "_child"); + widget.region = "center";; + dattr.set(widget.domNode,"region","center"); + this._supportingWidgets.push(widget); + dconstruct.place(widget.domNode,this.domNode); + this.children[widget.id] = widget; + return widget; + }, + + removeChild: function(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 + + if(widget){ + var node = widget.domNode; + if(node && node.parentNode){ + node.parentNode.removeChild(node); // detach but don't destroy + } + return widget; + } + }, + + _setSelectedChildAttr: function(child,opts){ + if (child !== this.selectedChild) { + return deferred.when(child, dlang.hitch(this, function(child){ + if (this.selectedChild){ + if (this.selectedChild.deactivate){ + this.selectedChild.deactivate(); + } + + dstyle.set(this.selectedChild.domNode,"zIndex",25); + } + + //dojo.style(child.domNode, { + // "display": "", + // "zIndex": 50, + // "overflow": "auto" + //}); + this.selectedChild = child; + dstyle.set(child.domNode, "display", ""); + dstyle.set(child.domNode,"zIndex",50); + this.selectedChild=child; + if (this._started) { + if (child.startup && !child._started){ + child.startup(); + }else if (child.activate){ + child.activate(); + } + + } + this.layout(); + })); + } + }, + + + transition: function(transitionTo,opts){ + //summary: + // transitions from the currently visible scene to the defined scene. + // it should determine what would be the best transition unless + // an override in opts tells it to use a specific transitioning methodology + // the transitionTo is a string in the form of [view]@[scene]. If + // view is left of, the current scene will be transitioned to the default + // view of the specified scene (eg @scene2), if the scene is left off + // the app controller will instruct the active scene to the view (eg view1). If both + // are supplied (view1@scene2), then the application should transition to the scene, + // and instruct the scene to navigate to the view. + var toId,subIds,next, current = this.selectedChild; + console.log("scene", this.id, transitionTo); + if (transitionTo){ + var parts = transitionTo.split(","); + toId= parts.shift(); + subIds = parts.join(','); + + }else{ + toId = this.defaultView; + if(this.views[this.defaultView] && this.views[this.defaultView]["defaultView"]){ + subIds = this.views[this.defaultView]["defaultView"]; + } + } + + next = this.loadChild(toId,subIds); + + if (!current){ + //assume this.set(...) will return a promise object if child is first loaded + //return nothing if child is already in array of this.children + return this.set("selectedChild",next); + } + + var transitionDeferred = new deferred(); + deferred.when(next, dlang.hitch(this, function(next){ + var promise; + + if (next!==current){ + //TODO need to refactor here, when clicking fast, current will not be the + //view we want to start transition. For example, during transition 1 -> 2 + //if user click button to transition to 3 and then transition to 1. It will + //perform transition 2 -> 3 and 2 -> 1 because current is always point to + //2 during 1 -> 2 transition. + + var waitingList = anim.getWaitingList([next.domNode, current.domNode]); + //update registry with deferred objects in animations of args. + var transitionDefs = {}; + transitionDefs[current.domNode.id] = anim.playing[current.domNode.id] = new deferred(); + transitionDefs[next.domNode.id] = anim.playing[current.domNode.id] = new deferred(); + + deferred.when(waitingList, dojo.hitch(this, function(){ + //assume next is already loaded so that this.set(...) will not return + //a promise object. this.set(...) will handles the this.selectedChild, + //activate or deactivate views and refresh layout. + this.set("selectedChild", next); + + //publish /app/transition event + //application can subscript this event to do user define operation like select TabBarButton, etc. + connect.publish("/app/transition", [next, toId]); + transit(current.domNode,next.domNode,dojo.mixin({},opts,{transition: this.defaultTransition || "none", transitionDefs: transitionDefs})).then(dlang.hitch(this, function(){ + //dojo.style(current.domNode, "display", "none"); + if (subIds && next.transition){ + promise = next.transition(subIds,opts); + } + deferred.when(promise, function(){ + transitionDeferred.resolve(); + }); + })); + })); + return; + } + + //we didn't need to transition, but continue to propogate. + if (subIds && next.transition){ + promise = next.transition(subIds,opts); + } + deferred.when(promise, function(){ + transitionDeferred.resolve(); + }); + })); + return transitionDeferred; + }, + toString: function(){return this.id}, + + activate: function(){}, + deactive: function(){} + }); +}); |
