Index: Dev/branches/rest-dojo-ui/client/index.html
===================================================================
--- Dev/branches/rest-dojo-ui/client/index.html	(revision 260)
+++ Dev/branches/rest-dojo-ui/client/index.html	(revision 263)
@@ -18,4 +18,7 @@
                 <span data-dojo-type="rft.ui.MenuBarLink" data-dojo-props="pageId:'surveys'">Surveys</span>
                 <span data-dojo-type="rft.ui.MenuBarLink" data-dojo-props="pageId:'results'">Results</span>
+                <span data-dojo-type="rft.ui.MenuBarLink" data-dojo-props="pageId:'test1'">Test 1</span>
+                <span data-dojo-type="rft.ui.MenuBarLink" data-dojo-props="pageId:'test2'">Test 2</span>
+                <span data-dojo-type="rft.ui.MenuBarLink" data-dojo-props="pageId:'test3'">Test 3</span>
             </div>
             <div id="userinfo" class="mainUserInfo">Login</div>
Index: Dev/branches/rest-dojo-ui/client/pages/index.html
===================================================================
--- Dev/branches/rest-dojo-ui/client/pages/index.html	(revision 263)
+++ Dev/branches/rest-dojo-ui/client/pages/index.html	(revision 263)
@@ -0,0 +1,3 @@
+<div>
+    <h1>Index pagina</h1>
+</div>
Index: Dev/branches/rest-dojo-ui/client/pages/session.html
===================================================================
--- Dev/branches/rest-dojo-ui/client/pages/session.html	(revision 260)
+++ Dev/branches/rest-dojo-ui/client/pages/session.html	(revision 263)
@@ -1,5 +1,6 @@
-<div data-dojo-type="rft.ui.Page">
-    <script type="dojo/method" data-dojo-event="init">
+<div>
+    <script type="dojo/method" data-dojo-event="onVisit">
         var that = this;
