1 // MODEL 2 3 /* 4 * For a JSON array that gets too large to load in one go, this class 5 * helps break it up into chunks and provides an 6 * async API for using the information in the array. 7 */ 8 9 /** 10 * Construct a new LazyArray, which partially loads large JSON arrays. 11 * @class 12 * @constructor 13 * @param lazyArrayParams {Object} as: 14 * <ul> 15 * <li><code>urlTemplate</code> - for each lazily-loaded array chunk, the chunk number will get substituted for {chunk} in this template, and the result will beused as the URL of the JSON for that array chunk</li> 16 * <li><code>length</code> - length of the overall array</li> 17 * <li><code>chunkSize</code> - the size of each array chunk</li> 18 * </ul> 19 */ 20 function LazyArray(lazyArrayParams, baseUrl) { 21 this.urlTemplate = lazyArrayParams.urlTemplate; 22 this.chunkSize = lazyArrayParams.chunkSize; 23 this.length = lazyArrayParams.length; 24 this.baseUrl = (baseUrl === undefined ? "" : baseUrl); 25 // Once a range gets loaded, it goes into the "chunks" array. 26 // this.chunks[n] contains data for indices in the range 27 // [n * chunkSize, Math.min(length - 1, (n * (chunkSize + 1)) - 1)] 28 this.chunks = []; 29 // If a range is currently loading, this will contain a property 30 // "chunk number": [{start, end, callback, param}, ...] 31 this.toProcess = {}; 32 } 33 34 /** 35 * call the callback on one element of the array 36 * @param i index 37 * @param callback callback, gets called with (i, value, param) 38 * @param param (optional) callback will get this as its last parameter 39 */ 40 LazyArray.prototype.index = function(i, callback, param) { 41 this.range(i, i, callback, undefined, param); 42 }; 43 44 /** 45 * call the callback on each element in the range [start, end] 46 * @param start index of first element to call the callback on 47 * @param end index of last element to call the callback on 48 * @param callback callback, gets called with (i, value, param) 49 * @param postFun (optional) callback that gets called when <code>callback</code> has been run on every element in the range 50 * @param param (optional) callback will get this as its last parameter 51 */ 52 LazyArray.prototype.range = function(start, end, callback, postFun, param) { 53 start = Math.max(0, start); 54 end = Math.min(end, this.length - 1); 55 56 var firstChunk = Math.floor(start / this.chunkSize); 57 var lastChunk = Math.floor(end / this.chunkSize); 58 59 if (postFun === undefined) /** @inner */ postFun = function() {}; 60 var finish = new Finisher(postFun); 61 62 for (var chunk = firstChunk; chunk <= lastChunk; chunk++) { 63 if (this.chunks[chunk]) { 64 // chunk is loaded 65 this._processChunk(start, end, chunk, callback, param); 66 } else { 67 var toProcessInfo = { 68 start: start, 69 end: end, 70 callback: callback, 71 param: param, 72 finish: finish 73 }; 74 75 finish.inc(); 76 if (this.toProcess[chunk]) { 77 // chunk is currently being loaded 78 this.toProcess[chunk].push(toProcessInfo); 79 } else { 80 // start loading chunk 81 this.toProcess[chunk] = [toProcessInfo]; 82 var url = this.urlTemplate.replace(/\{Chunk\}/g, chunk); 83 var thisObj = this; 84 dojo.xhrGet( 85 { 86 url: Util.resolveUrl(this.baseUrl, url), 87 handleAs: "json", 88 load: this._makeLoadFun(chunk), 89 error: function() { finish.dec(); } 90 }); 91 } 92 } 93 } 94 finish.finish(); 95 }; 96 97 LazyArray.prototype._makeLoadFun = function(chunk) { 98 var thisObj = this; 99 return function(data) { 100 thisObj.chunks[chunk] = data; 101 var toProcess = thisObj.toProcess[chunk]; 102 delete thisObj.toProcess[chunk]; 103 for (var i = 0; i < toProcess.length; i++) { 104 thisObj._processChunk(toProcess[i].start, 105 toProcess[i].end, 106 chunk, 107 toProcess[i].callback, 108 toProcess[i].param); 109 toProcess[i].finish.dec(); 110 } 111 }; 112 }; 113 114 LazyArray.prototype._processChunk = function(start, end, chunk, 115 callback, param) { 116 // index (in the overall lazy array) of the first position in this chunk 117 var firstIndex = chunk * this.chunkSize; 118 119 var chunkStart = start - firstIndex; 120 var chunkEnd = end - firstIndex; 121 chunkStart = Math.max(0, chunkStart); 122 chunkEnd = Math.min(chunkEnd, this.chunkSize - 1); 123 124 for (var i = chunkStart; i <= chunkEnd; i++) { 125 callback(i + firstIndex, this.chunks[chunk][i], param); 126 } 127 }; 128