1 /* 2 * JBoss, Home of Professional Open Source 3 * Copyright ${year}, Red Hat, Inc. and individual contributors 4 * by the @authors tag. See the copyright.txt in the distribution for a 5 * full listing of individual contributors. 6 * 7 * This is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU Lesser General Public License as 9 * published by the Free Software Foundation; either version 2.1 of 10 * the License, or (at your option) any later version. 11 * 12 * This software is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this software; if not, write to the Free 19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 21 */ 22 (function($, rf) { 23 rf.utils = rf.utils || {}; 24 25 rf.utils.addCSSText = function(cssText, elementId) { 26 var style = $("<style></style>").attr({type: 'text/css', id: elementId}).appendTo("head"); 27 try { 28 style.html(cssText); 29 } catch (e) { 30 //IE 31 style[0].styleSheet.cssText = cssText; 32 } 33 }; 34 35 rf.utils.Ranges = function() { 36 this.ranges = []; 37 }; 38 39 rf.utils.Ranges.prototype = { 40 41 add: function(index) { 42 var i = 0; 43 while (i < this.ranges.length && index >= this.ranges[i++][1]); 44 i--; 45 if (this.ranges[i - 1] && index == (this.ranges[i - 1][1] + 1)) { 46 if (index == (this.ranges[i][0] - 1)) { 47 this.ranges[i - 1][1] = this.ranges[i][1]; 48 this.ranges.splice(i, 1); 49 } else { 50 this.ranges[i - 1][1]++; 51 } 52 } else { 53 if (this.ranges[i]) { 54 if (this.ranges[i] && index == (this.ranges[i][0] - 1)) { 55 this.ranges[i][0]--; 56 } else { 57 if (index == (this.ranges[i][1] + 1)) { 58 this.ranges[i][1]++; 59 } else { 60 if (index < this.ranges[i][1]) { 61 this.ranges.splice(i, 0, [index, index]); 62 } else { 63 this.ranges.splice(i + 1, 0, [index, index]); 64 } 65 } 66 } 67 } else { 68 this.ranges.splice(i, 0, [index, index]); 69 } 70 } 71 }, 72 73 remove: function(index) { 74 var i = 0; 75 while (i < this.ranges.length && index > this.ranges[i++][1]); 76 i--; 77 if (this.ranges[i]) { 78 if (index == (this.ranges[i][1])) { 79 if (index == (this.ranges[i][0])) { 80 this.ranges.splice(i, 1); 81 } else { 82 this.ranges[i][1]--; 83 } 84 } else { 85 if (index == (this.ranges[i][0])) { 86 this.ranges[i][0]++; 87 } else { 88 this.ranges.splice(i + 1, 0, [index + 1, this.ranges[i][1]]); 89 this.ranges[i][1] = index - 1; 90 } 91 } 92 } 93 }, 94 95 clear: function() { 96 this.ranges = []; 97 }, 98 99 contains: function(index) { 100 var i = 0; 101 while (i < this.ranges.length && index >= this.ranges[i][0]) { 102 if (index >= this.ranges[i][0] && index <= this.ranges[i][1]) { 103 return true; 104 } else { 105 i++; 106 } 107 } 108 return false; 109 }, 110 111 toString: function() { 112 var ret = new Array(this.ranges.length); 113 for (var i = 0; i < this.ranges.length; i++) { 114 ret[i] = this.ranges[i].join(); 115 } 116 return ret.join(";"); 117 } 118 }; 119 120 var WIDTH_CLASS_NAME_BASE = "rf-edt-c-"; 121 var MIN_WIDTH = 20; 122 123 rf.ui = rf.ui || {}; 124 125 rf.ui.ExtendedDataTable = rf.BaseComponent.extendClass({ 126 127 name: "ExtendedDataTable", 128 129 init: function (id, rowCount, ajaxFunction, options) { 130 $super.constructor.call(this, id); 131 this.ranges = new rf.utils.Ranges(); 132 this.rowCount = rowCount; 133 this.ajaxFunction = ajaxFunction; 134 this.options = options || {}; 135 this.element = this.attachToDom(); 136 this.newWidths = {}; 137 this.storeDomReferences(); 138 if (this.options['onready'] && typeof this.options['onready'] == 'function') { 139 rf.Event.bind(this.element, "rich:ready", this.options['onready']); 140 } 141 this.resizeEventName = "resize.rf.edt." + this.id; 142 $(document).ready($.proxy(this.initialize, this)); 143 this.activateResizeListener(); 144 $(this.scrollElement).bind("scroll", $.proxy(this.updateScrollPosition, this)); 145 this.bindHeaderHandlers(); 146 $(this.element).bind("rich:onajaxcomplete", $.proxy(this.ajaxComplete, this)); 147 148 this.resizeData = {}; 149 this.idOfReorderingColumn = ""; 150 this.timeoutId = null; 151 }, 152 153 storeDomReferences: function() { 154 this.dragElement = document.getElementById(this.id + ":d"); 155 this.reorderElement = document.getElementById(this.id + ":r"); 156 this.reorderMarkerElement = document.getElementById(this.id + ":rm"); 157 this.widthInput = document.getElementById(this.id + ":wi"); 158 this.selectionInput = document.getElementById(this.id + ":si"); 159 160 161 this.header = $(this.element).children(".rf-edt-hdr"); 162 this.headerCells = this.header.find(".rf-edt-hdr-c"); 163 this.footerCells = $(this.element).children(".rf-edt-ftr").find(".rf-edt-ftr-c"); 164 this.resizerHolders = this.header.find(".rf-edt-rsz-cntr"); 165 166 this.frozenHeaderPartElement = document.getElementById(this.id + ":frozenHeader"); 167 this.frozenColumnCount = this.frozenHeaderPartElement ? this.frozenHeaderPartElement.children[0].rows[0].cells.length : 0;//TODO Richfaces.firstDescendant; 168 169 this.headerElement = document.getElementById(this.id + ":header"); 170 this.footerElement = document.getElementById(this.id + ":footer"); 171 this.scrollElement = document.getElementById(this.id + ":scrl"); 172 this.scrollContentElement = document.getElementById(this.id + ":scrl-cnt"); 173 174 }, 175 176 getColumnPosition: function(id) { 177 var position; 178 for (var i = 0; i < this.headerCells.length; i++) { 179 if (id == this.headerCells[i].className.match(new RegExp(WIDTH_CLASS_NAME_BASE + "([^\\W]*)"))[1]) { 180 position = i; 181 } 182 } 183 return position; 184 }, 185 186 setColumnPosition: function(id, position) { 187 var colunmsOrder = ""; 188 var before; 189 for (var i = 0; i < this.headerCells.length; i++) { 190 var current = this.headerCells[i].className.match(new RegExp(WIDTH_CLASS_NAME_BASE + "([^\\W]*)"))[1]; 191 if (i == position) { 192 if (before) { 193 colunmsOrder += current + "," + id + ","; 194 } else { 195 colunmsOrder += id + "," + current + ","; 196 } 197 } else { 198 if (id != current) { 199 colunmsOrder += current + ","; 200 } else { 201 before = true; 202 } 203 } 204 } 205 this.ajaxFunction(null, {"rich:columnsOrder" : colunmsOrder}); // TODO Maybe, event model should be used here. 206 }, 207 208 setColumnWidth: function(columnId, width) { 209 width = width + "px"; 210 var $table = $(document.getElementById(this.element.id)); 211 $table.find("." + WIDTH_CLASS_NAME_BASE + columnId).parent().css('width', width); 212 $table.find("." + WIDTH_CLASS_NAME_BASE + columnId).css('width', width); 213 this.newWidths[columnId] = width; 214 var widthsArray = new Array(); 215 for (var id in this.newWidths) { 216 widthsArray.push(id + ":" + this.newWidths[id]); 217 } 218 this.widthInput.value = widthsArray.toString(); 219 this.updateLayout(); 220 this.adjustResizers(); 221 this.ajaxFunction(); // TODO Maybe, event model should be used here. 222 }, 223 224 filter: function(colunmId, filterValue, isClear) { 225 if (typeof(filterValue) == "undefined" || filterValue == null) { 226 filterValue = ""; 227 } 228 var map = {}; 229 map[this.id + "rich:filtering"] = colunmId + ":" + filterValue + ":" + isClear; 230 this.ajaxFunction(null, map); // TODO Maybe, event model should be used here. 231 }, 232 233 clearFiltering: function() { 234 this.filter("", "", true); 235 }, 236 237 sortHandler: function(event) { 238 var sortHandle = $(event.data.sortHandle); 239 var button = sortHandle.find('.rf-edt-srt-btn'); 240 var columnId = button.data('columnid'); 241 var sortOrder = button.hasClass('rf-edt-srt-asc') ? 'descending' : 'ascending'; 242 this.sort(columnId, sortOrder, false); 243 }, 244 245 filterHandler: function(event) { 246 var filterHandle = $(event.data.filterHandle); 247 var columnId = filterHandle.data('columnid'); 248 var filterValue = filterHandle.val(); 249 this.filter(columnId, filterValue, false); 250 }, 251 252 253 sort: function(colunmId, sortOrder, isClear) { 254 if (typeof(sortOrder) == "string") { 255 sortOrder = sortOrder.toLowerCase(); 256 } 257 var map = {} 258 map[this.id + "rich:sorting"] = colunmId + ":" + sortOrder + ":" + isClear; 259 this.ajaxFunction(null, map); // TODO Maybe, event model should be used here. 260 }, 261 262 clearSorting: function() { 263 this.sort("", "", true); 264 }, 265 266 destroy: function() { 267 $(window).unbind("resize", this.updateLayout); 268 $(rf.getDomElement(this.id + ':st')).remove(); 269 $super.destroy.call(this); 270 }, 271 272 bindHeaderHandlers: function() { 273 this.header.find(".rf-edt-rsz").bind("mousedown", $.proxy(this.beginResize, this)); 274 this.headerCells.bind("mousedown", $.proxy(this.beginReorder, this)); 275 var self = this; 276 this.header.find(".rf-edt-c-srt").each(function() { 277 $(this).bind("click", {sortHandle: this}, $.proxy(self.sortHandler, self)); 278 }); 279 this.header.find(".rf-edt-flt-i").each(function() { 280 $(this).bind("blur", {filterHandle: this}, $.proxy(self.filterHandler, self)); 281 }); 282 }, 283 284 updateLayout: function() { 285 this.deActivateResizeListener(); 286 this.headerCells.height("auto"); 287 var headerCellHeight = 0; 288 this.headerCells.each(function() { 289 if (this.clientHeight > headerCellHeight) { 290 headerCellHeight = this.clientHeight; 291 } 292 }); 293 this.headerCells.height(headerCellHeight + "px"); 294 this.footerCells.height("auto"); 295 var footerCellHeight = 0; 296 this.footerCells.each(function() { 297 if (this.clientHeight > footerCellHeight) { 298 footerCellHeight = this.clientHeight; 299 } 300 }); 301 this.footerCells.height(footerCellHeight + "px"); 302 this.contentDivElement.css('width', 'auto'); 303 var offsetWidth = this.frozenHeaderPartElement ? this.frozenHeaderPartElement.offsetWidth : 0; 304 var width = Math.max(0, this.element.clientWidth - offsetWidth); 305 if (width) { 306 this.parts.each(function() { 307 this.style.width = "auto"; 308 }); 309 var contentWidth = this.parts.width(); 310 if (contentWidth > width) { 311 this.contentDivElement.css('width', width + 'px'); 312 } 313 this.contentDivElement.css('display', 'block'); 314 // update scroller and scroll-content 315 if (contentWidth > width) { 316 this.parts.each(function() { 317 this.style.width = width + "px"; 318 }); 319 this.scrollElement.style.display = "block"; 320 this.scrollElement.style.overflowX = "scroll"; 321 this.scrollElement.style.width = width + "px"; 322 this.scrollContentElement.style.width = contentWidth + "px"; 323 this.updateScrollPosition(); 324 } else { 325 this.parts.each(function() { 326 this.style.width = ""; 327 }); 328 this.scrollElement.style.display = "none"; 329 } 330 } else { 331 this.contentDivElement.css('display', 'none'); 332 } 333 var height = this.element.clientHeight; 334 var el = this.element.firstChild; 335 while (el && (!el.nodeName || el.nodeName.toUpperCase() != "TABLE")) { 336 if (el.nodeName && el.nodeName.toUpperCase() == "DIV" && el != this.bodyElement) { 337 height -= el.offsetHeight; 338 } 339 el = el.nextSibling; 340 } 341 if (this.bodyElement.offsetHeight > height || !this.contentElement) { 342 this.bodyElement.style.height = height + "px"; 343 } 344 this.activateResizeListener(); 345 }, 346 347 adjustResizers: function() { //For IE7 only. 348 var scrollLeft = this.scrollElement ? this.scrollElement.scrollLeft : 0; 349 var clientWidth = this.element.clientWidth - 3; 350 var i = 0; 351 for (; i < this.frozenColumnCount; i++) { 352 if (clientWidth > 0) { 353 this.resizerHolders[i].style.display = "none"; 354 this.resizerHolders[i].style.display = ""; 355 clientWidth -= this.resizerHolders[i].offsetWidth; 356 } 357 if (clientWidth <= 0) { 358 this.resizerHolders[i].style.display = "none"; 359 } 360 } 361 scrollLeft -= 3; 362 for (; i < this.resizerHolders.length; i++) { 363 if (clientWidth > 0) { 364 this.resizerHolders[i].style.display = "none"; 365 if (scrollLeft > 0) { 366 this.resizerHolders[i].style.display = ""; 367 scrollLeft -= this.resizerHolders[i].offsetWidth; 368 if (scrollLeft > 0) { 369 this.resizerHolders[i].style.display = "none"; 370 } else { 371 clientWidth += scrollLeft; 372 } 373 } else { 374 this.resizerHolders[i].style.display = ""; 375 clientWidth -= this.resizerHolders[i].offsetWidth; 376 } 377 } 378 if (clientWidth <= 0) { 379 this.resizerHolders[i].style.display = "none"; 380 } 381 } 382 }, 383 384 updateScrollPosition: function() { 385 if (this.scrollElement) { 386 var scrollLeft = this.scrollElement.scrollLeft; 387 this.parts.each(function() { 388 this.scrollLeft = scrollLeft; 389 }); 390 } 391 this.adjustResizers(); 392 }, 393 394 initialize: function() { 395 this.deActivateResizeListener(); 396 if (! $(this.element).is(":visible")) { 397 this.showOffscreen(this.element); 398 } 399 this.bodyElement = document.getElementById(this.id + ":b"); 400 this.bodyElement.tabIndex = -1; //TODO don't use tabIndex. 401 this.contentDivElement = $(this.bodyElement).find('.rf-edt-cnt'); 402 var bodyJQuery = $(this.bodyElement); 403 this.contentElement = bodyJQuery.children("div:not(.rf-edt-ndt):first")[0]; 404 if (this.contentElement) { 405 this.spacerElement = this.contentElement.children[0];//TODO this.marginElement = Richfaces.firstDescendant(this.contentElement); 406 this.dataTableElement = this.contentElement.lastChild;//TODO this.dataTableElement = Richfaces.lastDescendant(this.contentElement); 407 this.tbodies = $(document.getElementById(this.id + ":tbf")).add(document.getElementById(this.id + ":tbn")); 408 this.rows = this.tbodies[0].rows.length; 409 this.rowHeight = this.dataTableElement.offsetHeight / this.rows; 410 if (this.rowCount != this.rows) { 411 this.contentElement.style.height = (this.rowCount * this.rowHeight) + "px"; 412 } 413 bodyJQuery.bind("scroll", $.proxy(this.bodyScrollListener, this)); 414 if (this.options.selectionMode != "none") { 415 this.tbodies.bind("click", $.proxy(this.selectionClickListener, this)); 416 bodyJQuery.bind(window.opera ? "keypress" : "keydown", $.proxy(this.selectionKeyDownListener, this)); 417 this.initializeSelection(); 418 } 419 } else { 420 this.spacerElement = null; 421 this.dataTableElement = null; 422 } 423 var edt = this.element; 424 this.parts = $(this.element).find(".rf-edt-cnt, .rf-edt-ftr-cnt").filter(function() { 425 return $(this).parents('.rf-edt').get(0) === edt; 426 }); 427 this.updateLayout(); 428 this.updateScrollPosition(); //TODO Restore horizontal scroll position 429 if ($(this.element).data('offscreenElements')) { 430 this.hideOffscreen(this.element); 431 } 432 this.activateResizeListener(); 433 $(this.element).trigger("rich:ready", this); 434 }, 435 436 showOffscreen: function(element) { 437 var $element = $(element); 438 var offscreenElements = $element.parents(":not(:visible)").addBack().toArray().reverse(); 439 var that = this; 440 $.each(offscreenElements, function() { 441 $this = $(this); 442 if ($this.css('display') === 'none') { 443 that.showOffscreenElement($(this)); 444 } 445 }) 446 $element.data('offscreenElements', offscreenElements); 447 }, 448 449 hideOffscreen: function(element) { 450 var $element = $(element); 451 var offscreenElements = $element.data('offscreenElements'); 452 var that = this; 453 $.each(offscreenElements, function() { 454 $this = $(this); 455 if ($this.data('offscreenOldValues')) { 456 that.hideOffscreenElement($(this)); 457 } 458 }) 459 $element.removeData('offscreenElements'); 460 }, 461 462 showOffscreenElement: function($element) { 463 var offscreenOldValues = {}; 464 offscreenOldValues.oldPosition = $element.css('position'); 465 offscreenOldValues.oldLeft = $element.css('left'); 466 offscreenOldValues.oldDisplay = $element.css('display'); 467 $element.css('position', 'absolute'); 468 $element.css('left', '-10000'); 469 $element.css('display', 'block'); 470 $element.data('offscreenOldValues', offscreenOldValues); 471 }, 472 473 hideOffscreenElement: function($element) { 474 var offscreenOldValues = $element.data('offscreenOldValues'); 475 $element.css('display', offscreenOldValues.oldDisplay); 476 $element.css('left', offscreenOldValues.oldLeft); 477 $element.css('position', offscreenOldValues.oldPosition); 478 $element.removeData('offscreenOldValues'); 479 }, 480 481 drag: function(event) { 482 $(this.dragElement).setPosition({left:Math.max(this.resizeData.left + MIN_WIDTH, event.pageX)}); 483 return false; 484 }, 485 486 beginResize: function(event) { 487 var id = event.currentTarget.parentNode.className.match(new RegExp(WIDTH_CLASS_NAME_BASE + "([^\\W]*)"))[1]; 488 this.resizeData = { 489 id : id, 490 left : $(event.currentTarget).parent().offset().left 491 }; 492 this.dragElement.style.height = this.element.offsetHeight + "px"; 493 $(this.dragElement).setPosition({top:$(this.element).offset().top, left:event.pageX}); 494 this.dragElement.style.display = "block"; 495 $(document).bind("mousemove", $.proxy(this.drag, this)); 496 $(document).one("mouseup", $.proxy(this.endResize, this)); 497 return false; 498 }, 499 500 endResize: function(event) { 501 $(document).unbind("mousemove", this.drag); 502 this.dragElement.style.display = "none"; 503 var width = Math.max(MIN_WIDTH, event.pageX - this.resizeData.left); 504 this.setColumnWidth(this.resizeData.id, width); 505 }, 506 507 reorder: function(event) { 508 $(this.reorderElement).setPosition(event, {offset:[5,5]}); 509 this.reorderElement.style.display = "block"; 510 return false; 511 }, 512 513 beginReorder: function(event) { 514 if (!$(event.target).is("a, img, :input")) { 515 this.idOfReorderingColumn = event.currentTarget.className.match(new RegExp(WIDTH_CLASS_NAME_BASE + "([^\\W]*)"))[1]; 516 $(document).bind("mousemove", $.proxy(this.reorder, this)); 517 this.headerCells.bind("mouseover", $.proxy(this.overReorder, this)); 518 $(document).one("mouseup", $.proxy(this.cancelReorder, this)); 519 return false; 520 } 521 }, 522 523 overReorder: function(event) { 524 if (this.idOfReorderingColumn != event.currentTarget.className.match(new RegExp(WIDTH_CLASS_NAME_BASE + "([^\\W]*)"))[1]) { 525 var eventElement = $(event.currentTarget); 526 var offset = eventElement.offset(); 527 $(this.reorderMarkerElement).setPosition({top:offset.top + eventElement.height(), left:offset.left - 5}); 528 this.reorderMarkerElement.style.display = "block"; 529 eventElement.one("mouseout", $.proxy(this.outReorder, this)); 530 eventElement.one("mouseup", $.proxy(this.endReorder, this)); 531 } 532 }, 533 534 outReorder: function(event) { 535 this.reorderMarkerElement.style.display = ""; 536 $(event.currentTarget).unbind("mouseup", this.endReorder); 537 }, 538 539 endReorder: function(event) { 540 this.reorderMarkerElement.style.display = ""; 541 $(event.currentTarget).unbind("mouseout", this.outReorder); 542 var id = event.currentTarget.className.match(new RegExp(WIDTH_CLASS_NAME_BASE + "([^\\W]*)"))[1]; 543 var colunmsOrder = ""; 544 var _this = this; 545 this.headerCells.each(function() { 546 var i = this.className.match(new RegExp(WIDTH_CLASS_NAME_BASE + "([^\\W]*)"))[1]; 547 if (i == id) { 548 colunmsOrder += _this.idOfReorderingColumn + "," + id + ","; 549 } else if (i != _this.idOfReorderingColumn) { 550 colunmsOrder += i + ","; 551 } 552 }); 553 this.ajaxFunction(event, {"rich:columnsOrder" : colunmsOrder}); // TODO Maybe, event model should be used here. 554 }, 555 556 cancelReorder: function(event) { 557 $(document).unbind("mousemove", this.reorder); 558 this.headerCells.unbind("mouseover", this.overReorder); 559 this.reorderElement.style.display = "none"; 560 }, 561 562 loadData: function(event) { 563 var clientFirst = Math.round((this.bodyElement.scrollTop + this.bodyElement.clientHeight / 2) / this.rowHeight - this.rows / 2); 564 if (clientFirst <= 0) { 565 clientFirst = 0; 566 } else { 567 clientFirst = Math.min(this.rowCount - this.rows, clientFirst); 568 } 569 this.ajaxFunction(event, {"rich:clientFirst" : clientFirst});// TODO Maybe, event model should be used here. 570 }, 571 572 bodyScrollListener: function(event) { 573 if (this.timeoutId) { 574 window.clearTimeout(this.timeoutId); 575 this.timeoutId = null; 576 } 577 if (Math.max(event.currentTarget.scrollTop - this.rowHeight, 0) < this.spacerElement.offsetHeight 578 || Math.min(event.currentTarget.scrollTop + this.rowHeight + event.currentTarget.clientHeight, event.currentTarget.scrollHeight) > this.spacerElement.offsetHeight + this.dataTableElement.offsetHeight) { 579 var _this = this; 580 this.timeoutId = window.setTimeout(function (event) { 581 _this.loadData(event) 582 }, 1000); 583 } 584 }, 585 586 showActiveRow: function() { 587 if (this.bodyElement.scrollTop > this.activeIndex * this.rowHeight + this.spacerElement.offsetHeight) { //UP 588 this.bodyElement.scrollTop = Math.max(this.bodyElement.scrollTop - this.rowHeight, 0); 589 } else if (this.bodyElement.scrollTop + this.bodyElement.clientHeight 590 < (this.activeIndex + 1) * this.rowHeight + this.spacerElement.offsetHeight) { //DOWN 591 this.bodyElement.scrollTop = Math.min(this.bodyElement.scrollTop + this.rowHeight, this.bodyElement.scrollHeight - this.bodyElement.clientHeight); 592 } 593 }, 594 595 selectRow: function(index) { 596 this.ranges.add(index); 597 for (var i = 0; i < this.tbodies.length; i++) { 598 $(this.tbodies[i].rows[index]).addClass("rf-edt-r-sel"); 599 } 600 }, 601 602 deselectRow: function (index) { 603 this.ranges.remove(index); 604 for (var i = 0; i < this.tbodies.length; i++) { 605 $(this.tbodies[i].rows[index]).removeClass("rf-edt-r-sel"); 606 } 607 }, 608 609 setActiveRow: function (index) { 610 if (typeof this.activeIndex == "number") { 611 for (var i = 0; i < this.tbodies.length; i++) { 612 $(this.tbodies[i].rows[this.activeIndex]).removeClass("rf-edt-r-act"); 613 } 614 615 } 616 this.activeIndex = index; 617 for (var i = 0; i < this.tbodies.length; i++) { 618 $(this.tbodies[i].rows[this.activeIndex]).addClass("rf-edt-r-act"); 619 } 620 }, 621 622 resetShiftRow: function () { 623 if (typeof this.shiftIndex == "number") { 624 for (var i = 0; i < this.tbodies.length; i++) { 625 $(this.tbodies[i].rows[this.shiftIndex]).removeClass("rf-edt-r-sht"); 626 } 627 628 } 629 this.shiftIndex = null; 630 }, 631 632 setShiftRow: function (index) { 633 this.resetShiftRow(); 634 this.shiftIndex = index; 635 if (typeof index == "number") { 636 for (var i = 0; i < this.tbodies.length; i++) { 637 $(this.tbodies[i].rows[this.shiftIndex]).addClass("rf-edt-r-sht"); 638 } 639 } 640 }, 641 642 initializeSelection: function() { 643 this.ranges.clear(); 644 var strings = this.selectionInput.value.split("|"); 645 this.activeIndex = strings[1] || null; 646 this.shiftIndex = strings[2] || null; 647 this.selectionFlag = null; 648 var rows = this.tbodies[0].rows; 649 for (var i = 0; i < rows.length; i++) { 650 var row = $(rows[i]); 651 if (row.hasClass("rf-edt-r-sel")) { 652 this.ranges.add(row[0].rowIndex) 653 } 654 if (row.hasClass("rf-edt-r-act")) { 655 this.activeIndex = row[0].rowIndex; 656 } 657 if (row.hasClass("rf-edt-r-sht")) { 658 this.shiftIndex = row[0].rowIndex; 659 } 660 } 661 this.writeSelection(); 662 }, 663 664 writeSelection: function() { 665 this.selectionInput.value = [this.ranges, this.activeIndex, this.shiftIndex, this.selectionFlag].join("|"); 666 }, 667 668 selectRows: function(range) { 669 if (typeof range == "number") { 670 range = [range, range]; 671 } 672 var changed; 673 var i = 0; 674 for (; i < range[0]; i++) { 675 if (this.ranges.contains(i)) { 676 this.deselectRow(i); 677 changed = true; 678 } 679 } 680 for (; i <= range[1]; i++) { 681 if (!this.ranges.contains(i)) { 682 this.selectRow(i); 683 changed = true; 684 } 685 } 686 for (; i < this.rows; i++) { 687 if (this.ranges.contains(i)) { 688 this.deselectRow(i); 689 changed = true; 690 } 691 } 692 this.selectionFlag = typeof this.shiftIndex == "string" ? this.shiftIndex : "x"; 693 return changed; 694 }, 695 696 processSlectionWithShiftKey: function(index) { 697 if (this.shiftIndex == null) { 698 this.setShiftRow(this.activeIndex != null ? this.activeIndex : index); 699 } 700 var range; 701 if ("u" == this.shiftIndex) { 702 range = [0, index]; 703 } else if ("d" == this.shiftIndex) { 704 range = [index, this.rows - 1]; 705 } else if (index >= this.shiftIndex) { 706 range = [this.shiftIndex, index]; 707 } else { 708 range = [index, this.shiftIndex]; 709 } 710 return this.selectRows(range); 711 }, 712 713 onbeforeselectionchange: function (event) { 714 return !this.options.onbeforeselectionchange || this.options.onbeforeselectionchange.call(this.element, event) != false; 715 }, 716 717 onselectionchange: function (event, index, changed) { 718 if (!event.shiftKey) { 719 this.resetShiftRow(); 720 } 721 if (this.activeIndex != index) { 722 this.setActiveRow(index); 723 this.showActiveRow(); 724 } 725 if (changed) { 726 this.writeSelection(); 727 if (this.options.onselectionchange) { 728 this.options.onselectionchange.call(this.element, event); 729 } 730 } 731 }, 732 733 selectionClickListener: function (event) { 734 if (!this.onbeforeselectionchange(event)) { 735 return; 736 } 737 var changed; 738 if (event.shiftKey || event.ctrlKey) { 739 if (window.getSelection) { //TODO Try to find other way. 740 window.getSelection().removeAllRanges(); 741 } else if (document.selection) { 742 document.selection.empty(); 743 } 744 } 745 var tr = event.target; 746 while (this.tbodies.index(tr.parentNode) == -1) { 747 tr = tr.parentNode; 748 } 749 var index = tr.rowIndex; 750 if (this.options.selectionMode == "single" || (this.options.selectionMode != "multipleKeyboardFree" 751 && !event.shiftKey && !event.ctrlKey)) { 752 changed = this.selectRows(index); 753 } else if (this.options.selectionMode == "multipleKeyboardFree" || (!event.shiftKey && event.ctrlKey)) { 754 if (this.ranges.contains(index)) { 755 this.deselectRow(index); 756 } else { 757 this.selectRow(index); 758 } 759 changed = true; 760 } else { 761 changed = this.processSlectionWithShiftKey(index); 762 } 763 this.onselectionchange(event, index, changed); 764 }, 765 766 selectionKeyDownListener: function(event) { 767 if (event.ctrlKey && this.options.selectionMode != "single" && (event.keyCode == 65 || event.keyCode == 97) //Ctrl-A 768 && this.onbeforeselectionchange(event)) { 769 this.selectRows([0, rows]); 770 this.selectionFlag = "a"; 771 this.onselectionchange(event, this.activeIndex, true); //TODO Is there a way to know that selection haven't changed? 772 event.preventDefault(); 773 } else { 774 var index; 775 if (event.keyCode == 38) { //UP 776 index = -1; 777 } else if (event.keyCode == 40) { //DOWN 778 index = 1; 779 } 780 if (index != null && this.onbeforeselectionchange(event)) { 781 if (typeof this.activeIndex == "number") { 782 index += this.activeIndex; 783 if (index >= 0 && index < this.rows) { 784 var changed; 785 if (this.options.selectionMode == "single" || (!event.shiftKey && !event.ctrlKey)) { 786 changed = this.selectRows(index); 787 } else if (event.shiftKey) { 788 changed = this.processSlectionWithShiftKey(index); 789 } 790 this.onselectionchange(event, index, changed); 791 } 792 } 793 event.preventDefault(); 794 } 795 } 796 }, 797 798 ajaxComplete: function (event, data) { 799 this.storeDomReferences(); 800 if (data.reinitializeHeader) { 801 this.bindHeaderHandlers(); 802 this.updateLayout(); 803 } else { 804 this.selectionInput = document.getElementById(this.id + ":si"); 805 if (data.reinitializeBody) { 806 this.rowCount = data.rowCount; 807 this.initialize(); 808 } else if (this.options.selectionMode != "none") { 809 this.initializeSelection(); 810 } 811 if (this.spacerElement) { 812 this.spacerElement.style.height = (data.first * this.rowHeight) + "px"; 813 } 814 } 815 816 // resize columns if necessary 817 var $table = $(document.getElementById(this.element.id)), 818 widthsArray = new Array(); 819 for (var id in this.newWidths) { 820 $table.find("." + WIDTH_CLASS_NAME_BASE + id).css('width', this.newWidths[id]) 821 .parent().css('width', this.newWidths[id]); 822 widthsArray.push(id + ":" + this.newWidths[id]); 823 } 824 this.widthInput.value = widthsArray.toString(); 825 this.updateLayout(); 826 this.adjustResizers(); 827 }, 828 829 activateResizeListener: function() { 830 if (typeof this.resizeEventName !== "undefined") { 831 $(window).on(this.resizeEventName, $.proxy(this.updateLayout, this)); 832 } 833 }, 834 835 deActivateResizeListener: function() { 836 if (typeof this.resizeEventName !== "undefined") { 837 $(window).off(this.resizeEventName); 838 } 839 }, 840 841 contextMenuAttach: function (menu) { 842 var selector = "[id='" + this.element.id + "'] "; 843 selector += (typeof menu.options.targetSelector === 'undefined') 844 ? ".rf-edt-b td" : menu.options.targetSelector; 845 selector = $.trim(selector); 846 rf.Event.bind(selector, menu.options.showEvent, $.proxy(menu.__showHandler, menu), menu); 847 }, 848 849 contextMenuShow: function (menu, event) { 850 var tr = event.target; 851 while (this.tbodies.index(tr.parentNode) == -1) { 852 tr = tr.parentNode; 853 } 854 var index = tr.rowIndex; 855 if (! this.ranges.contains(index) ) { 856 this.selectionClickListener(event); 857 } 858 } 859 }); 860 861 var $super = rf.ui.ExtendedDataTable.$super; 862 }(RichFaces.jQuery, window.RichFaces)); 863