+        debugger;
         require(['dojo/_base/json','dojo/dom','dojo/store/JsonRest','dojo/domReady!'],
             function(json,dom,JsonRest){
Index: Dev/branches/rest-dojo-ui/client/pages/sessions.html
===================================================================
--- Dev/branches/rest-dojo-ui/client/pages/sessions.html	(revision 260)
+++ Dev/branches/rest-dojo-ui/client/pages/sessions.html	(revision 263)
@@ -1,21 +1,36 @@
-<div data-dojo-type="rft.ui.Page">
-    <script type="dojo/method" data-dojo-event="init">
+<div>
+    <script type="rft/method" data-rft-method="onVisit">
         var that = this;
-        require(['dojo/store/JsonRest','dojox/grid/DataGrid',
-            Â Â Â Â 'dojo/data/ObjectStore','dojo/domReady!'],
-        function(JsonRest,DataGrid,ObjectStore){
+        require(['dojo/_base/lang','dojo/_base/event','dojo/store/JsonRest','dojox/grid/DataGrid',
+        Â Â Â Â 'dojo/data/ObjectStore','rft/content','dojo/domReady!'],
+        function(lang,event,JsonRest,DataGrid,ObjectStore,content){
             var store = new JsonRest({
                 target:"../server/api.php/data/Session/",
                 idProperty: 'uid'
             });
-        Â Â Â Â var grid = new DataGrid({
-        Â Â Â Â Â Â Â Â store: ObjectStore({objectStore: store}),
-                autoWidth: true,
-                autoHeight: true,
-        Â Â Â Â Â Â Â Â structure: [
-            Â Â Â Â Â Â Â Â Â Â Â Â {name:"Title", field:"title", width: "200px"}
-        Â Â Â Â Â Â Â Â ]
-        Â Â Â Â }, that.page_sessions_grid);
-        Â Â Â Â grid.startup();
+
+            that.grid.setStore(ObjectStore({objectStore: store}));
+        
+            that.grid.on('rowclick',lang.hitch(that,function(evt){
+                var obj = evt.grid.getItem(evt.rowIndex);
+                this.form.set('value', obj);
+            }));
+
+            that.grid.on('rowdblclick',lang.hitch(that,function(evt){
+                var obj = evt.grid.getItem(evt.rowIndex);
+                content.goTo('/session',{uid:obj.uid});
+            }));
+
+            var submitHandler = lang.hitch(that,function(evt){
+                var newObj = this.form.get('value');
+                debugger;
+                // mixin newObj in obj.
+                // store.put(newObj);
+                event.stop(evt);
+                return false;
+            });
+            that.btnSave.on('click',submitHandler);
+            that.form.on('submit',submitHandler);
+        
         });
     </script>
@@ -23,5 +38,19 @@
         <div class="largeTitle">Sessions</div>
         <div class="content">
-            <div data-rft-attach-point="page_sessions_grid"></div>
+            <div data-dojo-type="dojox.grid.DataGrid" data-dojo-props="autoWidth:true,autoHeight:true,structure:[{name:'Title',field:'title'}]" data-dojo-attach-point="grid"></div>
+        </div>
+
+    </div>
+
+    <div class="largeFrame" style="width: 400px;">
+        <div class="largeTitle">Details</div>
+        <div>
+            <form data-dojo-type="dijit.form.Form" data-dojo-attach-point="form">
+                <fieldset>
+                    <label for="title" class="loginLabel">Title</label>
+                    <input data-dojo-type="dijit.form.TextBox" name="title" type="text" class="loginInput" />
+                </fieldset>
+                <button type="submit" data-dojo-type="dijit.form.Button" data-dojo-attach-point="btnSave">Save</button>
+            </form>
         </div>
     </div>
Index: Dev/branches/rest-dojo-ui/client/pages/test1.html
===================================================================
--- Dev/branches/rest-dojo-ui/client/pages/test1.html	(revision 263)
+++ Dev/branches/rest-dojo-ui/client/pages/test1.html	(revision 263)
@@ -0,0 +1,18 @@
+<div data-rft-mixin="rft/ui/TestPageController">
+    <h1>External _PageController test</h1>
+    <div data-dojo-type="dijit.layout.TabContainer" data-dojo-props="doLayout: false">
+        <div data-dojo-type="dijit.layout.ContentPane" title="Sessions">
+            <div data-dojo-type="dojox.grid.DataGrid" data-dojo-props="autoWidth:true,autoHeight:true,structure:[{name:'Title',field:'title'}]" data-dojo-attach-point="grid"></div>
+        </div>
+        <div data-dojo-type="dijit.layout.ContentPane" title="Details">
+            <form data-dojo-type="dijit.form.Form" data-dojo-attach-point="form">
+                <fieldset>
+                    <label for="title" class="loginLabel">Title</label>
+                    <input data-dojo-type="dijit.form.TextBox" name="title" type="text" class="loginInput" />
+                </fieldset>
+                <button type="submit" data-dojo-type="dijit.form.Button" data-dojo-attach-point="btnSave">Save</button>
+            </form>
+            <div data-dojo-attach-point="errors"></div>
+        </div>
+    </div>
+</div>
Index: Dev/branches/rest-dojo-ui/client/pages/test2.html
===================================================================
--- Dev/branches/rest-dojo-ui/client/pages/test2.html	(revision 263)
+++ Dev/branches/rest-dojo-ui/client/pages/test2.html	(revision 263)
@@ -0,0 +1,18 @@
+<div>
+    <h1>No rft.ui._Page features used</h1>
+    <div data-dojo-type="dijit.layout.TabContainer" data-dojo-props="doLayout: false" style="border: 1px solid black" id="tabber">
+        <div data-dojo-type="dijit.layout.ContentPane" title="Sessions">
+            <div data-dojo-type="dojox.grid.DataGrid" data-dojo-props="store:dojo.data.ObjectStore({objectStore: new dojo.store.JsonRest({target:'../server/api.php/data/Session/',idProperty: 'uid'})}),autoWidth:true,autoHeight:true,structure:[{name:'Title',field:'title'}]"></div>
+        </div>
+        <div data-dojo-type="dijit.layout.ContentPane" title="Details">
+            <form data-dojo-type="dijit.form.Form">
+                <fieldset>
+                    <label for="title" class="loginLabel">Title</label>
+                    <input data-dojo-type="dijit.form.TextBox" name="title" type="text" class="loginInput" />
+                </fieldset>
+                <button type="submit" data-dojo-type="dijit.form.Button">Save</button>
+            </form>
+            <div></div>
+        </div>
+    </div>
+</div>
Index: Dev/branches/rest-dojo-ui/client/pages/test3.html
===================================================================
--- Dev/branches/rest-dojo-ui/client/pages/test3.html	(revision 263)
+++ Dev/branches/rest-dojo-ui/client/pages/test3.html	(revision 263)
@@ -0,0 +1,92 @@
+<div>
+    <script type="rft/method" data-rft-method="onVisit">
+        var that = this;
+        require(['dojo/_base/declare','dojo/_base/lang','dojo/_base/event','rft/content',
+            'dojo/store/JsonRest','dojo/data/ObjectStore','rft/ui/_PageController','dijit/registry'],
+        function(declare,lang,event,content,JsonRest,ObjectStore,_PageController,registry){
+            (lang.hitch(that,function(){
+                this._store = new JsonRest({
+                    target:"../server/api.php/data/Session/",
+                    idProperty: 'uid'
+                });
+
+                function setEnabledRecursive(node,value) {
+                    registry.findWidgets(node).forEach(function(w){
+                        w.set('disabled',!value);
+                        setEnabledRecursive(w.domNode,value);
+                    });
+                };
+
+                setEnabledRecursive(this.form.domNode,false);
+                
+                var refreshGrid = lang.hitch(this,function(){
+                    this.grid.setStore(ObjectStore({
+                        objectStore: this._store
+                    }),'');
+                });
+                refreshGrid();
+                
+                var getSelectedItem = lang.hitch(this,function(evt){
+                    var items = this.grid.selection.getSelected();
+                    if (items.length)
+                        return items[0];
+                    return null;
+                });
+                
+                this.grid.on('rowclick',lang.hitch(this,function(evt){
+                    var obj = getSelectedItem();
+                    if ( obj) {
+                        this.form.set('value', obj);
+                        setEnabledRecursive(this.form.domNode, true);
+                    } else {
+                        setEnabledRecursive(this.form.domNode, false);
+                        this.form.reset();
+                    }
+                }));
+
+                this.grid.on('rowdblclick',lang.hitch(this,function(evt){
+                    var obj = getSelectedItem();
+                    content.goTo('/session',{
+                        uid:obj.uid
+                    });
+                }));
+
+                var submitHandler = lang.hitch(this,function(evt){
+                    this.errors.innerHTML = '';
+                    var newObj = this.form.get('value');
+                    var obj = getSelectedItem();
+                    obj = lang.mixin(obj,newObj);
+                    //this._store.put(newObj,{id:obj.uid})
+                    this._store.put(obj,{
+                        overwrite:true
+                    })
+                    .then(lang.hitch(this,function(){
+                        refreshGrid();
+                    }),lang.hitch(this,function(e){
+                        this.errors.innerHTML = e.toString();
+                    }));
+                    event.stop(evt);
+                    return false;
+                });
+                
+                this.form.on('submit',submitHandler);
+            }))();
+        });
+    </script>
+    <h1>Embedded scripts</h1>
+    <div data-dojo-type="dijit.layout.TabContainer" data-dojo-props="doLayout: false">
+        <div data-dojo-type="dijit.layout.ContentPane" title="Sessions">
+            <div data-dojo-type="dojox.grid.DataGrid" data-dojo-props="autoWidth:true,autoHeight:true,structure:[{name:'Title',field:'title'}]" data-dojo-attach-point="grid"></div>
+        </div>
+        <div data-dojo-type="dijit.layout.ContentPane" title="Details">
+            <form data-dojo-type="dijit.form.Form" data-dojo-attach-point="form">
+                <fieldset>
+                    <label for="title" class="loginLabel">Title</label>
+                    <input data-dojo-type="dijit.form.TextBox" name="title" type="text" class="loginInput" />
+                </fieldset>
+                <button type="submit" data-dojo-type="dijit.form.Button" data-dojo-attach-point="btnSave">Save</button>
+            </form>
+            <div data-dojo-attach-point="errors"></div>
+        </div>
+    </div>
+</div>
Index: Dev/branches/rest-dojo-ui/client/rft/content.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/content.js	(revision 260)
+++ Dev/branches/rest-dojo-ui/client/rft/content.js	(revision 263)
@@ -1,13 +1,14 @@
-define(['dojo/_base/declare','dojo/_base/connect','dojo/_base/xhr','dojo/dom-construct',
-    'dojo/dom-attr','dojo/_base/lang','dojo/_base/Deferred','dojo/hash',
-    'dojo/io-query','dojo/_base/json','dijit','rft/util'],
-    function(declare,connect,xhr,domConstruct,attr,lang,Deferred,hash,uriQuery,json,dijit,util){
+define(['dojo/_base/declare','dojo/_base/connect','dojo/_base/xhr',
+    'dojo/_base/lang','dojo/_base/Deferred','dojo/hash',
+    'dojo/io-query','dijit','rft/util','rft/ui/_Page'],
+    function(declare,connect,xhr,lang,Deferred,hash,uriQuery,dijit,util,_Page){
         return new (function() {
             var self = this;
-            var current = "";
+            var currentHash = "";
+            var currentPage = null;
             
             var HRI = declare(null,{
                 constructor: function() {
-                    this._path = '/';
+                    this._path = this._fixPath('/');
                     this._args = {};
                     if ( arguments.length == 1 ) {
@@ -54,35 +55,54 @@
             function _goTo(hri,replace) {
                 var contentPane = dijit.byId('content');
-                var d = new Deferred();
+                var dfd = new Deferred();
                 var newHash = hri.hash();
-                if ( current == newHash ) {
-                    d.resolve();
-                    return d.promise;
+                
+                // if already there, return
+                if ( currentHash === newHash ) {
+                    dfd.resolve();
+                    return dfd.promise;
                 }
-                current = newHash;
+                
+                // check if we can leave current page
+                if ( currentPage ) {
+                    if ( currentPage.onLeave() === false ) {
+                        dfd.reject();
+                        return dfd.promise;
+                    }
+                }
+                
+                // update hash
+                currentHash = newHash;
                 hash(newHash,replace);
+                
+                // load html
+                var pageUrl = 'pages'+hri.path()+'.html';
                 xhr.get({
-                    url: 'pages'+hri.path()+'.html',
+                    url: pageUrl,
                     failOk: true
                 })
+                
+                // initialize page or create error message
                 .then(function(html){
-                    var root = domConstruct.toDom(html)
-                    var props = json.toJson(hri.args());
-                    props = props.slice(1,props.length-2);
-                    if ( props )
-                        attr.set(root,'data-rft-props',props);
-                    contentPane.set('content',root);
-                    d.resolve();
+                    currentPage = new _Page({
+                        templateString: html,
+                        pageQuery: hri.args()
+                    });
+                    contentPane.set('content',currentPage);
+                    dfd.resolve();
                 },function(){
-                    contentPane.set('content',"Page "+hri.path()+" not found.");
-                    d.reject();
+                    currentPage = new _Page({
+                        templateString: "<div>Page "+hri.path()+" not found.</div>"
+                    });
+                    contentPane.set('content',currentPage);
+                    dfd.reject();
                 });
-                return d.promise;
+                return dfd.promise;
             }
 
             self.initial = function(path,args) {
-                if ( current ) {
+                if ( currentHash ) {
                     var dfd = new Deferred();
-                    dfd.resolved();
+                    dfd.resolve();
                     return dfd.promise;
                 }
Index: Dev/branches/rest-dojo-ui/client/rft/run.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/run.js	(revision 260)
+++ Dev/branches/rest-dojo-ui/client/rft/run.js	(revision 263)
@@ -1,14 +1,25 @@
+require([
+    'dijit/form/Form',
+    'dojo/store/JsonRest',
+    'dojo/data/ObjectStore',
+    'dojox/grid/DataGrid',
+    'dijit/layout/ContentPane',
+    'dijit/layout/TabContainer',
+    'dijit/MenuBar',
+    'dijit/MenuBarItem',
+    'rft/ui/MenuBarLink',
+]);
+
 require(['dojo/_base/window','dojo/parser', 'rft/ui/LoginDialog', 
     'dojo/dom', 'dojo/_base/connect', 'rft/auth', 'rft/content',
-    'dojo/domReady!', 'dijit/layout/ContentPane', 'dijit/MenuBar',
-    'dijit/MenuBarItem', 'rft/ui/MenuBarLink', 'rft/ui/Page'],
+    'dojo/domReady!', ],
     function(win,parser,LoginDialog,dom,connect,auth,content) {
         parser.parse();
 
-        var login = new LoginDialog().placeAt(document.body);
+        var login = new LoginDialog().placeAt(win.body());
         login.startup();
 
         auth.restore()
-        .then(function(user){
+        .then(function(){
             content.initial();
         },function(){
Index: Dev/branches/rest-dojo-ui/client/rft/ui/LoginDialog.html
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/ui/LoginDialog.html	(revision 260)
+++ Dev/branches/rest-dojo-ui/client/rft/ui/LoginDialog.html	(revision 263)
@@ -14,7 +14,7 @@
                 <input data-dojo-type="dijit.form.TextBox" name="password" type="password" class="loginInput" />
             </fieldset>
+            <button type="submit" data-dojo-type="dijit.form.Button" data-dojo-attach-event="onClick:onLogin">Login</button>
+            <button type="button" data-dojo-type="dijit.form.Button" data-dojo-attach-event="onClick:onCancel">Cancel</button>
         </form>
-        <button type="button" data-dojo-type="dijit.form.Button" data-dojo-attach-event="onClick:onLogin">Login</button>
-        <button type="button" data-dojo-type="dijit.form.Button" data-dojo-attach-event="onClick:onCancel">Cancel</button>
         <div data-dojo-attach-point="loginErrors"></div>
     </div>
Index: Dev/branches/rest-dojo-ui/client/rft/ui/LoginDialog.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/ui/LoginDialog.js	(revision 260)
+++ Dev/branches/rest-dojo-ui/client/rft/ui/LoginDialog.js	(revision 263)
@@ -1,6 +1,6 @@
-define(['dojo/_base/declare','dojo/_base/lang','dijit/Dialog','dijit/_WidgetsInTemplateMixin',
-    '../auth', 'dojo/text!./LoginDialog.html','../content',
-    'dijit/form/Form','dijit/form/Button','dijit/form/TextBox'],
-    function (declare, lang, Dialog, _WidgetsInTemplateMixin, auth, template, content) {
+define(['dojo/_base/declare','dojo/_base/lang','dojo/_base/event','dijit/Dialog',
+    'dijit/_WidgetsInTemplateMixin','../auth', 'dojo/text!./LoginDialog.html',
+    '../content','dijit/form/Form','dijit/form/Button','dijit/form/TextBox'],
+    function (declare, lang, event, Dialog, _WidgetsInTemplateMixin, auth, template, content) {
         return declare([Dialog,_WidgetsInTemplateMixin], {
             templateString: template,
@@ -8,4 +8,8 @@
             title: "Login",
             baseClass: "rftLoginDialog",
+            startup: function() {
+                this.inherited(arguments);
+                this.loginForm.on('submit',lang.hitch(this,'onLogin'));
+            },
             show: function() {
                 this.loginErrors.innerHTML = '';
@@ -13,15 +17,15 @@
                 this.inherited(arguments);
             },
-            onLogin: function() {
+            onLogin: function(evt) {
                 this.loginErrors.innerHTML = '';
                 var data = this.loginForm.get('value');
-                var hide = lang.hitch(this,"hide");
                 auth.login(data.username,data.password)
-                .then(function() {
-                    hide();
+                .then(lang.hitch(this,function() {
+                    this.hide();
                     content.initial();
-                },function() {
+                }),lang.hitch(this,function() {
                     this.loginErrors.innerHTML = 'Login failed.';
-                })
+                }));
+                event.stop(evt);
                 return false;
             }
Index: Dev/branches/rest-dojo-ui/client/rft/ui/Page.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/ui/Page.js	(revision 260)
+++ 	(revision )
@@ -1,23 +1,0 @@
-define(['dojo/_base/declare','dojo/query','dojo/_base/json','dojo/dom-attr','dojo/_base/lang','dijit/registry','dijit/_WidgetBase','dijit/_Container'],
-    function(declare,query,json,attr,lang,registry,_WidgetBase,_Container){
-        return declare('rft.ui.Page',[_WidgetBase,_Container],{
-            postCreate: function() {
-                this.inherited(arguments);
-                this.args = {};
-                var props = attr.get(this.domNode,'data-rft-props');
-                if ( props ) {
-                    props = json.fromJson.call(this,'{'+props+'}');
-                    lang.mixin(this.args,props);
-                }
-            },
-            startup: function() {
-                this.inherited(arguments);
-                query('[data-rft-attach-point]').forEach(lang.hitch(this,function(node){
-                    var name = attr.get(node,'data-rft-attach-point');
-                    this[name] = registry.byNode(node) || node;
-                }));
-                this.init();
-            },
-            init: function(){}
-        });
-    });
Index: Dev/branches/rest-dojo-ui/client/rft/ui/TestPageController.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/ui/TestPageController.js	(revision 263)
+++ Dev/branches/rest-dojo-ui/client/rft/ui/TestPageController.js	(revision 263)
@@ -0,0 +1,73 @@
+define(['dojo/_base/declare','dojo/_base/lang','dojo/_base/event','rft/content',
+    'dojo/store/JsonRest','dojo/data/ObjectStore','rft/ui/_PageController','dijit/registry'],
+    function(declare,lang,event,content,JsonRest,ObjectStore,_PageController,registry) {
+        return declare([_PageController],{
+            onVisit: function() {
+                this._store = new JsonRest({
+                    target:"../server/api.php/data/Session/",
+                    idProperty: 'uid'
+                });
+
+                function setEnabledRecursive(node,value) {
+                    registry.findWidgets(node).forEach(function(w){
+                        w.set('disabled',!value);
+                        setEnabledRecursive(w.domNode,value);
+                    });
+                };
+
+                setEnabledRecursive(this.form.domNode,false);
+
+                var refreshGrid = lang.hitch(this,function(){
+                    this.grid.setStore(ObjectStore({
+                        objectStore: this._store
+                    }),'');
+                });
+                refreshGrid();
+                
+                var getSelectedItem = lang.hitch(this,function(evt){
+                    var items = this.grid.selection.getSelected();
+                    if (items.length)
+                        return items[0];
+                    return null;
+                });
+                
+                this.grid.on('rowclick',lang.hitch(this,function(evt){
+                    var obj = getSelectedItem();
+                    if ( obj) {
+                        this.form.set('value', obj);
+                        setEnabledRecursive(this.form.domNode, true);
+                    } else {
+                        setEnabledRecursive(this.form.domNode, false);
+                        this.form.reset();
+                    }
+                }));
+
+                this.grid.on('rowdblclick',lang.hitch(this,function(evt){
+                    var obj = getSelectedItem();
+                    content.goTo('/session',{
+                        uid:obj.uid
+                    });
+                }));
+
+                var submitHandler = lang.hitch(this,function(evt){
+                    this.errors.innerHTML = '';
+                    var newObj = this.form.get('value');
+                    var obj = getSelectedItem();
+                    obj = lang.mixin(obj,newObj);
+                    //this._store.put(newObj,{id:obj.uid})
+                    this._store.put(obj,{
+                        overwrite:true
+                    })
+                    .then(lang.hitch(this,function(){
+                        refreshGrid();
+                    }),lang.hitch(this,function(e){
+                        this.errors.innerHTML = e.toString();
+                    }));
+                    event.stop(evt);
+                    return false;
+                });
+                
+                this.form.on('submit',submitHandler);
+            }
+        });
+    });
Index: Dev/branches/rest-dojo-ui/client/rft/ui/_Page.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/ui/_Page.js	(revision 263)
+++ Dev/branches/rest-dojo-ui/client/rft/ui/_Page.js	(revision 263)
@@ -0,0 +1,44 @@
+define(['dojo/_base/declare','dojo/_base/lang','dojo/_base/array','dojo/parser','dojo/query','dojo/_base/Deferred',
+    'dijit/_WidgetBase','dijit/_TemplatedMixin','dijit/_WidgetsInTemplateMixin','rft/ui/_PageController'],
+    function(declare,lang,array,parser,query,Deferred,_WidgetBase,_TemplatedMixin,_WidgetsInTemplateMixin,_PageController){
+        return declare('rft.ui._Page',[_WidgetBase,_TemplatedMixin,_WidgetsInTemplateMixin,_PageController],{
+            _controllerLoaded: false,
+            _hasStarted: false,
+            _triggeredOnVisit: false,
+            pageController: null, /* class derived from rft.ui._PageController, mixed in at creation */
+            buildRendering: function() {
+                this.inherited(arguments);
+                var dfd = new Deferred();
+                var mixinType = this.domNode.getAttribute("data-rft-mixin");
+                if ( mixinType ) {
+                    require([mixinType],lang.hitch(this,function(SomePageController){
+                        declare.safeMixin(this,new SomePageController());
+                        dfd.resolve();
+                    }));
+                } else {
+                    dfd.resolve();
+                }
+                dfd.then(lang.hitch(this,function(){
+                    var methods = query('> script[type^="rft/method"]',this.domNode).orphan();
+                    array.forEach(methods,lang.hitch(this,function(s){
+                        var method = s.getAttribute("data-rft-method");
+                        var func = parser._functionFromScript(s);
+                        this[method] = func;
+                    }));
+                    this._controllerLoaded = true;
+                    this._triggerOnVisit();
+                }));
+            },
+            startup: function() {
+                this.inherited(arguments);
+                this._hasStarted = true;
+                this._triggerOnVisit();
+            },
+            _triggerOnVisit: function() {
+                if ( this._controllerLoaded && this._hasStarted && !this._triggeredOnVisit ) {
+                    this._triggeredOnVisit = false;
+                    this.onVisit();
+                }
+            }
+        });
+    });
Index: Dev/branches/rest-dojo-ui/client/rft/ui/_PageController.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/ui/_PageController.js	(revision 263)
+++ Dev/branches/rest-dojo-ui/client/rft/ui/_PageController.js	(revision 263)
@@ -0,0 +1,10 @@
+define(['dojo/_base/declare'],
+    function(declare) {
+        return declare('rft.ui._PageController',null,{
+            /** Event fired after startup of all widegts is complete */
+            onVisit: function(){},
+            /** Event fired before leaving the page. Return false to prevent */
+            onLeave: function(){}
+        });
+    });
+
