123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588 |
- 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(){}
- });
- });
|