summaryrefslogtreecommitdiff
path: root/js/dojo/dojox/app
diff options
context:
space:
mode:
Diffstat (limited to 'js/dojo/dojox/app')
-rw-r--r--js/dojo/dojox/app/README.txt174
-rw-r--r--js/dojo/dojox/app/animation.js347
-rw-r--r--js/dojo/dojox/app/bind.js40
-rw-r--r--js/dojo/dojox/app/main.js116
-rw-r--r--js/dojo/dojox/app/model.js27
-rw-r--r--js/dojo/dojox/app/module/env.js22
-rw-r--r--js/dojo/dojox/app/module/history.js91
-rw-r--r--js/dojo/dojox/app/module/lifecycle.js27
-rw-r--r--js/dojo/dojox/app/scene.js589
-rw-r--r--js/dojo/dojox/app/schema/README1
-rw-r--r--js/dojo/dojox/app/schema/application.json55
-rw-r--r--js/dojo/dojox/app/schema/model.json11
-rw-r--r--js/dojo/dojox/app/schema/scene.json29
-rw-r--r--js/dojo/dojox/app/schema/store.json11
-rw-r--r--js/dojo/dojox/app/schema/view.json30
-rw-r--r--js/dojo/dojox/app/transition.js61
-rw-r--r--js/dojo/dojox/app/view.js16
17 files changed, 1647 insertions, 0 deletions
diff --git a/js/dojo/dojox/app/README.txt b/js/dojo/dojox/app/README.txt
new file mode 100644
index 0000000..1849006
--- /dev/null
+++ b/js/dojo/dojox/app/README.txt
@@ -0,0 +1,174 @@
+-------------------------------------------------------------------------------
+DojoX app
+-------------------------------------------------------------------------------
+Version 0.1
+Release date: 05/08/2011
+-------------------------------------------------------------------------------
+Project state: EXPERIMENTAL / Under Construction
+
+This project is under active development with increasing capabilities beginning
+in dojo 1.7, but is not yet capable or stable enough to use in production
+-------------------------------------------------------------------------------
+Project authors
+ Dustin Machi
+ Stephen Zhang
+-------------------------------------------------------------------------------
+Project description
+
+dojox/app is a small application framework providing a set of classes to manage the the lifecycle and behavior of a single page application hosted on mobile or desktop platforms. The main class, Application, is responsible for providing the lifecycle of the application, but is designed to be easily modified with additional custom behaviors. An application instance contains scenes and views which provide the visible user interface. The available views, scenes, module dependencies, and other information about the application are all passed into the Application class through a JSON configuration file.
+-------------------------------------------------------------------------------
+Dependencies:
+
+Dojo Core (dijit, dojox/mobile).
+-------------------------------------------------------------------------------
+Documentation
+
+config.json
+
+The config file defines all of the dependencies, application behaviors, top level views and scenes, and any other information required for the application to function.
+
+Example Config:
+<code>
+{
+ /* global application dependencies */
+ "dependencies": [
+ "dojox/mobile/Heading",
+ "dojo/mobile/RoundRect",
+ "my/custom/module"
+ ],
+
+ /* Application Modules. These are implicitly added to the above set of dependencies */
+ modules: [
+ "dojox/app/module/history",
+ "my/custom/appModule"
+ ],
+
+ /* The html template for the application itself */
+ template: "example.html",
+
+ /* the view to start on by default */
+ "defaultView": "home",
+
+ /* transition to use if none has been provided */
+ "defaultTransition": "slide",
+
+ /* Views and Scenes */
+ "views": {
+
+ /* home is a top level dojox.app.view */
+ "home": {
+
+ /* class to instantiate this view as */
+ "type": "dojox.app.view",
+
+ /* dependencies specific to this view */
+ "dependencies: [
+ "dojox/mobile/ListItem",
+ "dojox/mobile/EdgeToEdgeCategory"
+ ],
+
+ /* template to use for this view */
+ template: "views/home.html"
+ },
+
+ /* tabscene is a dojox.app.scene, and it contains three child views */
+
+ "tabscene": {
+ /* class to instantiate, a scene in this case */
+ "type": "dojox.app.scene",
+
+ /* the scene's template */
+ "template": "tabScene.html",
+
+ /* the default view within this scene */
+ "defaultView": "tab1",
+
+ /* when transitioning between tabs, use a flip animation by default */
+ "defaultTransition": "flip",
+
+ //the views available to this scene
+ "views": {
+ "tab1":{
+ "template": "views/tabs/tab1.html"
+ },
+ "tab2":{
+ "template": "views/tabs/tab2.html"
+ },
+ "tab3":{
+ "template": "views/tabs/tab3.html"
+ }
+ },
+
+ /* dependencies specific to this scene */
+ "dependencies":["dojox/mobile/RoundRectList","dojox/mobile/ListItem", "dojox/mobile/EdgeToEdgeCategory"],
+ }
+
+ }
+}
+
+</code>
+
+Property descriptions
+
+ - dependencies - These are the modules that are required for the application when defined at the root of the configuration. When defined inside of a scene or a view, the dependency property defines modules which must be loaded before that view/scene can be instantiated.
+
+ - modules - The modules property defines application modules that will mixed into the Application class to control the lifecycle and behavior of the application. These properties will become the array of mixins provided to a dojo.declare() extending the base Application class. In other words, the Application class that is instantiated is dynamically created at run time using the base class and this list of modules.
+
+ - template - This is the template/html that is used for the application when defined at the root of the configuration. Within the context of a view or a scene, it is the template/html for defining said component.
+
+ - defaultView - The default view defines the starting view for the application when loaded to its root.
+
+ - defaultTransition - This is the default transition method for top level views/scenes when defined at the root of the configuration. When defined within a scene, it is the default transition method for the associated scene only.
+
+ - views - The views property is a nested set of objects defining the views and scenes available to the application. Details of views and scene classes will be discussed below.
+
+Some additional properties, such as models, stores, id, name, and description are reserved for future use, but their exact use is still under development.
+
+The Application Class:
+ The application class itself doesn't currently exist as an exported class! The base Application class is a very simple extension of the Scene Class (see below) defined in dojox/app/main.js. This module file exports a generatation function, which when provided a configuration file will declare the application class that will actually be used on a page and then start it up at a specific node:
+
+<code>
+ require(["dojo/_base/html","dojox/app/main", "dojo/text!app/config.json"],function(dojo,Application,config){
+ app = Application(json.parse(config));
+ });
+</code>
+
+
+The Scene Class:
+
+ The Scene Class provides a templated container for views. Its purpose is to allow the layout of the scene to be provided through an html template and to have a set of children views which the scene transitions between. For example, to display a set of tabs, you would use a Scene with a child view for each tab. The scene's template would define where within the scene the views are displayed and where any tab buttons and such are displayed.
+
+ Internally, the Scene steals some concepts layout and templated dijits provide. The "template", for the base Scene is pretty simple and not really a template. It is simply HTML content. However, nodes within the template can be tagged with region="top" (bottom, left, right) to define where that node and its children should be displayed. For example:
+
+<code>
+<div style="background:#c5ccd3;" class="view mblView">
+ <div region="top" dojoType="dojox.mobile.Heading">Tab Scene</div>
+ <ul region="top" dojoType="dojox.mobile.TabBar" barType="segmentedControl">
+ <li dojoType="dojox.mobile.TabBarButton" icon1="images/tab-icon-16.png" icon2="images/tab-icon-16h.png" transitionOptions='{title:"TabScene-Tab1",target:"tabscene,tab1",url: "#tabscene,tab1"}' selected="true">Tab 1</li>
+ <li dojoType="dojox.mobile.TabBarButton" icon1="images/tab-icon-15.png" icon2="images/tab-icon-15h.png" transitionOptions='{title:"TabScene-Tab2",target:"tabscene,tab2",url: "#tabscene,tab2"}'>Tab 2</li>
+ <li dojoType="dojox.mobile.TabBarButton" icon1="images/tab-icon-10.png" icon2="images/tab-icon-10h.png" transitionOptions='{title:"TabScene-Tab3",target:"tabscene,tab3",url: "#tabscene,tab3"}'>Tab 3</li>
+ </ul>
+</div>
+</code>
+
+This template for the tab scene defines two areas with region top, a header and the tab buttons. The will be placed at the top of this scene when rendered.
+
+Normally, when using a BorderContainer, one would also have a region="center" section. In the case of a Scene however, the "center" region will be applied to the currently active view (the current tab for example).
+
+In addition to the code to support the appropriate lifecycle of the scene and its rendering, it provides a transition method which controls the transition of content from one child view to another. This includes propogating transition events on to children if the active child is itself another scene. Scene can container views and other scenes. Views can only be leaf nodes in the view tree.
+
+The View Class:
+
+Views, like Scenes, are also containers of content. However, instead of containing additional views as children, they contain only the content defined by their template. The template may contain widgets.
+
+All three of these classes are intended to, at their base, be as simple and feature free as possible providing only basic structure and lifecycle described above (though additional core methods/lifecycle hooks will be added as the are worked out). A developer using the dojox/app framework can define additional custom view or scene types simply by extending the base classes (or implementing equivalent functionality) and defining them in the applications configuration file. The base application need not be extended, as its extensions are provided at run time through the modules config property. Scenes and Views are easily extended and included in an app.
+
+TODO:
+
+dojox/app is still an experimental framework with several key pieces still under design and development before a final release. This final release is expected to occur prior to the Dojo 2.0 release. The following items are piece that are under development and testing and we see as requirements prior to the final release
+
+- Model/Store support. We have a couple of preliminary implementations of model/store support, including one for dojox/mvc. However, additional work and testing are required to come to a simple and agreed up on API for these components. While MVC systems such as dojox/mvc should be supported with first class capabilities, they should not be required. An application developer can 'control' the html of any one view by simply extending the view class and using javascript if they so desired.
+
+- Desktop/Mobile Branching - Dojox/app is not to be specific to any one particular web platform. Using css media selectors and definitions within the config, there will be support for choosing which set of views and parameters to use based on the users browser.
+
+- Intelligent build support - For performance, especially on the mobile side, an appropriate build of the application is required. Rather than adding a build profile for the app, there will be a wrapper utility that runs the build from the config.json. This will allow us to intelligently build the base layers and dynamically loaded layers which should be defined by dependencies and default views as well as other information.
diff --git a/js/dojo/dojox/app/animation.js b/js/dojo/dojox/app/animation.js
new file mode 100644
index 0000000..f1d90b0
--- /dev/null
+++ b/js/dojo/dojox/app/animation.js
@@ -0,0 +1,347 @@
+//>>built
+define("dojox/app/animation", ["dojo/_base/kernel",
+ "dojo/_base/lang",
+ "dojo/_base/declare",
+ "dojo/_base/array",
+ "dojo/_base/Deferred",
+ "dojo/DeferredList",
+ "dojo/on",
+ "dojo/_base/sniff"],
+ function(dojo, lang, declare, array, deferred, deferredList, on, has){
+ //TODO create cross platform animation/transition effects
+ var transitionEndEventName = "transitionend";
+ var transitionPrefix = "t"; //by default use "t" prefix and "ransition" to make word "transition"
+ var translateMethodStart = "translate3d(";//Android 2.x does not support translateX in CSS Transition, we need to use translate3d in webkit browsers
+ var translateMethodEnd = ",0,0)";
+ if(has("webkit")){
+ transitionPrefix = "WebkitT";
+ transitionEndEventName = "webkitTransitionEnd";
+ }else if(has("mozilla")){
+ transitionPrefix = "MozT";
+ translateMethodStart = "translateX(";
+ translateMethodEnd = ")";
+ }
+
+
+
+ //TODO find a way to lock the animation and prevent animation conflict
+ declare("dojox.app.animation", null, {
+
+
+ constructor: function(args){
+ //default config should be in animation object itself instead of its prototype
+ //otherwise, it might be easy for making mistake of modifying prototype
+ var defaultConfig = {
+ startState: {},
+ endState: {},
+ node: null,
+ duration: 250,
+ "in": true,
+ direction: 1,
+ autoClear: true
+ };
+
+ lang.mixin(this, defaultConfig);
+ lang.mixin(this, args);
+
+ //create the deferred object which will resolve after the animation is finished.
+ //We can rely on "onAfterEnd" function to notify the end of a single animation,
+ //but using a deferred object is easier to wait for multiple animations end.
+ if(!this.deferred){
+ this.deferred = new deferred();
+ }
+ },
+
+ play: function(){
+ //play the animation using CSS3 Transition
+ dojox.app.animation.groupedPlay([this]);
+ },
+
+ //method to apply the state of the transition
+ _applyState: function(state){
+ var style = this.node.style;
+ for(var property in state){
+ if(state.hasOwnProperty(property)){
+ style[property] = state[property];
+ }
+ }
+ },
+
+ //method to initialize state for transition
+ initState: function(){
+
+ //apply the immediate style change for initial state.
+ this.node.style[transitionPrefix + "ransitionProperty"] = "none";
+ this.node.style[transitionPrefix + "ransitionDuration"] = "0ms";
+ this._applyState(this.startState);
+
+ },
+
+ _beforeStart: function(){
+ if (this.node.style.display === "none"){
+ this.node.style.display = "";
+ }
+ this.beforeStart();
+ },
+
+ _beforeClear: function(){
+ this.node.style[transitionPrefix + "ransitionProperty"] = null;
+ this.node.style[transitionPrefix + "ransitionDuration"] = null;
+ if(this["in"] !== true){
+ this.node.style.display = "none";
+ }
+ this.beforeClear();
+ },
+
+ _onAfterEnd: function(){
+ this.deferred.resolve(this.node);
+ if(this.node.id && dojox.app.animation.playing[this.node.id]===this.deferred){
+ delete dojox.app.animation.playing[this.node.id];
+ }
+ this.onAfterEnd();
+ },
+
+ beforeStart: function(){
+
+ },
+
+ beforeClear: function(){
+
+ },
+
+ onAfterEnd: function(){
+
+ },
+
+ //method to start the transition
+ start: function(){
+ this._beforeStart();
+
+ var self = this;
+ //change the transition duration
+ self.node.style[transitionPrefix + "ransitionProperty"] = "all";
+ self.node.style[transitionPrefix + "ransitionDuration"] = self.duration + "ms";
+
+ //connect to clear the transition state after the transition end.
+ //Since the transition is conducted asynchronously, we need to
+ //connect to transition end event to clear the state
+ on.once(self.node, transitionEndEventName, function(){
+ self.clear();
+ });
+
+ this._applyState(this.endState);
+ },
+
+ //method to clear state after transition
+ clear: function(){
+ this._beforeClear();
+ this._removeState(this.endState);
+ console.log(this.node.id + " clear.");
+ this._onAfterEnd();
+ },
+
+ //create removeState method
+ _removeState: function(state){
+ var style = this.node.style;
+ for(var property in state){
+ if(state.hasOwnProperty(property)){
+ style[property] = null;
+ }
+ }
+ }
+
+ });
+
+ //TODO add the lock mechanism for all of the transition effects
+ // consider using only one object for one type of transition.
+ //TODO create the first animation, slide.
+ dojox.app.animation.slide = function(node, config){
+
+ //TODO create the return and set the startState, endState of the return
+ var ret = new dojox.app.animation(config);
+ ret.node = node;
+
+ var startX = "0";
+ var endX = "0";
+
+ if(ret["in"]){
+ if(ret.direction === 1){
+ startX = "100%";
+ }else{
+ startX = "-100%";
+ }
+ }else{
+ if(ret.direction === 1){
+ endX = "-100%";
+ }else{
+ endX = "100%";
+ }
+ }
+
+
+ ret.startState[transitionPrefix + "ransform"]=translateMethodStart+startX+translateMethodEnd;
+
+ ret.endState[transitionPrefix + "ransform"]=translateMethodStart+endX+translateMethodEnd;
+
+ return ret;
+ };
+
+
+ //fade in/out animation effects
+ dojox.app.animation.fade = function(node, config){
+
+ var ret = new dojox.app.animation(config);
+ ret.node = node;
+
+ var startOpacity = "0";
+ var endOpacity = "0";
+
+ if(ret["in"]){
+ endOpacity = "1";
+ }else{
+ startOpacity = "1";
+ }
+
+ lang.mixin(ret, {
+ startState:{
+ "opacity": startOpacity
+ },
+ endState:{
+ "opacity": endOpacity
+ }
+ });
+
+ return ret;
+ };
+
+ //fade in/out animation effects
+ dojox.app.animation.flip = function(node, config){
+
+ var ret = new dojox.app.animation(config);
+ ret.node = node;
+
+ if(ret["in"]){
+ //Need to set opacity here because Android 2.2 has bug that
+ //scale(...) in transform does not persist status
+ lang.mixin(ret,{
+ startState:{
+ "opacity": "0"
+ },
+ endState:{
+ "opacity": "1"
+ }
+ });
+ ret.startState[transitionPrefix + "ransform"]="scale(0,0.8) skew(0,-30deg)";
+ ret.endState[transitionPrefix + "ransform"]="scale(1,1) skew(0,0)";
+ }else{
+ lang.mixin(ret,{
+ startState:{
+ "opacity": "1"
+ },
+ endState:{
+ "opacity": "0"
+ }
+ });
+ ret.startState[transitionPrefix + "ransform"]="scale(1,1) skew(0,0)";
+ ret.endState[transitionPrefix + "ransform"]="scale(0,0.8) skew(0,30deg)";
+ }
+
+ return ret;
+ };
+
+ var getWaitingList = function(/*Array*/ nodes){
+ var defs = [];
+ array.forEach(nodes, function(node){
+ //check whether the node is under other animation
+ if(node.id && dojox.app.animation.playing[node.id]){
+ //TODO hook on deferred object in dojox.app.animation.playing
+ defs.push(dojox.app.animation.playing[node.id]);
+ }
+
+ });
+ return new deferredList(defs);
+ };
+
+ dojox.app.animation.getWaitingList = getWaitingList;
+
+ //TODO groupedPlay should ensure the UI update happens when
+ //all animations end.
+ //the group player to start multiple animations together
+ dojox.app.animation.groupedPlay = function(/*Array*/args){
+ //args should be array of dojox.app.animation
+
+ var animNodes = array.filter(args, function(item){
+ return item.node;
+ });
+
+ var waitingList = getWaitingList(animNodes);
+
+ //update registry with deferred objects in animations of args.
+ array.forEach(args, function(item){
+ if(item.node.id){
+ dojox.app.animation.playing[item.node.id] = item.deferred;
+ }
+ });
+
+ //TODO wait for all deferred object in deferred list to resolve
+ dojo.when(waitingList, function(){
+ array.forEach(args, function(item){
+ //set the start state
+ item.initState();
+ });
+
+ //Assume the fps of the animation should be higher than 30 fps and
+ //allow the browser to use one frame's time to redraw so that
+ //the transition can be started
+ setTimeout(function(){
+ array.forEach(args, function(item){
+ item.start();
+ });
+ }, 33);
+ });
+ };
+
+ //the chain player to start multiple animations one by one
+ dojox.app.animation.chainedPlay = function(/*Array*/args){
+ //args should be array of dojox.app.animation
+
+ var animNodes = array.filter(args, function(item){
+ return item.node;
+ });
+
+ var waitingList = getWaitingList(animNodes);
+
+ //update registry with deferred objects in animations of args.
+ array.forEach(args, function(item){
+ if(item.node.id){
+ dojox.app.animation.playing[item.node.id] = item.deferred;
+ }
+ });
+
+ dojo.when(waitingList, function(){
+ array.forEach(args, function(item){
+ //set the start state
+ item.initState();
+ });
+
+ //chain animations together
+ for (var i=1, len=args.length; i < len; i++){
+ args[i-1].deferred.then(lang.hitch(args[i], function(){
+ this.start();
+ }));
+ }
+
+ //Assume the fps of the animation should be higher than 30 fps and
+ //allow the browser to use one frame's time to redraw so that
+ //the transition can be started
+ setTimeout(function(){
+ args[0].start();
+ }, 33);
+ });
+ };
+
+ //TODO complete the registry mechanism for animation handling and prevent animation conflicts
+ dojox.app.animation.playing = {};
+
+ return dojox.app.animation;
+});
diff --git a/js/dojo/dojox/app/bind.js b/js/dojo/dojox/app/bind.js
new file mode 100644
index 0000000..0d6dede
--- /dev/null
+++ b/js/dojo/dojox/app/bind.js
@@ -0,0 +1,40 @@
+//>>built
+define("dojox/app/bind", ["dojo/_base/kernel", "dojo/query" , "dojo/_base/array", "dijit", "dojo/_base/json"], function(dojo, query, array, dijit, djson){
+ return function(/*Array of widgets*/widgets, /*Object*/ models){
+ array.forEach(widgets, function(item){
+ //TODO need to find a better way to get all bindable widgets
+ var bindWidgets = query("div[dojoType^=\"dojox.mvc\"],div[data-dojo-type^=\"dojox.mvc\"]", item.domNode);
+ //set ref for each dojox.mvc widgets.
+ array.forEach(bindWidgets, function(widget){
+ //TODO need to find a better way to know which model the widget is bound to
+ //currently, the ref attribute in dojox.mvc.Group cannot be empty, leave
+ //explicit string with single quote in ref attribute.
+ var ref = widget.getAttribute("ref");
+
+ if(ref === null){
+ var refProps = widget.getAttribute("data-dojo-props");
+ if(refProps){
+ try{
+ refProps = djson.fromJson("{" + refProps + "}");
+ }catch(e){
+ // give the user a pointer to their invalid parameters. FIXME: can we kill this in production?
+ throw new Error(e.toString() + " in data-dojo-props='" + extra + "'");
+ }
+ ref = refProps.ref.replace(/^\s*rel\s*:\s*/, "");
+ }
+ }
+
+ if (ref) {
+ if(ref[0] === "'"){
+ ref = ref.substring(1, ref.length-1);
+ }
+ var model = dojo.getObject(ref, false, models);
+ if (model){
+ dijit.byNode(widget).set("ref", model);
+ }
+ }
+ }, this);
+ }, this);
+
+ }
+});
diff --git a/js/dojo/dojox/app/main.js b/js/dojo/dojox/app/main.js
new file mode 100644
index 0000000..66c9414
--- /dev/null
+++ b/js/dojo/dojox/app/main.js
@@ -0,0 +1,116 @@
+//>>built
+define("dojox/app/main", ["dojo/_base/kernel",
+ "dojo/_base/lang",
+ "dojo/_base/declare",
+ "dojo/_base/Deferred",
+ "dojo/_base/connect",
+ "dojo/ready",
+ "dojo/_base/window",
+ "dojo/dom-construct",
+ "./scene"],
+ function(dojo, lang, declare, deferred, connect, ready, baseWindow, dom, sceneCtor){
+
+ dojo.experimental("dojox.app");
+ var Application = declare([sceneCtor], {
+ constructor: function(params){
+ this.scenes={};
+ if(params.stores){
+ //create stores in the configuration.
+ for (var item in params.stores){
+ if(item.charAt(0)!=="_"){//skip the private properties
+ var type = params.stores[item].type? params.stores[item].type : "dojo.store.Memory";
+ var config = {};
+ if(params.stores[item].params){
+ dojo.mixin(config, params.stores[item].params);
+ }
+ var storeCtor = dojo.getObject(type);
+ if(config.data && lang.isString(config.data)){
+ //get the object specified by string value of data property
+ //cannot assign object literal or reference to data property
+ //because json.ref will generate __parent to point to its parent
+ //and will cause infinitive loop when creating StatefulModel.
+ config.data = dojo.getObject(config.data);
+ }
+ params.stores[item].store = new storeCtor(config);
+ }
+ }
+ }
+
+ },
+
+ // load default view and startup the default view
+ start: function(applicaton){
+ var child = this.loadChild();
+
+ deferred.when(child, dojo.hitch(this, function(){
+ this.startup();
+
+ //set application status to STARTED
+ this.setStatus(this.lifecycle.STARTED);
+ }));
+ },
+ templateString: "<div></div>",
+ selectedChild: null,
+ baseClass: "application mblView",
+ defaultViewType: sceneCtor,
+ buildRendering: function(){
+ if (this.srcNodeRef===baseWindow.body()){
+ this.srcNodeRef = dom.create("DIV",{},baseWindow.body());
+ }
+ this.inherited(arguments);
+ }
+ });
+
+ function generateApp(config,node,appSchema,validate){
+
+ //console.log("config.modules: ", config.modules);
+ var modules = config.modules.concat(config.dependencies);
+
+ if (config.template){
+ //console.log("config.template: ", config.template);
+ modules.push("dojo/text!" + "app/" + config.template);
+ }
+ //console.log("modules: ", modules);
+
+ require(modules, function(){
+ var modules=[Application];
+ for(var i=0;i<config.modules.length;i++){
+ modules.push(arguments[i]);
+ }
+
+ if (config.template){
+ var ext = {
+ templateString: arguments[arguments.length-1]
+ }
+ }
+ App = declare(modules,ext);
+
+ ready(function(){
+ app = App(config,node || baseWindow.body());
+ app.setStatus(app.lifecycle.STARTING);
+ app.start();
+ });
+ });
+ }
+
+
+ return function(config,node){
+ if (!config){
+ throw Error("App Config Missing");
+ }
+
+
+ if (config.validate){
+ require(["dojox/json/schema","dojox/json/ref","dojo/text!dojox/application/schema/application.json"],function(schema,appSchema){
+ schema = dojox.json.ref.resolveJson(schema);
+ if (schema.validate(config,appSchema)){
+ generateApp(config,node);
+ }
+ });
+
+
+ }else{
+ generateApp(config,node);
+ }
+ }
+});
diff --git a/js/dojo/dojox/app/model.js b/js/dojo/dojox/app/model.js
new file mode 100644
index 0000000..e74e7a7
--- /dev/null
+++ b/js/dojo/dojox/app/model.js
@@ -0,0 +1,27 @@
+//>>built
+define("dojox/app/model", ["dojo/_base/kernel","dojo/_base/Deferred","dojox/mvc/StatefulModel"], function(dojo,deferred){
+ return function(config, parent){
+ //load models here. create dojox.newStatefulModel
+ //using the configuration data for models
+ var loadedModels = {};
+ if(parent){
+ dojo.mixin(loadedModels, parent);
+ }
+ if(config){
+ for(var item in config){
+ if(item.charAt(0)!=="_"){
+ var params = config[item].params ? config[item].params:{};
+ var options = {
+ "store": params.store.store,
+ "query": params.store.query ? params.store.query : {}
+ };
+
+ //TODO improve performance of loading at here
+ // do not wait for the models to be created.
+ loadedModels[item] = deferred.when(dojox.mvc.newStatefulModel(options), function(model){return model});
+ }
+ }
+ }
+ return loadedModels;
+ }
+});
diff --git a/js/dojo/dojox/app/module/env.js b/js/dojo/dojox/app/module/env.js
new file mode 100644
index 0000000..f4e8601
--- /dev/null
+++ b/js/dojo/dojox/app/module/env.js
@@ -0,0 +1,22 @@
+//>>built
+define("dojox/app/module/env", ["dojo/_base/declare"], function(declare){
+ return declare(null, {
+ mode: "",
+ init: function(){
+
+ //TODO BROADLY categorize the mode of the app...mobile,desktop
+ // This should be done with UA sniffing, but remember
+ // very broadly, this is for purposes of deciding
+ // which ui to render, NOT feature detection
+ /*
+ this.mode="mobile";
+ var def = this.inherited(arguments);
+
+ //just an example
+ return def.then(function(){
+ console.log("env init after inherited inits");
+ });
+ */
+ }
+ });
+});
diff --git a/js/dojo/dojox/app/module/history.js b/js/dojo/dojox/app/module/history.js
new file mode 100644
index 0000000..56e6851
--- /dev/null
+++ b/js/dojo/dojox/app/module/history.js
@@ -0,0 +1,91 @@
+//>>built
+define("dojox/app/module/history", ["dojo/_base/kernel","dojo/_base/lang", "dojo/_base/declare", "dojo/on"],function(dojo,dlang,declare,listen){
+ return declare(null, {
+ postCreate: function(params,node){
+ this.inherited(arguments);
+ var hash=window.location.hash;
+ this._startView= ((hash && hash.charAt(0)=="#")?hash.substr(1):hash)||this.defaultView;
+
+ listen(this.domNode, "startTransition", dojo.hitch(this, "onStartTransition"));
+ listen(window,"popstate", dojo.hitch(this, "onPopState"));
+ },
+ startup: function(){
+ this.inherited(arguments);
+ },
+
+ onStartTransition: function(evt){
+ console.log("onStartTransition", evt.detail.href, history.state);
+ if (evt.preventDefault){
+ evt.preventDefault();
+ }
+
+ var target = evt.detail.target;
+ var regex = /#(.+)/;
+ if(!target && regex.test(evt.detail.href)){
+ target = evt.detail.href.match(regex)[1];
+ }
+
+ //prevent event from bubbling to window and being
+ //processed by dojox/mobile/ViewController
+ evt.cancelBubble = true;
+ if(evt.stopPropagation){
+ evt.stopPropagation();
+ }
+
+ dojo.when(this.transition(target, dojo.mixin({reverse: false},evt.detail)), dojo.hitch(this, function(){
+ history.pushState(evt.detail,evt.detail.href, evt.detail.url);
+ }))
+
+ },
+
+ /*
+ onHashChange: function(evt){
+ var target = window.location.hash.substr(1);;
+ var evt = {target: window.location.hash, url: "#" + target,title:null};
+ //this.onStartTransition(evt);
+ },
+ */
+
+ onPopState: function(evt){
+ // Check application status, if application status not STARTED, do nothing.
+ // when clean browser's cache then refresh the current page, it will trigger popState event.
+ // but the application not start, it will throw an error.
+ if(this.getStatus() !== this.lifecycle.STARTED ){
+ return;
+ }
+ var state = evt.state;
+ if (!state){
+
+ if(!this._startView && window.location.hash){
+ state={
+ target: (location.hash && location.hash.charAt(0)=="#")?location.hash.substr(1):location.hash,
+ url: location.hash
+ }
+ }else{
+ state={};
+ }
+ }
+
+ var target = state.target || this._startView || this.defaultView;
+
+ if (this._startView){
+ this._startView=null;
+ }
+ var title = state.title||null;
+ var href = state.url || null;
+
+ if (evt._sim) {
+ history.replaceState(state, title, href );
+ }
+
+ /*
+ dojo.when(this.transition(window.history.state, {rev: true}), dojo.hitch(this, function(){
+
+ console.log('done transition from onPopState');
+ }))
+ */
+ var currentState = history.state;
+ this.transition(target, dojo.mixin({reverse: true},state));
+ }
+ });
+});
diff --git a/js/dojo/dojox/app/module/lifecycle.js b/js/dojo/dojox/app/module/lifecycle.js
new file mode 100644
index 0000000..d444463
--- /dev/null
+++ b/js/dojo/dojox/app/module/lifecycle.js
@@ -0,0 +1,27 @@
+//>>built
+define("dojox/app/module/lifecycle", ["dojo/_base/declare", "dojo/_base/connect"], function(declare, connect){
+ return declare(null, {
+
+ lifecycle: {
+ UNKNOWN: 0, //unknown
+ STARTING: 1, //starting
+ STARTED: 2, //started
+ STOPPING: 3, //stopping
+ STOPPED: 4 //stopped
+ },
+
+ _status: 0, //unknown
+
+ getStatus: function(){
+ return this._status;
+ },
+
+ setStatus: function(newStatus){
+ this._status = newStatus;
+
+ // publish /app/stauts event.
+ // application can subscribe this event to do some status change operation.
+ connect.publish("/app/status", [newStatus]);
+ }
+ });
+});
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(){}
+ });
+});
diff --git a/js/dojo/dojox/app/schema/README b/js/dojo/dojox/app/schema/README
new file mode 100644
index 0000000..7e514bb
--- /dev/null
+++ b/js/dojo/dojox/app/schema/README
@@ -0,0 +1 @@
+Schemas to validate application configs, not fully in sync with what is being accepted just yet
diff --git a/js/dojo/dojox/app/schema/application.json b/js/dojo/dojox/app/schema/application.json
new file mode 100644
index 0000000..7f8fdd4
--- /dev/null
+++ b/js/dojo/dojox/app/schema/application.json
@@ -0,0 +1,55 @@
+define({
+ "description":"A representation of an Application",
+ "type": "object",
+ "properties":{
+ "author": {"$ref": "http://json-schema.org/card"},
+
+ "description": {
+ "description": "Description of the application represented here",
+ "type": "string"
+ },
+
+ "modules": {
+ "type": "array",
+ "description": "Modules this application requires ",
+ "default": [],
+ "items":{
+ "type": "string",
+ "description": "Module to be loaded and mixed into the application"
+ }
+ },
+
+
+ "defaultScene": {
+ "type": "string"
+ "description": "id of scene to load for this application at startup"
+ },
+
+ "scenes": {
+ "type": "object",
+ "description": "This object contains references to scene objects, which are collections of views and models making up a closely related set of the ui",
+ "additionalProperties":{"$ref":"/jdoe/test/schema/scene.json"}
+ },
+
+ "stores": {
+ "type": "object",
+ "description":"This object contains references to store instances that the rest of the application will use.",
+ "additionalProperties": {"$ref":"/jdoe/test/schema/scene.json"},
+ "default": {}
+ },
+
+ "models": {
+ "type": "object",
+ "description": "This object contains references to model instances the application uses",
+ "additionalProperties":{"$ref":"/jdoe/test/schema/model.json"},
+ "default": {}
+ },
+
+ "views": {
+ "type": "object",
+ "description": "This object contains references to view instances the application uses",
+ "additionalProperties":{"$ref":"/jdoe/test/schema/view.json"},
+ "default": {}
+ }
+ }
+});
diff --git a/js/dojo/dojox/app/schema/model.json b/js/dojo/dojox/app/schema/model.json
new file mode 100644
index 0000000..a630516
--- /dev/null
+++ b/js/dojo/dojox/app/schema/model.json
@@ -0,0 +1,11 @@
+{
+ "description":"An applications model declaration",
+ "type": "object",
+ "properties":{
+ "type": {
+ "type": "string",
+ "description": "Model Instance Type (Class)"
+ },
+ },
+ "additionalProperties": true
+}
diff --git a/js/dojo/dojox/app/schema/scene.json b/js/dojo/dojox/app/schema/scene.json
new file mode 100644
index 0000000..412ce90
--- /dev/null
+++ b/js/dojo/dojox/app/schema/scene.json
@@ -0,0 +1,29 @@
+{
+ "description":"An application's scene instance declarations",
+ "type": "object",
+ "properties":{
+ "type": {
+ "type": "string",
+ "description": "Scene Instance Type (Class)"
+ },
+ "models": {
+ "type": "array",
+ "items": {"$ref": "/jdoe/test/schema/model"}
+ },
+ "views": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties" : {
+ "id": {
+ "type": "string"
+ },
+ "view": "$ref": "/jdoe/test/schema/view",
+ "params": {
+ "type": "object"
+ }
+ }
+ }
+ },
+ "additionalProperties": true
+}
diff --git a/js/dojo/dojox/app/schema/store.json b/js/dojo/dojox/app/schema/store.json
new file mode 100644
index 0000000..924f5e1
--- /dev/null
+++ b/js/dojo/dojox/app/schema/store.json
@@ -0,0 +1,11 @@
+{
+ "description":"An applications store instance declarations",
+ "type": "object",
+ "properties":{
+ "type": {
+ "type": "string",
+ "description": "Store Instance Type (Class)"
+ },
+ },
+ "additionalProperties": true
+}
diff --git a/js/dojo/dojox/app/schema/view.json b/js/dojo/dojox/app/schema/view.json
new file mode 100644
index 0000000..b6c1c54
--- /dev/null
+++ b/js/dojo/dojox/app/schema/view.json
@@ -0,0 +1,30 @@
+{
+ "description": "Base View Schema for defining View Instances in an application",
+ "type": "object",
+ "properties": {
+ "type": {
+ "type":"string",
+ "description": "The Name of the Class to be used for this view"
+ },
+
+ "models": {
+ "type": "array",
+ "items": {
+ "type":"string",
+ "description": "Models that this view requires. These should reference one of the available #models",
+ "dependences":{}
+ }
+ },
+
+ "persist": {
+ "type": "boolean",
+ "description":"Keep this view loaded on the dom or memory, but not necessarily visible"
+ },
+
+ "template": {
+ "type": "string",
+ "description": "Template to be used with this view"
+ }
+ }
+}
+
diff --git a/js/dojo/dojox/app/transition.js b/js/dojo/dojox/app/transition.js
new file mode 100644
index 0000000..17a6bcd
--- /dev/null
+++ b/js/dojo/dojox/app/transition.js
@@ -0,0 +1,61 @@
+//>>built
+define("dojox/app/transition", ["dojo/_base/kernel", "dojo/_base/array","dojo/_base/html","dojo/DeferredList","./animation"],
+ function(dojo, darray, dhtml, DeferredList,animation){
+ return function(from, to, options){
+ var rev = (options && options.reverse) ? -1 : 1;
+ if(!options || !options.transition || !animation[options.transition]){
+ dojo.style(from,"display","none");
+ dojo.style(to, "display", "");
+ if(options.transitionDefs){
+ if(options.transitionDefs[from.id]){
+ options.transitionDefs[from.id].resolve(from);
+ }
+ if(options.transitionDefs[to.id]){
+ options.transitionDefs[to.id].resolve(to);
+ }
+ }
+ }else{
+ var defs=[];
+ var transit=[];
+ var duration = 250;
+ if(options.transition === "fade"){
+ duration = 600;
+ }else if (options.transition === "flip"){
+ duration = 200;
+ }
+ dojo.style(from, "display", "");
+ dojo.style(to, "display", "");
+ if (from){
+ //create animation to transit "from" out
+ var fromTransit = animation[options.transition](from, {
+ "in": false,
+ direction: rev,
+ duration: duration,
+ deferred: (options.transitionDefs && options.transitionDefs[from.id]) ? options.transitionDefs[from.id] : null
+ });
+ defs.push(fromTransit.deferred);//every animation object should have a deferred.
+ transit.push(fromTransit);
+ }
+
+ //create animation to transit "to" in
+ var toTransit = animation[options.transition](to, {
+ direction: rev,
+ duration: duration,
+ deferred: (options.transitionDefs && options.transitionDefs[to.id]) ? options.transitionDefs[to.id] : null
+ });
+ defs.push(toTransit.deferred);//every animation object should have a deferred.
+ transit.push(toTransit);
+
+ //TODO If it is flip use the chainedPlay
+ //play fromTransit and toTransit together
+ if(options.transition === "flip"){
+ animation.chainedPlay(transit);
+ }else{
+ animation.groupedPlay(transit);
+ }
+
+ return new dojo.DeferredList(defs);
+
+ }
+ };
+});
diff --git a/js/dojo/dojox/app/view.js b/js/dojo/dojox/app/view.js
new file mode 100644
index 0000000..b3acfbb
--- /dev/null
+++ b/js/dojo/dojox/app/view.js
@@ -0,0 +1,16 @@
+//>>built
+define("dojox/app/view", ["dojo/_base/declare", "dijit/_WidgetBase", "dijit/_Container", "dijit/_Contained","dijit/_TemplatedMixin","dijit/_WidgetsInTemplateMixin"],function(declare,Widget,Container,Contained,TemplatedMixin,WidgetsInTemplateMixin){
+ return declare("dojox.app.view", [Widget,TemplatedMixin,Container,Contained, WidgetsInTemplateMixin], {
+ selected: false,
+ keepScrollPosition: true,
+ baseClass: "applicationView mblView",
+ config:null,
+ widgetsInTemplate: true,
+ templateString: '<div></div>',
+ toString: function(){return this.id},
+ activate:function(){},
+ deactivate: function(){},
+ //Temporary work around for getting a null when calling getParent
+ getParent: function(){return null;}
+ });
+});