1/* 2* 3* Copyright (c) 2007 Andrew Tetlaw & Millstream Web Software 4* http://www.millstream.com.au/view/code/tablekit/ 5* Version: 1.3b 2008-03-23 6* 7* Permission is hereby granted, free of charge, to any person 8* obtaining a copy of this software and associated documentation 9* files (the "Software"), to deal in the Software without 10* restriction, including without limitation the rights to use, copy, 11* modify, merge, publish, distribute, sublicense, and/or sell copies 12* of the Software, and to permit persons to whom the Software is 13* furnished to do so, subject to the following conditions: 14* 15* The above copyright notice and this permission notice shall be 16* included in all copies or substantial portions of the Software. 17* 18* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 22* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 23* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 24* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25* SOFTWARE. 26* * 27*/ 28 29// Use the TableKit class constructure if you'd prefer to init your tables as JS objects 30var TableKit = Class.create(); 31 32TableKit.prototype = { 33 initialize : function(elm, options) { 34 var table = $(elm); 35 if(table.tagName !== "TABLE") { 36 return; 37 } 38 TableKit.register(table,Object.extend(TableKit.options,options || {})); 39 this.id = table.id; 40 var op = TableKit.option('sortable resizable editable', this.id); 41 if(op.sortable) { 42 TableKit.Sortable.init(table); 43 } 44 if(op.resizable) { 45 TableKit.Resizable.init(table); 46 } 47 if(op.editable) { 48 TableKit.Editable.init(table); 49 } 50 }, 51 sort : function(column, order) { 52 TableKit.Sortable.sort(this.id, column, order); 53 }, 54 resizeColumn : function(column, w) { 55 TableKit.Resizable.resize(this.id, column, w); 56 }, 57 editCell : function(row, column) { 58 TableKit.Editable.editCell(this.id, row, column); 59 } 60}; 61 62Object.extend(TableKit, { 63 getBodyRows : function(table) { 64 table = $(table); 65 var id = table.id; 66 if(!TableKit.tables[id].dom.rows) { 67 TableKit.tables[id].dom.rows = (table.tHead && table.tHead.rows.length > 0) ? $A(table.tBodies[0].rows) : $A(table.rows).without(table.rows[0]); 68 } 69 return TableKit.tables[id].dom.rows; 70 }, 71 getHeaderCells : function(table, cell) { 72 if(!table) { table = $(cell).up('table'); } 73 var id = table.id; 74 if(!TableKit.tables[id].dom.head) { 75 TableKit.tables[id].dom.head = $A((table.tHead && table.tHead.rows.length > 0) ? table.tHead.rows[table.tHead.rows.length-1].cells : table.rows[0].cells); 76 } 77 return TableKit.tables[id].dom.head; 78 }, 79 getCellIndex : function(cell) { 80 return $A(cell.parentNode.cells).indexOf(cell); 81 }, 82 getRowIndex : function(row) { 83 return $A(row.parentNode.rows).indexOf(row); 84 }, 85 getCellText : function(cell, refresh) { 86 if(!cell) { return ""; } 87 var data = TableKit.getCellData(cell); 88 if(refresh || data.refresh || !data.textContent) { 89 data.textContent = cell.textContent ? cell.textContent : cell.innerText; 90 data.refresh = false; 91 } 92 return data.textContent; 93 }, 94 getCellData : function(cell) { 95 var t = null; 96 if(!cell.id) { 97 t = $(cell).up('table'); 98 cell.id = t.id + "-cell-" + TableKit._getc(); 99 } 100 var tblid = t ? t.id : cell.id.match(/(.*)-cell.*/)[1]; 101 if(!TableKit.tables[tblid].dom.cells[cell.id]) { 102 TableKit.tables[tblid].dom.cells[cell.id] = {textContent : '', htmlContent : '', active : false}; 103 } 104 return TableKit.tables[tblid].dom.cells[cell.id]; 105 }, 106 register : function(table, options) { 107 if(!table.id) { 108 table.id = "tablekit-table-" + TableKit._getc(); 109 } 110 var id = table.id; 111 TableKit.tables[id] = TableKit.tables[id] ? 112 Object.extend(TableKit.tables[id], options || {}) : 113 Object.extend( 114 {dom : {head:null,rows:null,cells:{}},sortable:false,resizable:false,editable:false}, 115 options || {} 116 ); 117 }, 118 notify : function(eventName, table, event) { 119 if(TableKit.tables[table.id] && TableKit.tables[table.id].observers && TableKit.tables[table.id].observers[eventName]) { 120 TableKit.tables[table.id].observers[eventName](table, event); 121 } 122 TableKit.options.observers[eventName](table, event)(); 123 }, 124 isSortable : function(table) { 125 return TableKit.tables[table.id] ? TableKit.tables[table.id].sortable : false; 126 }, 127 isResizable : function(table) { 128 return TableKit.tables[table.id] ? TableKit.tables[table.id].resizable : false; 129 }, 130 isEditable : function(table) { 131 return TableKit.tables[table.id] ? TableKit.tables[table.id].editable : false; 132 }, 133 setup : function(o) { 134 Object.extend(TableKit.options, o || {} ); 135 }, 136 option : function(s, id, o1, o2) { 137 o1 = o1 || TableKit.options; 138 o2 = o2 || (id ? (TableKit.tables[id] ? TableKit.tables[id] : {}) : {}); 139 var key = id + s; 140 if(!TableKit._opcache[key]){ 141 TableKit._opcache[key] = $A($w(s)).inject([],function(a,v){ 142 a.push(a[v] = o2[v] || o1[v]); 143 return a; 144 }); 145 } 146 return TableKit._opcache[key]; 147 }, 148 e : function(event) { 149 return event || window.event; 150 }, 151 tables : {}, 152 _opcache : {}, 153 options : { 154 autoLoad : true, 155 stripe : true, 156 sortable : true, 157 resizable : true, 158 editable : true, 159 rowEvenClass : 'roweven', 160 rowOddClass : 'rowodd', 161 sortableSelector : ['table.sortable'], 162 columnClass : 'sortcol', 163 descendingClass : 'sortdesc', 164 ascendingClass : 'sortasc', 165 defaultSortDirection : 1, 166 noSortClass : 'nosort', 167 sortFirstAscendingClass : 'sortfirstasc', 168 sortFirstDecendingClass : 'sortfirstdesc', 169 resizableSelector : ['table.resizable'], 170 minWidth : 10, 171 showHandle : true, 172 resizeOnHandleClass : 'resize-handle-active', 173 editableSelector : ['table.editable'], 174 formClassName : 'editable-cell-form', 175 noEditClass : 'noedit', 176 editAjaxURI : '/', 177 editAjaxOptions : {}, 178 observers : { 179 'onSortStart' : function(){}, 180 'onSort' : function(){}, 181 'onSortEnd' : function(){}, 182 'onResizeStart' : function(){}, 183 'onResize' : function(){}, 184 'onResizeEnd' : function(){}, 185 'onEditStart' : function(){}, 186 'onEdit' : function(){}, 187 'onEditEnd' : function(){} 188 } 189 }, 190 _c : 0, 191 _getc : function() {return TableKit._c += 1;}, 192 unloadTable : function(table){ 193 table = $(table); 194 if(!TableKit.tables[table.id]) {return;} //if not an existing registered table return 195 var cells = TableKit.getHeaderCells(table); 196 var op = TableKit.option('sortable resizable editable noSortClass descendingClass ascendingClass columnClass sortFirstAscendingClass sortFirstDecendingClass', table.id); 197 //unregister all the sorting and resizing events 198 cells.each(function(c){ 199 c = $(c); 200 if(op.sortable) { 201 if(!c.hasClassName(op.noSortClass)) { 202 Event.stopObserving(c, 'mousedown', TableKit.Sortable._sort); 203 c.removeClassName(op.columnClass); 204 c.removeClassName(op.sortFirstAscendingClass); 205 c.removeClassName(op.sortFirstDecendingClass); 206 //ensure that if table reloaded current sort is remembered via sort first class name 207 if(c.hasClassName(op.ascendingClass)) { 208 c.removeClassName(op.ascendingClass); 209 c.addClassName(op.sortFirstAscendingClass) 210 } else if (c.hasClassName(op.descendingClass)) { 211 c.removeClassName(op.descendingClass); 212 c.addClassName(op.sortFirstDecendingClass) 213 } 214 } 215 } 216 if(op.resizable) { 217 Event.stopObserving(c, 'mouseover', TableKit.Resizable.initDetect); 218 Event.stopObserving(c, 'mouseout', TableKit.Resizable.killDetect); 219 } 220 }); 221 //unregister the editing events and cancel any open editors 222 if(op.editable) { 223 Event.stopObserving(table.tBodies[0], 'click', TableKit.Editable._editCell); 224 for(var c in TableKit.tables[table.id].dom.cells) { 225 if(TableKit.tables[table.id].dom.cells[c].active) { 226 var cell = $(c); 227 var editor = TableKit.Editable.getCellEditor(cell); 228 editor.cancel(cell); 229 } 230 } 231 } 232 //delete the cache 233 TableKit.tables[table.id].dom = {head:null,rows:null,cells:{}}; // TODO: watch this for mem leaks 234 }, 235 reloadTable : function(table){ 236 table = $(table); 237 TableKit.unloadTable(table); 238 var op = TableKit.option('sortable resizable editable', table.id); 239 if(op.sortable) {TableKit.Sortable.init(table);} 240 if(op.resizable) {TableKit.Resizable.init(table);} 241 if(op.editable) {TableKit.Editable.init(table);} 242 }, 243 reload : function() { 244 for(var k in TableKit.tables) { 245 TableKit.reloadTable(k); 246 } 247 }, 248 load : function() { 249 if(TableKit.options.autoLoad) { 250 if(TableKit.options.sortable) { 251 $A(TableKit.options.sortableSelector).each(function(s){ 252 $$(s).each(function(t) { 253 TableKit.Sortable.init(t); 254 }); 255 }); 256 } 257 if(TableKit.options.resizable) { 258 $A(TableKit.options.resizableSelector).each(function(s){ 259 $$(s).each(function(t) { 260 TableKit.Resizable.init(t); 261 }); 262 }); 263 } 264 if(TableKit.options.editable) { 265 $A(TableKit.options.editableSelector).each(function(s){ 266 $$(s).each(function(t) { 267 TableKit.Editable.init(t); 268 }); 269 }); 270 } 271 } 272 } 273}); 274 275TableKit.Rows = { 276 stripe : function(table) { 277 var rows = TableKit.getBodyRows(table); 278 rows.each(function(r,i) { 279 TableKit.Rows.addStripeClass(table,r,i); 280 }); 281 }, 282 addStripeClass : function(t,r,i) { 283 t = t || r.up('table'); 284 var op = TableKit.option('rowEvenClass rowOddClass', t.id); 285 var css = ((i+1)%2 === 0 ? op[0] : op[1]); 286 // using prototype's assClassName/RemoveClassName was not efficient for large tables, hence: 287 var cn = r.className.split(/\s+/); 288 var newCn = []; 289 for(var x = 0, l = cn.length; x < l; x += 1) { 290 if(cn[x] !== op[0] && cn[x] !== op[1]) { newCn.push(cn[x]); } 291 } 292 newCn.push(css); 293 r.className = newCn.join(" "); 294 } 295}; 296 297TableKit.Sortable = { 298 init : function(elm, options){ 299 var table = $(elm); 300 if(table.tagName !== "TABLE") { 301 return; 302 } 303 TableKit.register(table,Object.extend(options || {},{sortable:true})); 304 var sortFirst; 305 var cells = TableKit.getHeaderCells(table); 306 var op = TableKit.option('noSortClass columnClass sortFirstAscendingClass sortFirstDecendingClass', table.id); 307 cells.each(function(c){ 308 c = $(c); 309 if(!c.hasClassName(op.noSortClass)) { 310 Event.observe(c, 'mousedown', TableKit.Sortable._sort); 311 c.addClassName(op.columnClass); 312 if(c.hasClassName(op.sortFirstAscendingClass) || c.hasClassName(op.sortFirstDecendingClass)) { 313 sortFirst = c; 314 } 315 } 316 }); 317 318 if(sortFirst) { 319 if(sortFirst.hasClassName(op.sortFirstAscendingClass)) { 320 TableKit.Sortable.sort(table, sortFirst, 1); 321 } else { 322 TableKit.Sortable.sort(table, sortFirst, -1); 323 } 324 } else { // just add row stripe classes 325 TableKit.Rows.stripe(table); 326 } 327 }, 328 reload : function(table) { 329 table = $(table); 330 var cells = TableKit.getHeaderCells(table); 331 var op = TableKit.option('noSortClass columnClass', table.id); 332 cells.each(function(c){ 333 c = $(c); 334 if(!c.hasClassName(op.noSortClass)) { 335 Event.stopObserving(c, 'mousedown', TableKit.Sortable._sort); 336 c.removeClassName(op.columnClass); 337 } 338 }); 339 TableKit.Sortable.init(table); 340 }, 341 _sort : function(e) { 342 if(TableKit.Resizable._onHandle) {return;} 343 e = TableKit.e(e); 344 Event.stop(e); 345 var cell = Event.element(e); 346 while(!(cell.tagName && cell.tagName.match(/td|th/gi))) { 347 cell = cell.parentNode; 348 } 349 TableKit.Sortable.sort(null, cell); 350 }, 351 sort : function(table, index, order) { 352 var cell; 353 if(typeof index === 'number') { 354 if(!table || (table.tagName && table.tagName !== "TABLE")) { 355 return; 356 } 357 table = $(table); 358 index = Math.min(table.rows[0].cells.length, index); 359 index = Math.max(1, index); 360 index -= 1; 361 cell = (table.tHead && table.tHead.rows.length > 0) ? $(table.tHead.rows[table.tHead.rows.length-1].cells[index]) : $(table.rows[0].cells[index]); 362 } else { 363 cell = $(index); 364 table = table ? $(table) : cell.up('table'); 365 index = TableKit.getCellIndex(cell); 366 } 367 var op = TableKit.option('noSortClass descendingClass ascendingClass defaultSortDirection', table.id); 368 369 if(cell.hasClassName(op.noSortClass)) {return;} 370 //TableKit.notify('onSortStart', table); 371 order = order ? order : op.defaultSortDirection; 372 var rows = TableKit.getBodyRows(table); 373 374 if(cell.hasClassName(op.ascendingClass) || cell.hasClassName(op.descendingClass)) { 375 rows.reverse(); // if it was already sorted we just need to reverse it. 376 order = cell.hasClassName(op.descendingClass) ? 1 : -1; 377 } else { 378 var datatype = TableKit.Sortable.getDataType(cell,index,table); 379 var tkst = TableKit.Sortable.types; 380 rows.sort(function(a,b) { 381 return order * tkst[datatype].compare(TableKit.getCellText(a.cells[index]),TableKit.getCellText(b.cells[index])); 382 }); 383 } 384 var tb = table.tBodies[0]; 385 var tkr = TableKit.Rows; 386 rows.each(function(r,i) { 387 tb.appendChild(r); 388 tkr.addStripeClass(table,r,i); 389 }); 390 var hcells = TableKit.getHeaderCells(null, cell); 391 $A(hcells).each(function(c,i){ 392 c = $(c); 393 c.removeClassName(op.ascendingClass); 394 c.removeClassName(op.descendingClass); 395 if(index === i) { 396 if(order === 1) { 397 c.addClassName(op.ascendingClass); 398 } else { 399 c.addClassName(op.descendingClass); 400 } 401 } 402 }); 403 }, 404 types : {}, 405 detectors : [], 406 addSortType : function() { 407 $A(arguments).each(function(o){ 408 TableKit.Sortable.types[o.name] = o; 409 }); 410 }, 411 getDataType : function(cell,index,table) { 412 cell = $(cell); 413 index = (index || index === 0) ? index : TableKit.getCellIndex(cell); 414 415 var colcache = TableKit.Sortable._coltypecache; 416 var cache = colcache[table.id] ? colcache[table.id] : (colcache[table.id] = {}); 417 418 if(!cache[index]) { 419 var t = false; 420 // first look for a data type id on the heading row cell 421 if(cell.id && TableKit.Sortable.types[cell.id]) { 422 t = cell.id 423 } 424 if(!t) { 425 t = $w(cell.className).detect(function(n){ // then look for a data type classname on the heading row cell 426 return (TableKit.Sortable.types[n]) ? true : false; 427 }); 428 } 429 if(!t) { 430 var rows = TableKit.getBodyRows(table); 431 cell = rows[0].cells[index]; // grab same index cell from body row to try and match data type 432 t = TableKit.Sortable.detectors.detect( 433 function(d){ 434 return TableKit.Sortable.types[d].detect(TableKit.getCellText(cell)); 435 }); 436 } 437 cache[index] = t; 438 } 439 return cache[index]; 440 }, 441 _coltypecache : {} 442}; 443 444TableKit.Sortable.detectors = $A($w('date-iso date date-eu date-au time currency datasize number casesensitivetext text')); // setting it here because Safari complained when I did it above... 445 446TableKit.Sortable.Type = Class.create(); 447TableKit.Sortable.Type.prototype = { 448 initialize : function(name, options){ 449 this.name = name; 450 options = Object.extend({ 451 normal : function(v){ 452 return v; 453 }, 454 pattern : /.*/ 455 }, options || {}); 456 this.normal = options.normal; 457 this.pattern = options.pattern; 458 if(options.compare) { 459 this.compare = options.compare; 460 } 461 if(options.detect) { 462 this.detect = options.detect; 463 } 464 }, 465 compare : function(a,b){ 466 return TableKit.Sortable.Type.compare(this.normal(a), this.normal(b)); 467 }, 468 detect : function(v){ 469 return this.pattern.test(v); 470 } 471}; 472 473TableKit.Sortable.Type.compare = function(a,b) { 474 return a < b ? -1 : a === b ? 0 : 1; 475}; 476 477TableKit.Sortable.addSortType( 478 new TableKit.Sortable.Type('number', { 479 pattern : /^[-+]?[\d]*\.?[\d]+(?:[eE][-+]?[\d]+)?/, 480 normal : function(v) { 481 // This will grab the first thing that looks like a number from a string, so you can use it to order a column of various srings containing numbers. 482 v = parseFloat(v.replace(/^.*?([-+]?[\d]*\.?[\d]+(?:[eE][-+]?[\d]+)?).*$/,"$1")); 483 return isNaN(v) ? 0 : v; 484 }}), 485 new TableKit.Sortable.Type('text',{ 486 normal : function(v) { 487 return v ? v.toLowerCase() : ''; 488 }}), 489 new TableKit.Sortable.Type('casesensitivetext',{pattern : /^[A-Z]+$/}), 490 new TableKit.Sortable.Type('datasize',{ 491 pattern : /^[-+]?[\d]*\.?[\d]+(?:[eE][-+]?[\d]+)?\s?[k|m|g|t]b$/i, 492 normal : function(v) { 493 var r = v.match(/^([-+]?[\d]*\.?[\d]+([eE][-+]?[\d]+)?)\s?([k|m|g|t]?b)?/i); 494 var b = r[1] ? Number(r[1]).valueOf() : 0; 495 var m = r[3] ? r[3].substr(0,1).toLowerCase() : ''; 496 var result = b; 497 switch(m) { 498 case 'k': 499 result = b * 1024; 500 break; 501 case 'm': 502 result = b * 1024 * 1024; 503 break; 504 case 'g': 505 result = b * 1024 * 1024 * 1024; 506 break; 507 case 't': 508 result = b * 1024 * 1024 * 1024 * 1024; 509 break; 510 } 511 return result; 512 }}), 513 new TableKit.Sortable.Type('date-au',{ 514 pattern : /^\d{2}\/\d{2}\/\d{4}\s?(?:\d{1,2}\:\d{2}(?:\:\d{2})?\s?[a|p]?m?)?/i, 515 normal : function(v) { 516 if(!this.pattern.test(v)) {return 0;} 517 var r = v.match(/^(\d{2})\/(\d{2})\/(\d{4})\s?(?:(\d{1,2})\:(\d{2})(?:\:(\d{2}))?\s?([a|p]?m?))?/i); 518 var yr_num = r[3]; 519 var mo_num = parseInt(r[2],10)-1; 520 var day_num = r[1]; 521 var hr_num = r[4] ? r[4] : 0; 522 if(r[7]) { 523 var chr = parseInt(r[4],10); 524 if(r[7].toLowerCase().indexOf('p') !== -1) { 525 hr_num = chr < 12 ? chr + 12 : chr; 526 } else if(r[7].toLowerCase().indexOf('a') !== -1) { 527 hr_num = chr < 12 ? chr : 0; 528 } 529 } 530 var min_num = r[5] ? r[5] : 0; 531 var sec_num = r[6] ? r[6] : 0; 532 return new Date(yr_num, mo_num, day_num, hr_num, min_num, sec_num, 0).valueOf(); 533 }}), 534 new TableKit.Sortable.Type('date-us',{ 535 pattern : /^\d{2}\/\d{2}\/\d{4}\s?(?:\d{1,2}\:\d{2}(?:\:\d{2})?\s?[a|p]?m?)?/i, 536 normal : function(v) { 537 if(!this.pattern.test(v)) {return 0;} 538 var r = v.match(/^(\d{2})\/(\d{2})\/(\d{4})\s?(?:(\d{1,2})\:(\d{2})(?:\:(\d{2}))?\s?([a|p]?m?))?/i); 539 var yr_num = r[3]; 540 var mo_num = parseInt(r[1],10)-1; 541 var day_num = r[2]; 542 var hr_num = r[4] ? r[4] : 0; 543 if(r[7]) { 544 var chr = parseInt(r[4],10); 545 if(r[7].toLowerCase().indexOf('p') !== -1) { 546 hr_num = chr < 12 ? chr + 12 : chr; 547 } else if(r[7].toLowerCase().indexOf('a') !== -1) { 548 hr_num = chr < 12 ? chr : 0; 549 } 550 } 551 var min_num = r[5] ? r[5] : 0; 552 var sec_num = r[6] ? r[6] : 0; 553 return new Date(yr_num, mo_num, day_num, hr_num, min_num, sec_num, 0).valueOf(); 554 }}), 555 new TableKit.Sortable.Type('date-eu',{ 556 pattern : /^\d{2}-\d{2}-\d{4}/i, 557 normal : function(v) { 558 if(!this.pattern.test(v)) {return 0;} 559 var r = v.match(/^(\d{2})-(\d{2})-(\d{4})/); 560 var yr_num = r[3]; 561 var mo_num = parseInt(r[2],10)-1; 562 var day_num = r[1]; 563 return new Date(yr_num, mo_num, day_num).valueOf(); 564 }}), 565 new TableKit.Sortable.Type('date-iso',{ 566 pattern : /[\d]{4}-[\d]{2}-[\d]{2}(?:T[\d]{2}\:[\d]{2}(?:\:[\d]{2}(?:\.[\d]+)?)?(Z|([-+][\d]{2}:[\d]{2})?)?)?/, // 2005-03-26T19:51:34Z 567 normal : function(v) { 568 if(!this.pattern.test(v)) {return 0;} 569 var d = v.match(/([\d]{4})(-([\d]{2})(-([\d]{2})(T([\d]{2}):([\d]{2})(:([\d]{2})(\.([\d]+))?)?(Z|(([-+])([\d]{2}):([\d]{2})))?)?)?)?/); 570 var offset = 0; 571 var date = new Date(d[1], 0, 1); 572 if (d[3]) { date.setMonth(d[3] - 1) ;} 573 if (d[5]) { date.setDate(d[5]); } 574 if (d[7]) { date.setHours(d[7]); } 575 if (d[8]) { date.setMinutes(d[8]); } 576 if (d[10]) { date.setSeconds(d[10]); } 577 if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); } 578 if (d[14]) { 579 offset = (Number(d[16]) * 60) + Number(d[17]); 580 offset *= ((d[15] === '-') ? 1 : -1); 581 } 582 offset -= date.getTimezoneOffset(); 583 if(offset !== 0) { 584 var time = (Number(date) + (offset * 60 * 1000)); 585 date.setTime(Number(time)); 586 } 587 return date.valueOf(); 588 }}), 589 new TableKit.Sortable.Type('date',{ 590 pattern: /^(?:sun|mon|tue|wed|thu|fri|sat)\,\s\d{1,2}\s(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\s\d{4}(?:\s\d{2}\:\d{2}(?:\:\d{2})?(?:\sGMT(?:[+-]\d{4})?)?)?/i, //Mon, 18 Dec 1995 17:28:35 GMT 591 compare : function(a,b) { // must be standard javascript date format 592 if(a && b) { 593 return TableKit.Sortable.Type.compare(new Date(a),new Date(b)); 594 } else { 595 return TableKit.Sortable.Type.compare(a ? 1 : 0, b ? 1 : 0); 596 } 597 }}), 598 new TableKit.Sortable.Type('time',{ 599 pattern : /^\d{1,2}\:\d{2}(?:\:\d{2})?(?:\s[a|p]m)?$/i, 600 compare : function(a,b) { 601 var d = new Date(); 602 var ds = d.getMonth() + "/" + d.getDate() + "/" + d.getFullYear() + " "; 603 return TableKit.Sortable.Type.compare(new Date(ds + a),new Date(ds + b)); 604 }}), 605 new TableKit.Sortable.Type('currency',{ 606 pattern : /^[$����]/, // dollar,pound,yen,euro,generic currency symbol 607 normal : function(v) { 608 return v ? parseFloat(v.replace(/[^-\d\.]/g,'')) : 0; 609 }}) 610); 611 612TableKit.Resizable = { 613 init : function(elm, options){ 614 var table = $(elm); 615 if(table.tagName !== "TABLE") {return;} 616 TableKit.register(table,Object.extend(options || {},{resizable:true})); 617 var cells = TableKit.getHeaderCells(table); 618 cells.each(function(c){ 619 c = $(c); 620 Event.observe(c, 'mouseover', TableKit.Resizable.initDetect); 621 Event.observe(c, 'mouseout', TableKit.Resizable.killDetect); 622 }); 623 }, 624 resize : function(table, index, w) { 625 var cell; 626 if(typeof index === 'number') { 627 if(!table || (table.tagName && table.tagName !== "TABLE")) {return;} 628 table = $(table); 629 index = Math.min(table.rows[0].cells.length, index); 630 index = Math.max(1, index); 631 index -= 1; 632 cell = (table.tHead && table.tHead.rows.length > 0) ? $(table.tHead.rows[table.tHead.rows.length-1].cells[index]) : $(table.rows[0].cells[index]); 633 } else { 634 cell = $(index); 635 table = table ? $(table) : cell.up('table'); 636 index = TableKit.getCellIndex(cell); 637 } 638 var pad = parseInt(cell.getStyle('paddingLeft'),10) + parseInt(cell.getStyle('paddingRight'),10); 639 w = Math.max(w-pad, TableKit.option('minWidth', table.id)[0]); 640 641 cell.setStyle({'width' : w + 'px'}); 642 }, 643 initDetect : function(e) { 644 e = TableKit.e(e); 645 var cell = Event.element(e); 646 Event.observe(cell, 'mousemove', TableKit.Resizable.detectHandle); 647 Event.observe(cell, 'mousedown', TableKit.Resizable.startResize); 648 }, 649 detectHandle : function(e) { 650 e = TableKit.e(e); 651 var cell = Event.element(e); 652 if(TableKit.Resizable.pointerPos(cell,Event.pointerX(e),Event.pointerY(e))){ 653 cell.addClassName(TableKit.option('resizeOnHandleClass', cell.up('table').id)[0]); 654 TableKit.Resizable._onHandle = true; 655 } else { 656 cell.removeClassName(TableKit.option('resizeOnHandleClass', cell.up('table').id)[0]); 657 TableKit.Resizable._onHandle = false; 658 } 659 }, 660 killDetect : function(e) { 661 e = TableKit.e(e); 662 TableKit.Resizable._onHandle = false; 663 var cell = Event.element(e); 664 Event.stopObserving(cell, 'mousemove', TableKit.Resizable.detectHandle); 665 Event.stopObserving(cell, 'mousedown', TableKit.Resizable.startResize); 666 cell.removeClassName(TableKit.option('resizeOnHandleClass', cell.up('table').id)[0]); 667 }, 668 startResize : function(e) { 669 e = TableKit.e(e); 670 if(!TableKit.Resizable._onHandle) {return;} 671 var cell = Event.element(e); 672 Event.stopObserving(cell, 'mousemove', TableKit.Resizable.detectHandle); 673 Event.stopObserving(cell, 'mousedown', TableKit.Resizable.startResize); 674 Event.stopObserving(cell, 'mouseout', TableKit.Resizable.killDetect); 675 TableKit.Resizable._cell = cell; 676 var table = cell.up('table'); 677 TableKit.Resizable._tbl = table; 678 if(TableKit.option('showHandle', table.id)[0]) { 679 TableKit.Resizable._handle = $(document.createElement('div')).addClassName('resize-handle').setStyle({ 680 'top' : cell.cumulativeOffset()[1] + 'px', 681 'left' : Event.pointerX(e) + 'px', 682 'height' : table.getDimensions().height + 'px' 683 }); 684 document.body.style.cursor = 'w-resize'; 685 document.body.appendChild(TableKit.Resizable._handle); 686 } 687 Event.observe(document, 'mousemove', TableKit.Resizable.drag); 688 Event.observe(document, 'mouseup', TableKit.Resizable.endResize); 689 Event.stop(e); 690 }, 691 endResize : function(e) { 692 e = TableKit.e(e); 693 var cell = TableKit.Resizable._cell; 694 TableKit.Resizable.resize(null, cell, (Event.pointerX(e) - cell.cumulativeOffset()[0])); 695 Event.stopObserving(document, 'mousemove', TableKit.Resizable.drag); 696 Event.stopObserving(document, 'mouseup', TableKit.Resizable.endResize); 697 if(TableKit.option('showHandle', TableKit.Resizable._tbl.id)[0]) { 698 $$('div.resize-handle').each(function(elm){ 699 document.body.removeChild(elm); 700 }); 701 } 702 Event.observe(cell, 'mouseout', TableKit.Resizable.killDetect); 703 document.body.style.cursor = 'auto'; 704 TableKit.Resizable._tbl = TableKit.Resizable._handle = TableKit.Resizable._cell = null; 705 Event.stop(e); 706 }, 707 drag : function(e) { 708 e = TableKit.e(e); 709 if(TableKit.Resizable._handle === null) { 710 try { 711 TableKit.Resizable.resize(TableKit.Resizable._tbl, TableKit.Resizable._cell, (Event.pointerX(e) - TableKit.Resizable._cell.cumulativeOffset()[0])); 712 } catch(e) {} 713 } else { 714 TableKit.Resizable._handle.setStyle({'left' : Event.pointerX(e) + 'px'}); 715 } 716 return false; 717 }, 718 pointerPos : function(element, x, y) { 719 var offset = $(element).cumulativeOffset(); 720 return (y >= offset[1] && 721 y < offset[1] + element.offsetHeight && 722 x >= offset[0] + element.offsetWidth - 5 && 723 x < offset[0] + element.offsetWidth); 724 }, 725 _onHandle : false, 726 _cell : null, 727 _tbl : null, 728 _handle : null 729}; 730 731 732TableKit.Editable = { 733 init : function(elm, options){ 734 var table = $(elm); 735 if(table.tagName !== "TABLE") {return;} 736 TableKit.register(table,Object.extend(options || {},{editable:true})); 737 Event.observe(table.tBodies[0], 'click', TableKit.Editable._editCell); 738 }, 739 _editCell : function(e) { 740 e = TableKit.e(e); 741 var cell = Event.findElement(e,'td'); 742 if(cell) { 743 TableKit.Editable.editCell(null, cell, null, e); 744 } else { 745 return false; 746 } 747 }, 748 editCell : function(table, index, cindex, event) { 749 var cell, row; 750 if(typeof index === 'number') { 751 if(!table || (table.tagName && table.tagName !== "TABLE")) {return;} 752 table = $(table); 753 index = Math.min(table.tBodies[0].rows.length, index); 754 index = Math.max(1, index); 755 index -= 1; 756 cindex = Math.min(table.rows[0].cells.length, cindex); 757 cindex = Math.max(1, cindex); 758 cindex -= 1; 759 row = $(table.tBodies[0].rows[index]); 760 cell = $(row.cells[cindex]); 761 } else { 762 cell = $(event ? Event.findElement(event, 'td') : index); 763 table = (table && table.tagName && table.tagName !== "TABLE") ? $(table) : cell.up('table'); 764 row = cell.up('tr'); 765 } 766 var op = TableKit.option('noEditClass', table.id); 767 if(cell.hasClassName(op.noEditClass)) {return;} 768 769 var head = $(TableKit.getHeaderCells(table, cell)[TableKit.getCellIndex(cell)]); 770 if(head.hasClassName(op.noEditClass)) {return;} 771 772 var data = TableKit.getCellData(cell); 773 if(data.active) {return;} 774 data.htmlContent = cell.innerHTML; 775 var ftype = TableKit.Editable.getCellEditor(null,null,head); 776 ftype.edit(cell, event); 777 data.active = true; 778 }, 779 getCellEditor : function(cell, table, head) { 780 var head = head ? head : $(TableKit.getHeaderCells(table, cell)[TableKit.getCellIndex(cell)]); 781 var ftype = TableKit.Editable.types['text-input']; 782 if(head.id && TableKit.Editable.types[head.id]) { 783 ftype = TableKit.Editable.types[head.id]; 784 } else { 785 var n = $w(head.className).detect(function(n){ 786 return (TableKit.Editable.types[n]) ? true : false; 787 }); 788 ftype = n ? TableKit.Editable.types[n] : ftype; 789 } 790 return ftype; 791 }, 792 types : {}, 793 addCellEditor : function(o) { 794 if(o && o.name) { TableKit.Editable.types[o.name] = o; } 795 } 796}; 797 798TableKit.Editable.CellEditor = Class.create(); 799TableKit.Editable.CellEditor.prototype = { 800 initialize : function(name, options){ 801 var langOK; 802 var langCancel; 803 804 langOK = document.getElementById("table_kit_OK").innerHTML; 805 langCancel = document.getElementById("table_kit_Cancel").innerHTML; 806 807 this.name = name; 808 this.options = Object.extend({ 809 element : 'input', 810 attributes : {name : 'value', type : 'text'}, 811 selectOptions : [], 812 showSubmit : true, 813 submitText : langOK, 814 showCancel : true, 815 cancelText : langCancel, 816 ajaxURI : null, 817 ajaxOptions : null 818 }, options || {}); 819 }, 820 edit : function(cell) { 821 cell = $(cell); 822 var op = this.options; 823 var table = cell.up('table'); 824 825 var form = $(document.createElement("form")); 826 form.id = cell.id + '-form'; 827 form.addClassName(TableKit.option('formClassName', table.id)[0]); 828 form.onsubmit = this._submit.bindAsEventListener(this); 829 830 var field = document.createElement(op.element); 831 $H(op.attributes).each(function(v){ 832 field[v.key] = v.value; 833 }); 834 switch(op.element) { 835 case 'input': 836 case 'textarea': 837 field.value = TableKit.getCellText(cell); 838 break; 839 840 case 'select': 841 var txt = TableKit.getCellText(cell); 842 $A(op.selectOptions).each(function(v){ 843 field.options[field.options.length] = new Option(v[0], v[1]); 844 if(txt === v[1]) { 845 field.options[field.options.length-1].selected = 'selected'; 846 } 847 }); 848 break; 849 } 850 form.appendChild(field); 851 if(op.element === 'textarea') { 852 form.appendChild(document.createElement("br")); 853 } 854 if(op.showSubmit) { 855 var okButton = document.createElement("input"); 856 okButton.type = "submit"; 857 okButton.value = op.submitText; 858 okButton.className = 'editor_ok_button'; 859 form.appendChild(okButton); 860 } 861 if(op.showCancel) { 862 var cancelLink = document.createElement("a"); 863 cancelLink.href = "#"; 864 cancelLink.appendChild(document.createTextNode(op.cancelText)); 865 cancelLink.onclick = this._cancel.bindAsEventListener(this); 866 cancelLink.className = 'editor_cancel'; 867 form.appendChild(cancelLink); 868 } 869 cell.innerHTML = ''; 870 cell.appendChild(form); 871 }, 872 _submit : function(e) { 873 var cell = Event.findElement(e,'td'); 874 var form = Event.findElement(e,'form'); 875 Event.stop(e); 876 this.submit(cell,form); 877 }, 878 submit : function(cell, form) { 879 var op = this.options; 880 form = form ? form : cell.down('form'); 881 var head = $(TableKit.getHeaderCells(null, cell)[TableKit.getCellIndex(cell)]); 882 var row = cell.up('tr'); 883 var table = cell.up('table'); 884 var auser = document.getElementById("currentuser"); 885 var currentID = document.getElementById("currentID"); 886 var s = '&row=' + (TableKit.getRowIndex(row)+1) + '&cell=' + (TableKit.getCellIndex(cell)+1) + '&id=' + row.id + '&field=' + head.id + '&' + Form.serialize(form) + '&usr=' + auser.innerHTML + '¤tID=' + currentID.innerHTML; 887 this.ajax = new Ajax.Updater(cell, op.ajaxURI || TableKit.option('editAjaxURI', table.id)[0], Object.extend(op.ajaxOptions || TableKit.option('editAjaxOptions', table.id)[0], { 888// this.ajax = new Ajax.Updater(cell, op.ajaxURI , Object.extend(op.ajaxOptions, { 889 postBody : s, 890 onComplete : function() { 891 var data = TableKit.getCellData(cell); 892 data.active = false; 893 data.refresh = true; // mark cell cache for refreshing, in case cell contents has changed and sorting is applied 894 } 895 })); 896 }, 897 _cancel : function(e) { 898 var cell = Event.findElement(e,'td'); 899 Event.stop(e); 900 this.cancel(cell); 901 }, 902 cancel : function(cell) { 903 this.ajax = null; 904 var data = TableKit.getCellData(cell); 905 cell.innerHTML = data.htmlContent; 906 data.htmlContent = ''; 907 data.active = false; 908 }, 909 ajax : null 910}; 911 912TableKit.Editable.textInput = function(n,attributes) { 913 TableKit.Editable.addCellEditor(new TableKit.Editable.CellEditor(n, { 914 element : 'input', 915 attributes : Object.extend({name : 'value', type : 'text'}, attributes||{}) 916 })); 917}; 918TableKit.Editable.textInput('text-input'); 919 920TableKit.Editable.multiLineInput = function(n,attributes) { 921 TableKit.Editable.addCellEditor(new TableKit.Editable.CellEditor(n, { 922 element : 'textarea', 923 attributes : Object.extend({name : 'value', rows : '20', cols : '60'}, attributes||{}) 924 })); 925}; 926TableKit.Editable.multiLineInput('multi-line-input'); 927 928TableKit.Editable.selectInput = function(n,attributes,selectOptions) { 929 TableKit.Editable.addCellEditor(new TableKit.Editable.CellEditor(n, { 930 element : 'select', 931 attributes : Object.extend({name : 'value'}, attributes||{}), 932 'selectOptions' : selectOptions 933 })); 934}; 935 936/* 937TableKit.Bench = { 938 bench : [], 939 start : function(){ 940 TableKit.Bench.bench[0] = new Date().getTime(); 941 }, 942 end : function(s){ 943 TableKit.Bench.bench[1] = new Date().getTime(); 944 alert(s + ' ' + ((TableKit.Bench.bench[1]-TableKit.Bench.bench[0])/1000)+' seconds.') //console.log(s + ' ' + ((TableKit.Bench.bench[1]-TableKit.Bench.bench[0])/1000)+' seconds.') 945 TableKit.Bench.bench = []; 946 } 947} */ 948 949document.observe("dom:loaded", TableKit.load); 950