1 /** 2 * @author Gillis Haasnoot <gillis.haasnoot@gmail.com> 3 * @package Banana.Controls 4 * @summary Datagrid 5 */ 6 7 goog.provide('Banana.Controls.DataGridBaseListRender'); 8 9 /** @namespace Banana.Controls.DataGridBaseListRender */ 10 namespace('Banana.Controls').DataGridBaseListRender = Banana.Controls.CustomListControl.extend( 11 /** @lends Banana.Controls.DataGridBaseListRender.prototype */ 12 { 13 14 /** 15 * Creates Bases list render. 16 * 17 * base class for list renders. a list render is responsible for rendering item renders. 18 * a listrender instance should be inserted into a datagrid. 19 * 20 * @constructs 21 * @extends Banana.Controls.CustomListControl 22 */ 23 init : function() 24 { 25 this._super(); 26 27 this.addCssClass("BDataGridListRender"); 28 29 this.indexItemRenderFactory = []; //this array consists out of factory's or strings 30 31 this.indexKey = 'id'; 32 this.useAutoIndexing = false; 33 this.autoIndexKey = '__uid__'; 34 this.selectedIndices = new Banana.Util.ArrayBiCollection(); 35 this.indexDataMap = []; 36 this.indexRenderedItemRenderMap = []; //these are the real ui item renders 37 }, 38 39 /** 40 * @param {int} index 41 * @return {Banana.Controls.ItemRender} 42 */ 43 getRenderedItemRenderByIndex : function(index) 44 { 45 return this.indexRenderedItemRenderMap[index]; 46 }, 47 48 /** 49 * @return {Array} 50 */ 51 getRenderedItemRenders : function() 52 { 53 return this.indexRenderedItemRenderMap; 54 }, 55 56 /** 57 * @ignore 58 */ 59 createComponents : function() 60 { 61 this.setupIndexing(); 62 this._super(); 63 }, 64 65 /** 66 * Items in a list needs to be indentified. You can specify a indexkey. 67 * or you can let the listrender generate one. Calling this method 68 * will change the indexKey to autoIndexKey value 69 * 70 * @return {boolean} true when index 71 * @ignore 72 */ 73 setupIndexing : function() 74 { 75 if (!this.indexKey) 76 { 77 this.useAutoIndexing = true; 78 this.indexKey = this.autoIndexKey; 79 return true; 80 } 81 82 return false; 83 }, 84 85 /** 86 * @return {String} almost unique 87 * @ignore 88 */ 89 getUid : function() 90 { 91 return (((1+Math.random())*0x1000000000)|0).toString(16).substring(1); 92 }, 93 94 /** 95 * applies a unique identifier to an object 96 * not that we wont overwrite an existing identifier. this would result into inconsistency problems. 97 * 98 * @param {Object} data 99 */ 100 applyUid : function(data) 101 { 102 if (this.useAutoIndexing && !data[this.indexKey]) 103 { 104 data[this.indexKey] = this.getUid(); 105 } 106 }, 107 108 /** 109 * Rerenders the list render. 110 */ 111 rerender : function() 112 { 113 this.clear(); 114 this.createControls(); 115 this.invalidateDisplay(); 116 }, 117 118 /** 119 * called by framework when control is removed 120 * @ignore 121 */ 122 unload : function() 123 { 124 this._super(); 125 126 this.selectedIndices.clear(); 127 this.selectedIndices = undefined; 128 this.indexDataMap = undefined; 129 }, 130 131 /** 132 * @param {String} key 133 */ 134 setItemIndexKey : function(key) 135 { 136 this.indexKey = key; 137 return this; 138 }, 139 140 /** 141 * @param {int} i 142 */ 143 addSelectedIndex : function(i) 144 { 145 this.selectIndex(i); 146 147 this.selectedIndices.addItem(i,true); 148 }, 149 150 /** 151 * clears selected indices 152 */ 153 clearSelectedIndices : function() 154 { 155 this.selectedIndices.each(this.getProxy(function(index,d) 156 { 157 var keyIndex = this.selectedIndices.getKeyByIndex(index); 158 this.deSelectIndex(keyIndex); 159 })); 160 161 this.selectedIndices.clear(); 162 }, 163 164 /** 165 * @param {int} index 166 */ 167 clearSelectedIndex : function(index) 168 { 169 this.deSelectIndex(index); 170 171 this.selectedIndices.remove(index); 172 }, 173 174 /** 175 * Retreives the selected indices. 176 * @param {boolean} flat true returns array of selected indices 177 * @return {mixed} 178 */ 179 getSelectedIndices : function(flat) 180 { 181 if (flat) 182 { 183 var result = []; 184 this.selectedIndices.each(this.getProxy(function(index,d) 185 { 186 var keyIndex = this.selectedIndices.getKeyByIndex(index); 187 result.push(keyIndex); 188 })); 189 190 return result; 191 } 192 193 return this.selectedIndices; 194 }, 195 196 /** 197 * @param {int} index 198 * @return {Boolean} true when selected 199 */ 200 getIndexIsSelected : function(index) 201 { 202 if (this.selectedIndices.getItemByKey(index)) 203 { 204 return true; 205 } 206 207 return false; 208 }, 209 210 /** 211 * Sets selected indices by items. 212 * @param {Array} items 213 */ 214 setSelectedItems : function(items) 215 { 216 for (i = 0, len = items.length; i < len; i++) 217 { 218 var rowIndex = 0; 219 var dataIndex; 220 for (dataIndex in this.datasource) 221 { 222 if (typeof(this.datasource[dataIndex]) === 'function' ) {continue;} 223 224 if (items[i] && (this.datasource[dataIndex][this.indexKey] === items[i][this.indexKey])) 225 { 226 this.addSelectedIndex(rowIndex); 227 } 228 229 rowIndex++; 230 } 231 } 232 }, 233 234 /** 235 * moves selected items up 236 * 237 * @return {this} 238 */ 239 moveSelectedItemsUp : function() 240 { 241 var si = []; 242 243 this.selectedIndices.each(this.getProxy(function(index,d) 244 { 245 si.push(this.selectedIndices.getKeyByIndex(index)); 246 })); 247 248 //no items selected 249 if (!si.length) 250 { 251 return; 252 } 253 254 //sort for extraction 255 si.sort(); 256 257 var amountToExtract = si.length; 258 259 //determine index for extraction 260 var indexToExtract = si[0]-1; 261 262 //no items above selected 263 if (indexToExtract < 0) 264 { 265 return; 266 } 267 268 //save item we are going to remove 269 var itemToExtract = this.datasource[indexToExtract]; 270 271 //remove item from index 272 this.datasource.splice(indexToExtract,1); 273 274 //reinsert item 275 this.datasource.splice(indexToExtract+amountToExtract,0,itemToExtract); 276 277 //force rerender 278 this.setDataSource(this.datasource); 279 280 return this; 281 }, 282 283 /** 284 * moves selected items down 285 * 286 * @return {this} 287 */ 288 moveSelectedItemsDown : function() 289 { 290 var si = []; 291 292 this.selectedIndices.each(this.getProxy(function(index,d) 293 { 294 si.push(this.selectedIndices.getKeyByIndex(index)); 295 })); 296 297 //no items selected 298 if (!si.length) 299 { 300 return; 301 } 302 303 //sort for extraction 304 si.sort(); 305 306 var amountToExtract = si.length; 307 308 //determine index for extraction 309 var indexToExtract = si[si.length-1]+1; 310 311 //no items above selected 312 if (indexToExtract+1 > this.datasource.length) 313 { 314 return; 315 } 316 317 //save item we are going to remove 318 var itemToExtract = this.datasource[indexToExtract]; 319 320 //remove item from index 321 this.datasource.splice(indexToExtract,1); 322 323 //reinsert item 324 this.datasource.splice(si[0],0,itemToExtract); 325 326 //force rerender 327 this.setDataSource(this.datasource); 328 329 return this; 330 }, 331 332 /** 333 * Selects previous from list 334 * @return {this} 335 */ 336 selectPreviousFromList : function() 337 { 338 var selected = this.listRender.getSelectedIndices(true); 339 340 this.listRender.clearSelectedIndices(); 341 342 if (selected.length) 343 { 344 var toSelect = Math.max(0,selected[0]-1); 345 this.listRender.addSelectedIndex(toSelect); 346 } 347 else 348 { 349 this.listRender.addSelectedIndex(selected.length-1); 350 } 351 return this; 352 }, 353 354 /** 355 * Selects next item from list 356 * @return {this} 357 */ 358 selectNextFromList : function() 359 { 360 var selected = this.listRender.getSelectedIndices(true); 361 362 this.listRender.clearSelectedIndices(); 363 364 if (selected.length) 365 { 366 var toSelect = Math.min(this.listRender.datasource.length-1,selected[0]+1); 367 this.listRender.addSelectedIndex(toSelect); 368 } 369 else 370 { 371 this.listRender.addSelectedIndex(0); 372 } 373 return this; 374 }, 375 376 /** 377 * @return {Array} of selected keys 378 */ 379 getSelectedKeys : function() 380 { 381 var keys = []; 382 383 this.selectedIndices.each(this.getProxy(function(index,d) 384 { 385 var keyIndex = this.selectedIndices.getKeyByIndex(index); 386 keys.push(this.indexDataMap[keyIndex][this.indexKey]); 387 })); 388 389 return keys; 390 }, 391 392 /** 393 * removes selected items 394 */ 395 removeSelectedItems : function() 396 { 397 var si = this.getSelectedItems(); 398 399 this.clearSelectedIndices(); 400 401 var i,len; 402 for (i = 0, len = si.length; i < len; i++ ) 403 { 404 var index = this.indexDataMap.indexOf(si[i]); 405 this.indexDataMap.splice(index,1); 406 this.removeItem(si[i],index); 407 } 408 }, 409 410 /** 411 * @return {Array} of selected items 412 */ 413 getSelectedItems : function() 414 { 415 var items = []; 416 417 this.selectedIndices.each(this.getProxy(function(index,d) 418 { 419 var keyIndex = this.selectedIndices.getKeyByIndex(index); 420 items.push(this.indexDataMap[keyIndex]); 421 })); 422 423 return items; 424 }, 425 426 /** 427 * adds an item to the datasource. 428 * if there is no datasource, we will set an empty datasource on the grid 429 * to make sure our items will get rendered 430 * @param {mixed} data 431 * @return {int} position of added item 432 */ 433 addItem : function(data) 434 { 435 var addedAt = 0; 436 437 var gridRerender = false; 438 439 if (!this.datasource) 440 { 441 //force the datagrid to rerender 442 this.datagrid.setDataSource([]); 443 } 444 else 445 { 446 addedAt = this.datasource.length; 447 } 448 449 this.datasource[addedAt] = data; 450 this.indexDataMap[addedAt] = data; 451 this.triggerEvent('dataSourceChanged'); 452 453 return addedAt; 454 }, 455 456 /** 457 * @param {Object} data 458 */ 459 removeItem : function(data) 460 { 461 var match = null; 462 463 var k,len; 464 for (k =0, len = this.datasource.length; k < len; k++) 465 { 466 if(data[this.indexKey] === this.datasource[k][this.indexKey]) 467 { 468 this.selectedIndices.remove(k); 469 match = k; 470 } 471 } 472 473 if (match !== null) 474 { 475 this.datasource.splice(match,1); 476 477 this.triggerEvent('dataSourceChanged'); 478 } 479 480 return match; 481 }, 482 483 /** 484 * Removes all items from the list and clears selected indices 485 */ 486 removeAllItems : function() 487 { 488 this.datasource = []; 489 this.selectedIndices.clear(); 490 }, 491 492 /** 493 * @abstract 494 * @ignore 495 */ 496 selectIndex : function(i){}, 497 498 /** 499 * @abstract 500 * @ignore 501 */ 502 deSelectIndex : function(i){}, 503 504 /** 505 * invoked after setting datasource 506 * @ignore 507 */ 508 createControls : function() 509 { 510 this.setupIndexing(); 511 512 var i,len; 513 for (i =0, len = this.datasource.length; i < len; i++) 514 { 515 this.indexDataMap[i] = this.datasource[i]; 516 } 517 }, 518 519 /** 520 * ensures item render from factory 521 * @return Banana.Controls.DatagridItemRender 522 */ 523 getObject : function(itemRender) 524 { 525 if (typeof(itemRender) === 'function') 526 { 527 itemRender = new itemRender(); 528 } 529 530 return itemRender; 531 } 532 }); 533