summaryrefslogtreecommitdiff
path: root/js/dojo/dojox/app/scene.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/dojo/dojox/app/scene.js')
-rw-r--r--js/dojo/dojox/app/scene.js589
1 files changed, 589 insertions, 0 deletions
diff --git a/js/dojo/dojox/app/scene.js b/js/dojo/dojox/app/scene.js
new file mode 100644
index 0000000..3d01413
--- /dev/null
+++ b/js/dojo/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(){}
+ });
+});