Changeset 8405

Show
Ignore:
Timestamp:
05/02/07 19:31:09 (21 months ago)
Author:
bill
Message:

Rough draft of tree <--> dojo.data connection.
This code is pretty small so maybe I'll roll it into Controller.js instead of
having a separate file.
Refs #2782.

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • dijit/trunk/_tree/DataController.js

    r8278 r8405  
     1dojo.provide("dijit._tree.DataController"); 
    12 
    2 dojo.provide("dojo.widget.TreeLoadingControllerV3"); 
     3dojo.require("dijit._tree.Controller"); 
    34 
    4 dojo.require("dojo.widget.TreeBasicControllerV3"); 
    5 dojo.require("dojo.event.*"); 
    6 dojo.require("dojo.json") 
    7 dojo.require("dojo.io.*"); 
    8 dojo.require("dojo.Deferred"); 
    9 dojo.require("dojo.DeferredList"); 
     5dijit.declare( 
     6        "dijit._tree.DataController", 
     7        dijit._tree.Controller, 
     8{ 
     9        // store: dojo.data.Store 
     10        //              Reference to store object 
     11        store: null, 
    1012 
    11 dojo.declare( 
    12         "dojo.Error", 
    13         Error, 
    14         function(message, extra) { 
    15                 this.message = message; 
    16                 this.extra = extra; 
    17                 this.stack = (new Error()).stack;        
    18         } 
    19 ); 
     13        // query: String 
     14        //      query to get top level node(s) of tree 
     15        query: "", 
    2016 
    21 dojo.declare( 
    22         "dojo.CommunicationError", 
    23         dojo.Error, 
    24         function() { 
    25                 this.name="CommunicationError"; 
    26         } 
    27 ); 
     17        labelAttr: "label", 
     18        typeAttr: "type", 
    2819 
    29 dojo.declare( 
    30         "dojo.LockedError", 
    31         dojo.Error, 
    32         function() { 
    33                 this.name="LockedError"; 
    34         } 
    35 ); 
     20        onAfterTreeCreate: function(message) { 
     21                // when a tree is created, we query against the store to get the top level nodes 
     22                // in the tree 
     23                var tree = message.tree; 
     24                 
     25                var getValue = this.store.getValue; 
     26                function onComplete(/*dojo.data.Item[]*/ items){ 
     27                        // TODO: confirm that I don't have to call loadItem() for each item 
     28                        var childParams=dojo.map(items, 
     29                                function(item){ 
     30                                        return { 
     31                                                item: item, 
     32                                                label: getValue(item, this.labelAttr), 
     33                                                type: getValue(item, this.typeAttr) 
     34                                                }; 
     35                                }); 
     36                        this.tree.setChildren(childParams); 
     37                } 
     38                store.fetch({ query: this.query, onComplete: onComplete }); 
    3639 
    37 dojo.declare( 
    38         "dojo.FormatError", 
    39         dojo.Error, 
    40         function() { 
    41                 this.name="FormatError"; 
    42         } 
    43 ); 
    44  
    45 dojo.declare( 
    46         "dojo.RpcError", 
    47         dojo.Error, 
    48         function() { 
    49                 this.name="RpcError"; 
    50         } 
    51 ); 
    52  
    53 dojo.widget.defineWidget( 
    54         "dojo.widget.TreeLoadingControllerV3", 
    55         dojo.widget.TreeBasicControllerV3, 
    56 {        
    57         RpcUrl: "", 
    58  
    59         RpcActionParam: "action", // used for GET for RpcUrl 
    60  
    61         preventCache: true, 
    62  
    63         checkValidRpcResponse: function(type, obj) { 
    64                 if (type != "load") { 
    65                         var extra = {}                           
    66                         for(var i=1; i<arguments.length;i++) { 
    67                                 dojo.lang.mixin(extra, arguments[i]);                                    
    68                         } 
    69                         return new dojo.CommunicationError(obj, extra);                          
    70                 } 
    71                  
    72                 if (typeof obj != 'object') { 
    73                         return new dojo.FormatError("Wrong server answer format "+(obj && obj.toSource ? obj.toSource() : obj)+" type "+(typeof obj), obj); 
    74                 } 
    75                  
    76                 //dojo.debugShallow(obj); 
    77                          
    78                 if (!dojo.lang.isUndefined(obj.error)) { 
    79                         return new dojo.RpcError(obj.error, obj); 
    80                 } 
    81                  
    82                 return false; 
    83         }, 
    84                  
    85  
    86         getDeferredBindHandler: function(/* dojo.rpc.Deferred */ deferred){ 
    87                 // summary 
    88                 // create callback that calls the Deferred's callback method             
    89                  
    90                 return dojo.lang.hitch(this,  
    91                         function(type, obj){                             
    92                                 //dojo.debug("getDeferredBindHandler "+obj.toSource()); 
    93                                                                  
    94                                 var error = this.checkValidRpcResponse.apply(this, arguments); 
    95                                  
    96                                 if (error) { 
    97                                         deferred.errback(error); 
    98                                         return; 
    99                                 } 
    100          
    101                                 deferred.callback(obj);                                                          
    102                         } 
    103                 ); 
    104                  
     40                dijit._tree.Controller.prototype.onAfterTreeCreate.apply(this, arguments); 
    10541        }, 
    10642 
    107         getRpcUrl: function(action) { 
     43        _expand: function(message){ 
     44                var store = this.store; 
     45                var node = message.node;        // the _TreeNode being expanded 
     46                var getValue = this.store.getValue; 
    10847 
    109                 // RpcUrl=local meant SOLELY for DEMO and LOCAL TESTS 
    110                 if (this.RpcUrl == "local") { 
    111                         var dir = document.location.href.substr(0, document.location.href.lastIndexOf('/')); 
    112                         var localUrl = dir+"/local/"+action; 
    113                         //dojo.debug(localUrl); 
    114                         return localUrl;         
     48                if(node.state == "LOADING"){ 
     49                        return; 
    11550                } 
     51                if(node.state == "UNCHECKED"){ 
     52                        // need to load all the children, and then expand 
     53                        var parentItem = node.item; 
     54                        var childItems = store.getValues(parentItem, "children"); 
    11655 
    117                 if (!this.RpcUrl) { 
    118                         dojo.raise("Empty RpcUrl: can't load"); 
     56                        // count how many items need to be loaded 
     57                        var _waitCount = 0; 
     58                        dojo.forEach(childItems, function(item){ if(!store.isLoaded(item)){ _waitCount++; } }); 
     59 
     60                if(_waitCount == 0){ 
     61                        // all items are already loaded.  proceed.. 
     62                        this._onLoadAllItems(node); 
     63                }else{ 
     64                        // still waiting for some or all of the items to load 
     65                        node.markProcessing(); 
     66 
     67                                function onItem(item){ 
     68                                        if(--_waitCount == 0){ 
     69                                                // all nodes have been loaded, send them to the tree 
     70                                                node.unmarkProcessing(); 
     71                                                this._onLoadAllItems(node); 
     72                                        } 
     73                                } 
     74                                dojo.forEach(childItems, function(item){ 
     75                                        if(!store.isLoaded(item)){ 
     76                                        store.loadItem({item: item, onItem: onItem}); 
     77                                } 
     78                        }); 
     79                } 
    11980                } 
    120                  
    121                 var url = this.RpcUrl; 
    122                  
    123                 if (url.indexOf("/") != 0) { // not absolute 
    124                         var protocol = document.location.href.replace(/:\/\/.*/,''); 
    125                         var prefix = document.location.href.substring(protocol.length+3); 
    126                          
    127                         if (prefix.lastIndexOf("/") != prefix.length-1) { 
    128                                 prefix = prefix.replace(/\/[^\/]+$/,'/'); // strip file name 
    129                         } 
    130                         if (prefix.lastIndexOf("/") != prefix.length-1) { 
    131                                 prefix = prefix+'/'; // add / if not exists it all 
    132                         } 
    133                         //dojo.debug(url); 
    134                         url = protocol + '://' + prefix + url; 
    135                 } 
    136                          
    137  
    138                 return url + (url.indexOf("?")>-1 ? "&" : "?") + this.RpcActionParam+"="+action; 
    13981        }, 
    14082 
    141  
    142         /** 
    143          * Add all loaded nodes from array obj as node children and expand it 
    144         */ 
    145         loadProcessResponse: function(node, result) { 
    146                 //dojo.debug("Process response "+node); 
    147                                  
    148                 if (!dojo.lang.isArray(result)) { 
    149                         throw new dojo.FormatError('loadProcessResponse: Not array loaded: '+result); 
    150                 } 
    151  
    152                 node.setChildren(result); 
    153                  
     83        _onLoadAllItems: function(/*_TreeNode*/ node){ 
     84                // sumary: callback when all the children of a given node have been loaded 
     85                // TODO: should this be used when the top level nodes are loaded too? 
     86                var childParams=dojo.map(items, function(item){ 
     87                        return { item: item, label: getValue(item, this.labelAttr), type: getValue(item, this,typeAttr) }; 
     88                }, this); 
     89                node.setChildren(childParams); 
     90                dijit._tree.Controller.prototype._expand.apply(this, arguments); 
    15491        }, 
    15592 
    156         /** 
    157          * kw = { url, sync, params } 
    158          */ 
    159         runRpc: function(kw) { 
    160                 var _this = this; 
    161                  
    162                 var deferred = new dojo.Deferred(); 
    163                  
    164                 dojo.io.bind({ 
    165                         url: kw.url,                     
    166                         handle: this.getDeferredBindHandler(deferred), 
    167                         mimetype: "text/javascript", 
    168                         preventCache: this.preventCache, 
    169                         sync: kw.sync, 
    170                         content: { data: dojo.json.serialize(kw.params) } 
    171                 }); 
    172                  
    173                 return deferred; 
    174  
    175         }, 
    176  
    177  
    178  
    179         /** 
    180          * Load children of the node from server 
    181          * Synchroneous loading doesn't break control flow 
    182          * I need sync mode for DnD 
    183         */ 
    184         loadRemote: function(node, sync){ 
    185                 var _this = this; 
    186  
    187                 var params = { 
    188                         node: this.getInfo(node), 
    189                         tree: this.getInfo(node.tree) 
    190                 }; 
    191  
    192                  
    193                 var deferred = this.runRpc({ 
    194                         url: this.getRpcUrl('getChildren'), 
    195                         sync: sync, 
    196                         params: params 
    197                 }); 
    198                  
    199                 deferred.addCallback(function(res) { return _this.loadProcessResponse(node,res) }); 
    200                  
    201                                  
    202                  
    203                 return deferred; 
    204  
    205         }, 
    206  
    207         batchExpandTimeout: 0, 
    208  
    209         recurseToLevel: function(widget, level, callFunc, callObj, skipFirst, sync) { 
    210                 if (level == 0) return; 
    211  
    212  
    213                  
    214                 if (!skipFirst) { 
    215                         var deferred = callFunc.call(callObj, widget, sync); 
    216                 } else { 
    217                         var deferred = dojo.Deferred.prototype.makeCalled(); 
     93        _collapse: function(message){ 
     94                if(node.state == "LOADING"){ 
     95                        return; 
    21896                } 
    219                  
    220                 //dojo.debug("expand deferred saved "+node+" sync "+sync); 
    221                  
    222                  
    223                 var _this = this; 
    224                  
    225                 var recurseOnExpand = function() { 
    226                         var children = widget.children; 
    227                         var deferreds = [];              
    228                         for(var i=0; i<children.length; i++) { 
    229                                 //dojo.debug("push recursive call for "+node.children[i]+" level "+level); 
    230                                 deferreds.push(_this.recurseToLevel(children[i], level-1, callFunc, callObj, sync)); 
    231                         } 
    232                         return new dojo.DeferredList(deferreds); 
    233                 } 
    234                  
    235                 deferred.addCallback(recurseOnExpand); 
    236                  
    237                 return deferred; 
    238         }, 
    239          
    240          
    241         expandToLevel: function(nodeOrTree, level, sync) { 
    242                 return this.recurseToLevel(nodeOrTree, nodeOrTree.isTree ? level+1 : level, this.expand, this, nodeOrTree.isTree, sync); 
    243         }, 
    244          
    245         loadToLevel: function(nodeOrTree, level, sync) { 
    246                 return this.recurseToLevel(nodeOrTree, nodeOrTree.isTree ? level+1 : level, this.loadIfNeeded, this, nodeOrTree.isTree, sync); 
    247         }, 
    248          
    249          
    250         loadAll: function(nodeOrTree, sync) { 
    251                 return this.loadToLevel(nodeOrTree, Number.POSITIVE_INFINITY, sync); 
    252         }, 
    253                  
    254          
    255          
    256         expand: function(node, sync) {           
    257                 // widget which children are data objects, is UNCHECKED, but has children and shouldn't be loaded 
    258                 // so I put children check here too 
    259                  
    260                 var _this = this; 
    261                  
    262                 var deferred = this.startProcessing(node); 
    263                  
    264                 deferred.addCallback(function() { 
    265                         return _this.loadIfNeeded(node, sync); 
    266                 }); 
    267                                  
    268                 deferred.addCallback(function(res) { 
    269                         //dojo.debug("Activated callback dojo.widget.TreeBasicControllerV3.prototype.expand(node); "+res); 
    270                         dojo.widget.TreeBasicControllerV3.prototype.expand(node); 
    271                         return res; 
    272                 }); 
    273                  
    274                 deferred.addBoth(function(res) { 
    275                         _this.finishProcessing(node); 
    276                         return res; 
    277                 }); 
    278                  
    279                  
    280                  
    281                 return deferred; 
    282         }, 
    283  
    284          
    285         loadIfNeeded: function(node, sync) { 
    286                 var deferred 
    287                 if (node.state == node.loadStates.UNCHECKED && node.isFolder && !node.children.length) { 
    288                         // populate deferred with other things to pre-do 
    289                         deferred = this.loadRemote(node, sync);                  
    290                 } else { 
    291                         /* "fake action" here */ 
    292                         deferred = new dojo.Deferred(); 
    293                         deferred.callback(); 
    294                 } 
    295                  
    296                 return deferred; 
    297         }, 
    298          
    299         /** 
    300          * 1) if specified, run check, return false if failed 
    301          * 2) if specified, run prepare 
    302          * 3) run make if prepare if no errors 
    303          * 4) run finalize no matter what happened, pass through make result 
    304          * 5) if specified, run expose if no errors 
    305          */ 
    306         runStages: function(check, prepare, make, finalize, expose, args) { 
    307                 var _this = this; 
    308                  
    309                 if (check && !check.apply(this, args)) { 
    310                         return false; 
    311                 } 
    312                  
    313                 var deferred = dojo.Deferred.prototype.makeCalled(); 
    314                  
    315                  
    316                 if (prepare) { 
    317                         deferred.addCallback(function() { 
    318                                 return prepare.apply(_this, args); 
    319                         }); 
    320                 } 
    321                  
    322                  
    323                 //deferred.addCallback(function(res) { dojo.debug("Prepare fired "+res); return res}); 
    324                  
    325                 if (make) { 
    326                         deferred.addCallback(function() {                        
    327                         var res = make.apply(_this, args); 
    328                         //res.addBoth(function(r) {dojo.debugShallow(r); return r;}); 
    329                         return res; 
    330                         }); 
    331                 } 
    332                  
    333                 //deferred.addCallback(function(res) { dojo.debug("Main fired "+res); return res}); 
    334                  
    335                 if (finalize) { 
    336                         deferred.addBoth(function(res) { 
    337                                 finalize.apply(_this, args); 
    338                                 return res; 
    339                         }); 
    340                 } 
    341                          
    342                                  
    343                 // exposer does not affect result 
    344                 if (expose) { 
    345                         deferred.addCallback(function(res) { 
    346                                 expose.apply(_this, args); 
    347                                 return res; 
    348                         }); 
    349                 } 
    350                  
    351                 return deferred; 
    352         }, 
    353                  
    354         startProcessing: function(nodesArray) { 
    355                 var deferred = new dojo.Deferred(); 
    356                  
    357                  
    358                 var nodes = dojo.lang.isArray(nodesArray) ? nodesArray : arguments; 
    359                  
    360                 /* 
    361                 for(var i=0;i<nodes.length;i++) { 
    362                         dojo.debug(nodes[i]); 
    363                 }*/ 
    364                  
    365                 for(var i=0;i<nodes.length;i++) { 
    366                         if (nodes[i].isLocked()) { 
    367                                 deferred.errback(new dojo.LockedError("item locked "+nodes[i], nodes[i])); 
    368                                 //dojo.debug("startProcessing errback "+arguments[i]); 
    369                                 return deferred; 
    370                         } 
    371                         if (nodes[i].isTreeNode) { 
    372                                 //dojo.debug("mark "+nodes[i]); 
    373                                 nodes[i].markProcessing(); 
    374                         } 
    375                         nodes[i].lock(); 
    376                 } 
    377                                  
    378                 //dojo.debug("startProcessing callback"); 
    379                                  
    380                 deferred.callback(); 
    381                  
    382                 return deferred; 
    383         }, 
    384          
    385         finishProcessing: function(nodesArray) { 
    386                  
    387                 var nodes = dojo.lang.isArray(nodesArray) ? nodesArray : arguments; 
    388                  
    389                 for(var i=0;i<nodes.length;i++) { 
    390                         if (!nodes[i].hasLock()) { 
    391                                 // is not processed. probably we locked it and then met bad node in startProcessing 
    392                                 continue;  
    393                         } 
    394                         //dojo.debug("has lock");        
    395                         nodes[i].unlock(); 
    396                         if (nodes[i].isTreeNode) { 
    397                                 //dojo.debug("unmark "+nodes[i]); 
    398                                 nodes[i].unmarkProcessing(); 
    399                         } 
    400                 } 
    401         }, 
    402          
    403         // ----------------- refresh ----------------- 
    404          
    405         refreshChildren: function(nodeOrTree, sync) {            
    406                 return this.runStages(null, this.prepareRefreshChildren, this.doRefreshChildren, this.finalizeRefreshChildren, this.exposeRefreshChildren, arguments); 
    407         }, 
    408  
    409  
    410         prepareRefreshChildren: function(nodeOrTree, sync) { 
    411                 var deferred = this.startProcessing(nodeOrTree); 
    412                 nodeOrTree.destroyChildren(); 
    413                                                  
    414                 nodeOrTree.state = nodeOrTree.loadStates.UNCHECKED; 
    415                  
    416                 return deferred; 
    417         }, 
    418          
    419         doRefreshChildren: function(nodeOrTree, sync) { 
    420                 return this.loadRemote(nodeOrTree, sync); 
    421         }, 
    422          
    423         finalizeRefreshChildren: function(nodeOrTree, sync) { 
    424                 this.finishProcessing(nodeOrTree); 
    425         }, 
    426          
    427         exposeRefreshChildren: function(nodeOrTree, sync) { 
    428                 nodeOrTree.expand(); 
    429         }, 
    430  
    431         // ----------------- move ----------------- 
    432  
    433         move: function(child, newParent, index/*,...*/) { 
    434                 return this.runStages(this.canMove, this.prepareMove, this.doMove, this.finalizeMove, this.exposeMove, arguments);                       
    435         }, 
    436  
    437         doMove: function(child, newParent, index) { 
    438                 //dojo.debug("MOVE "+child); 
    439                 child.tree.move(child, newParent, index); 
    440  
    441                 return true; 
    442         }, 
    443          
    444          
    445         prepareMove: function(child, newParent, index, sync) { 
    446                 var deferred = this.startProcessing(newParent); 
    447                 deferred.addCallback(dojo.lang.hitch(this, function() { 
    448                         return this.loadIfNeeded(newParent, sync); 
    449                 })); 
    450                 return deferred; 
    451         }, 
    452          
    453         finalizeMove: function(child, newParent) { 
    454                 this.finishProcessing(newParent); 
    455         }, 
    456  
    457         // -------------------- createChild ------------ 
    458  
    459         prepareCreateChild: function(parent, index, data, sync) { 
    460                 var deferred = this.startProcessing(parent); 
    461                  
    462                 deferred.addCallback(dojo.lang.hitch(this, function() { 
    463                         return this.loadIfNeeded(parent, sync); 
    464                 })); 
    465                 return deferred; 
    466         }, 
    467          
    468         finalizeCreateChild: function(parent) { 
    469                 this.finishProcessing(parent); 
    470         }, 
    471  
    472         // ---------------- clone --------------- 
    473          
    474         prepareClone: function(child, newParent, index, deep, sync) { 
    475                 var deferred = this.startProcessing(child, newParent); 
    476                 deferred.addCallback(dojo.lang.hitch(this, function() { 
    477                         return this.loadIfNeeded(newParent, sync); 
    478                 }));             
    479                 return deferred;         
    480         },       
    481          
    482         finalizeClone: function(child, newParent) { 
    483                 this.finishProcessing(child, newParent); 
     97                dijit._tree.Controller.prototype._expand.apply(this, arguments); 
    48498        } 
    48599