Changeset 8405
- Timestamp:
- 05/02/07 19:31:09 (21 months ago)
- Files:
-
- 1 modified
-
dijit/trunk/_tree/DataController.js (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
dijit/trunk/_tree/DataController.js
r8278 r8405 1 dojo.provide("dijit._tree.DataController"); 1 2 2 dojo. provide("dojo.widget.TreeLoadingControllerV3");3 dojo.require("dijit._tree.Controller"); 3 4 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"); 5 dijit.declare( 6 "dijit._tree.DataController", 7 dijit._tree.Controller, 8 { 9 // store: dojo.data.Store 10 // Reference to store object 11 store: null, 10 12 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: "", 20 16 21 dojo.declare( 22 "dojo.CommunicationError", 23 dojo.Error, 24 function() { 25 this.name="CommunicationError"; 26 } 27 ); 17 labelAttr: "label", 18 typeAttr: "type", 28 19 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 }); 36 39 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); 105 41 }, 106 42 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; 108 47 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; 115 50 } 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"); 116 55 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 } 119 80 } 120 121 var url = this.RpcUrl;122 123 if (url.indexOf("/") != 0) { // not absolute124 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 name129 }130 if (prefix.lastIndexOf("/") != prefix.length-1) {131 prefix = prefix+'/'; // add / if not exists it all132 }133 //dojo.debug(url);134 url = protocol + '://' + prefix + url;135 }136 137 138 return url + (url.indexOf("?")>-1 ? "&" : "?") + this.RpcActionParam+"="+action;139 81 }, 140 82 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); 154 91 }, 155 92 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; 218 96 } 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); 484 98 } 485 99