All Downloads are FREE. Search and download functionalities are using the official Maven repository.

META-INF.resources.org.richfaces.extendedDataTable.js Maven / Gradle / Ivy

There is a newer version: 4.3.7.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source
 * Copyright ${year}, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */ 
(function(richfaces, jQuery) {
    richfaces.utils = richfaces.utils || {};

    richfaces.utils.getCSSRule = function (className) {
		var rule = null;
		var sheets = document.styleSheets;
		for (var j = 0; !rule && j < sheets.length; j++) {
			var rules = sheets[j].cssRules ? sheets[j].cssRules : sheets[j].rules;
			for (var i = 0; !rule && i < rules.length; i++) {
				if (rules[i].selectorText && rules[i].selectorText.toLowerCase() == className.toLowerCase()) {
					rule = rules[i];
				}
			}
		}
		return rule;			
	};

	richfaces.utils.Ranges = function() {
		this.ranges = [];
	};
	
	richfaces.utils.Ranges.prototype = {

		add: function(index) {
			var i = 0;
			while(i < this.ranges.length && index >= this.ranges[i++][1]);
			i--;
			if(this.ranges[i-1] && index==(this.ranges[i-1][1]+1) ) {
				if(index==(this.ranges[i][0]-1)) {
					this.ranges[i-1][1] = this.ranges[i][1];
					this.ranges.splice(i, 1);
				} else {
					this.ranges[i-1][1]++;			
				}
			} else {
				if(this.ranges[i]){
					if(this.ranges[i] && index==(this.ranges[i][0]-1)) {
						this.ranges[i][0]--;			
					} else {
						if(index==(this.ranges[i][1]+1)){
							this.ranges[i][1]++;			
						} else {
							if(index this.ranges[i++][1]);
			i--;
			if(this.ranges[i]) {
				if(index==(this.ranges[i][1]) ) {
					if(index==(this.ranges[i][0])){
						this.ranges.splice(i, 1);
					} else {
						this.ranges[i][1]--;			
					}
				} else {
					if(index==(this.ranges[i][0])){
						this.ranges[i][0]++;			
					} else {
					this.ranges.splice(i+1, 0, [index+1, this.ranges[i][1]]);
					this.ranges[i][1] = index-1;
					}
				}
			}		
		},

		clear: function() {
			this.ranges = [];
		},
		
		contains: function(index) {
			var i = 0;
			while(i < this.ranges.length && index >= this.ranges[i][0]) {
				if(index >= this.ranges[i][0] && index <= this.ranges[i][1]) {
					return true;
				} else {
					i++;
				}
			}
			return false;
		},

		toString: function() {
			var ret = new Array(this.ranges.length);
			for (var i = 0; i < this.ranges.length; i++) {
				ret[i] = this.ranges[i].join();
			}
			return ret.join(";");
		}
	};

	richfaces.ExtendedDataTable = function(id, rowCount, ajaxFunction, options) {
		var WIDTH_CLASS_NAME_BASE = "rf-edt-cw-";
		var MIN_WIDTH = 20;
		
		options = options || {};
		var ranges = new richfaces.utils.Ranges();
		var element = document.getElementById(id);
		var bodyElement, contentElement, spacerElement, dataTableElement, rows, rowHeight, parts, tbodies, shiftIndex,
			activeIndex, selectionFlag;
		var dragElement = document.getElementById(id + ":d");
		var reorderElement = document.getElementById(id + ":r");
		var reorderMarkerElement = document.getElementById(id + ":rm");
		var widthInput = document.getElementById(id + ":wi");
		var selectionInput = document.getElementById(id + ":si");
		var normalPartStyle = richfaces.utils.getCSSRule(".rf-edt-pw").style;
		var header = jQuery(element).children(".rf-edt-h");
		var resizerHolders = header.find(".rf-edt-rsh");
		
		var frozenHeaderPartElement = document.getElementById(id + ":frozenHeader");
		var frozenColumnCount = frozenHeaderPartElement ? frozenHeaderPartElement.firstChild.rows[0].cells.length : 0;//TODO Richfaces.firstDescendant;
		
		var scrollElement = document.getElementById(id + ":footer");
		
		var resizeData = {};
		var idOfReorderingColumn = "";
		var newWidths = {};
		
		var timeoutId = null;
		
		var sendAjax = function(event, map) {
			for (key in options.parameters) {
				if(!map[key]) {
					map[key] = options.parameters[key];
				}
			}
			ajaxFunction(event, map);
		};
		
		var updateLayout = function() {
			normalPartStyle.width = "auto";
			var offsetWidth = frozenHeaderPartElement ? frozenHeaderPartElement.offsetWidth : 0;
			var width = Math.max(0, element.clientWidth - offsetWidth);
			if (width) {
				if (parts.width() > width) {
					normalPartStyle.width = width + "px";
				}
				normalPartStyle.display = "block";
				if (scrollElement.clientWidth < scrollElement.scrollWidth
						&& scrollElement.scrollHeight == scrollElement.offsetHeight) {
					scrollElement.style.overflowX = "scroll";
				} else {
					scrollElement.style.overflowX = "";
				}
				var delta = scrollElement.firstChild.offsetHeight - scrollElement.clientHeight;
				if (delta) {
					scrollElement.style.height = scrollElement.offsetHeight + delta;
				}
			} else {
				normalPartStyle.display = "none";
			}
			var height = element.clientHeight;
			var el = element.firstChild;
			while (el) {
				if(el.nodeName && el.nodeName.toUpperCase() == "DIV" && el != bodyElement) {
					height -= el.offsetHeight;
				}
				el = el.nextSibling;
			}
			if (bodyElement.offsetHeight > height) {
				bodyElement.style.height = height + "px";
			}
		};
		
		var adjustResizers = function() {
			var scrollLeft = scrollElement ? scrollElement.scrollLeft : 0;
			var clientWidth = element.clientWidth - 3;
			var i = 0;
			for (; i < frozenColumnCount; i++) {
				if (clientWidth > 0) {
					resizerHolders[i].style.display = "none";
					resizerHolders[i].style.display = "";
					clientWidth -= resizerHolders[i].offsetWidth;
				}
				if (clientWidth <= 0) {
					resizerHolders[i].style.display = "none";
				}
			}
			scrollLeft -= 3;
			for (; i < resizerHolders.length; i++) {
				if (clientWidth > 0) {
					resizerHolders[i].style.display = "none";
					if (scrollLeft > 0) {
						resizerHolders[i].style.display = "";
						scrollLeft -= resizerHolders[i].offsetWidth;
						if (scrollLeft > 0) {
							resizerHolders[i].style.display = "none";
						} else {
							clientWidth += scrollLeft;
						}
					} else {
						resizerHolders[i].style.display = "";
						clientWidth -= resizerHolders[i].offsetWidth;
					}
				}
				if (clientWidth <= 0) {
					resizerHolders[i].style.display = "none";
				}
			}
		};

		var updateScrollPosition = function() {
			if (scrollElement) {
				var scrollLeft = scrollElement.scrollLeft;
				parts.each(function() {
					this.scrollLeft = scrollLeft;
				});
			}
			adjustResizers();
		};

		var initialize = function() {
			bodyElement = document.getElementById(id + ":b");
			bodyElement.tabIndex = -1; //TODO don't use tabIndex.
			var bodyJQuery = jQuery(bodyElement);
			contentElement = bodyJQuery.children("div:first")[0];
			if (contentElement) {
				spacerElement = contentElement.firstChild;//TODO this.marginElement = Richfaces.firstDescendant(this.contentElement);
				dataTableElement = contentElement.lastChild;//TODO this.dataTableElement = Richfaces.lastDescendant(this.contentElement);
				tbodies = jQuery(document.getElementById(id + ":tbf")).add(document.getElementById(id + ":tbn"));
				rows = tbodies[0].rows.length;
				rowHeight = dataTableElement.offsetHeight / rows;
				if (rowCount != rows) {
					contentElement.style.height = (rowCount * rowHeight) + "px";
				}
				bodyJQuery.bind("scroll", bodyScrollListener);
				if (options.selectionMode != "none") {
					tbodies.bind("click", selectionClickListener);
					bodyJQuery.bind(window.opera ? "keypress" : "keydown", selectionKeyDownListener);
					initializeSelection();
				}
			} else {
				spacerElement = null;
				dataTableElement = null;
			}
			parts = jQuery(element).find(".rf-edt-p");
			updateLayout();
			updateScrollPosition(); //TODO Restore horizontal scroll position
		};
		
		var drag = function(event) {
			jQuery(dragElement).setPosition({left:Math.max(resizeData.left + MIN_WIDTH, event.pageX)});
			return false;
		};
		
		var beginResize = function(event) {
			var id = this.parentNode.className.match(new RegExp(WIDTH_CLASS_NAME_BASE + "([^\\W]*)"))[1];
			resizeData = {
				id : id,
				left : jQuery(this).parent().offset().left
			};
			dragElement.style.height = element.offsetHeight + "px";
			jQuery(dragElement).setPosition({top:jQuery(element).offset().top, left:event.pageX});
			dragElement.style.display = "block";
			jQuery(document).bind("mousemove", drag);
			jQuery(document).one("mouseup", endResize);
			return false;
		};
		
		var setColumnWidth = function(id, width) {
			width = width + "px";
			richfaces.utils.getCSSRule("." + WIDTH_CLASS_NAME_BASE + id).style.width = width;
			newWidths[id] = width;
			var widthsArray = new Array();
			for (var id in newWidths) {
				widthsArray.push(id + ":" + newWidths[id]);
			}
			widthInput.value = widthsArray.toString();
			updateLayout();
			adjustResizers();
			sendAjax(); // TODO Maybe, event model should be used here.
		};

		var endResize = function(event) {
			jQuery(document).unbind("mousemove", drag);
			dragElement.style.display = "none";
			var width = Math.max(MIN_WIDTH, event.pageX - resizeData.left);
			setColumnWidth(resizeData.id, width);
		};

		var reorder = function(event) {
			jQuery(reorderElement).setPosition(event, {offset:[5,5]});
			reorderElement.style.display = "block";
			return false;
		};

		var beginReorder = function(event) {
			idOfReorderingColumn = this.className.match(new RegExp(WIDTH_CLASS_NAME_BASE + "([^\\W]*)"))[1];
			jQuery(document).bind("mousemove", reorder);
			header.find(".rf-edt-hc").bind("mouseover", overReorder);
			jQuery(document).one("mouseup", cancelReorder);
			return false;
		};
		
		var overReorder = function(event) {
			if (idOfReorderingColumn != this.className.match(new RegExp(WIDTH_CLASS_NAME_BASE + "([^\\W]*)"))[1]) {
				var thisElement = jQuery(this);
				var offset = thisElement.offset();
				jQuery(reorderMarkerElement).setPosition({top:offset.top + thisElement.height(), left:offset.left - 5});
				reorderMarkerElement.style.display = "block";
				thisElement.one("mouseout", outReorder);
				thisElement.one("mouseup", endReorder);
			}
		};
		
		var outReorder = function(event) {
			reorderMarkerElement.style.display = "";
			jQuery(this).unbind("mouseup", endReorder);
		};
		
		var endReorder = function(event) {
			reorderMarkerElement.style.display = "";
			jQuery(this).unbind("mouseout", outReorder);
			var id = this.className.match(new RegExp(WIDTH_CLASS_NAME_BASE + "([^\\W]*)"))[1];
			var colunmsOrder = "";
			header.find(".rf-edt-hc").each(function() {
				var i = this.className.match(new RegExp(WIDTH_CLASS_NAME_BASE + "([^\\W]*)"))[1];
				if (i == id) {
					colunmsOrder += idOfReorderingColumn + "," + id + ",";
				} else if (i != idOfReorderingColumn) {
					colunmsOrder += i + ",";
				}
			});
			sendAjax(event, {"rich:columnsOrder" : colunmsOrder}); // TODO Maybe, event model should be used here.
		};
		
		var cancelReorder = function(event) {
			jQuery(document).unbind("mousemove", reorder);
			header.find(".rf-edt-hc").unbind("mouseover", overReorder);
			reorderElement.style.display = "none";
		};
		
		var loadData = function(event) {
			var clientFirst = Math.round((bodyElement.scrollTop + bodyElement.clientHeight / 2) / (rowHeight) - rows / 2);
			if (clientFirst <= 0) {
				clientFirst = 0;
			} else {
				clientFirst = Math.min(rowCount - rows, clientFirst);
			}
			sendAjax(event, {"rich:clientFirst" : clientFirst});// TODO Maybe, event model should be used here.
		}

		var bodyScrollListener = function(event) {
			if(timeoutId) {
				window.clearTimeout(timeoutId);
				timeoutId = null;
			}
			if (Math.max(this.scrollTop - rowHeight, 0) < spacerElement.offsetHeight
						|| Math.min(this.scrollTop + rowHeight + this.clientHeight, this.scrollHeight) > spacerElement.offsetHeight + dataTableElement.offsetHeight) {
				timeoutId = window.setTimeout(function (event) {loadData(event)}, 1000);
			}
		};

		var showActiveRow = function() {
			if (bodyElement.scrollTop > activeIndex * rowHeight + spacerElement.offsetHeight) { //UP
				bodyElement.scrollTop = Math.max(bodyElement.scrollTop - rowHeight, 0);
			} else if (bodyElement.scrollTop + bodyElement.clientHeight
					< (activeIndex + 1) * rowHeight + spacerElement.offsetHeight) { //DOWN
				bodyElement.scrollTop = Math.min(bodyElement.scrollTop + rowHeight, bodyElement.scrollHeight - bodyElement.clientHeight);
			}
		}

		var selectRow = function(index) {
			ranges.add(index);
			for ( var i = 0; i < tbodies.length; i++) {
				jQuery(tbodies[i].rows[index]).addClass("rf-edt-r-s");
			}
		}
		
		var deselectRow = function (index) {
			ranges.remove(index);
			for ( var i = 0; i < tbodies.length; i++) {
				jQuery(tbodies[i].rows[index]).removeClass("rf-edt-r-s");
			}
		}

		var setActiveRow = function (index) {
			if(typeof activeIndex == "number") {
				for ( var i = 0; i < tbodies.length; i++) {
					jQuery(tbodies[i].rows[activeIndex]).removeClass("rf-edt-r-a");
				}
				
			}
			activeIndex = index;
			for ( var i = 0; i < tbodies.length; i++) {
				jQuery(tbodies[i].rows[activeIndex]).addClass("rf-edt-r-a");
			}
		}
		
		var resetShiftRow = function () {
			if(typeof shiftIndex == "number") {
				for ( var i = 0; i < tbodies.length; i++) {
					jQuery(tbodies[i].rows[shiftIndex]).removeClass("rf-edt-r-t");
				}
				
			}
			shiftIndex = null;
		}
		
		var setShiftRow = function (index) {
			resetShiftRow();
			shiftIndex = index;
			if(typeof index == "number") {
				for ( var i = 0; i < tbodies.length; i++) {
					jQuery(tbodies[i].rows[shiftIndex]).addClass("rf-edt-r-t");
				}
			}
		}
		
		var initializeSelection = function() {
			ranges.clear();
			var strings = selectionInput.value.split("|");
			activeIndex = strings[1] || null;
			shiftIndex = strings[2] || null;
			selectionFlag = null;
			var rows = tbodies[0].rows;
			for (var i = 0; i < rows.length; i++) {
				var row = jQuery(rows[i]);
				if (row.hasClass("rf-edt-r-s")) {
					ranges.add(row[0].rowIndex)
				}
				if (row.hasClass("rf-edt-r-a")) {
					activeIndex = row[0].rowIndex;
				}
				if (row.hasClass("rf-edt-r-t")) {
					shiftIndex = row[0].rowIndex;
				}
			}
			writeSelection();
		}

		var writeSelection = function() {
			selectionInput.value = [ranges, activeIndex, shiftIndex, selectionFlag].join("|");
		}
		
		var selectRows = function(range) {
			if (typeof range == "number") {
				range = [range, range];
			}
			var changed;
			var i = 0;
			for (; i < range[0]; i++) {
				if (ranges.contains(i)) {
					deselectRow(i);
					changed = true;
				}
			}
			for (; i <= range[1]; i++) {
				if (!ranges.contains(i)) {
					selectRow(i);
					changed = true;
				}
			}
			for (; i < rows; i++) {
				if (ranges.contains(i)) {
					deselectRow(i);
					changed = true;
				}
			}
			selectionFlag = typeof shiftIndex == "string" ? shiftIndex : "x";
			return changed;
		}
		
		var processSlectionWithShiftKey = function(index) {
			if(shiftIndex == null) {
				setShiftRow(activeIndex != null ? activeIndex : index);
			}
			var range;
			if ("u" == shiftIndex) {
				range = [0, index];
			} else if ("d" == shiftIndex) {
				range = [index, rows - 1];
			} else if (index >= shiftIndex) {
				range = [shiftIndex, index];
			} else {
				range = [index, shiftIndex];
			}
			return selectRows(range);			
		}
		
		var onbeforeselectionchange = function (event) {
			return !options.onbeforeselectionchange || options.onbeforeselectionchange.call(element, event) !== false;
		}
		
		var onselectionchange = function (event, index, changed) {
			if(!event.shiftKey) {
				resetShiftRow();
			}
			if (activeIndex != index) {
				setActiveRow(index);
				showActiveRow();
			}
			if (changed) {
				writeSelection();
				if (options.onselectionchange) {
					options.onselectionchange.call(element, event);
				}
			}
		}
		
		var selectionClickListener = function (event) {
			if (!onbeforeselectionchange(event)) {
				return;
			}
			var changed;
			if (event.shiftKey || event.ctrlKey) {
				if (window.getSelection) { //TODO Try to find other way.
					window.getSelection().removeAllRanges();
				} else if (document.selection) {
					document.selection.empty();
				}
			}
			var tr = event.target;
			while (tbodies.index(tr.parentNode) == -1) {
				tr = tr.parentNode;
			}
			var index = tr.rowIndex;
			if (options.selectionMode == "single" || (options.selectionMode != "multipleKeyboardFree"
				&& !event.shiftKey && !event.ctrlKey)) {
				changed = selectRows(index);
			} else if (options.selectionMode == "multipleKeyboardFree" || (!event.shiftKey &&  event.ctrlKey)) {
				if (ranges.contains(index)) {
					deselectRow(index);
				} else {
					selectRow(index);
				}
				changed = true;
			} else {
				changed = processSlectionWithShiftKey(index);
			}
			onselectionchange(event, index, changed);
		}

		var selectionKeyDownListener = function(event) {
			if (event.ctrlKey && options.selectionMode != "single" && (event.keyCode == 65 || event.keyCode == 97) //Ctrl-A
					&& onbeforeselectionchange(event)) {
				selectRows([0, rows]);
				selectionFlag = "a";
				onselectionchange(event, activeIndex, true); //TODO Is there a way to know that selection haven't changed?
				event.preventDefault();
			} else {
				var index;
				if (event.keyCode == 38) { //UP
					index = -1;
				} else if (event.keyCode == 40) { //DOWN
					index = 1;
				}
				if (index != null && onbeforeselectionchange(event)) {
					if (typeof activeIndex == "number") {
						index += activeIndex;
						if (index >= 0 && index < rows ) {
							var changed;
							if (options.selectionMode == "single" || (!event.shiftKey && !event.ctrlKey)) {
								changed = selectRows(index);
							} else if (event.shiftKey) {
								changed = processSlectionWithShiftKey(index);
							}
							onselectionchange(event, index, changed);
						}
					}
					event.preventDefault();
				}
			}
		}

		var ajaxComplete = function (event, data) {
			if (data.reinitializeHeader) {
				bindHeaderHandlers();
			} else {
				selectionInput = document.getElementById(id + ":si");
				if (data.reinitializeBody) {
					rowCount = data.rowCount;
					initialize();
				} else if (options.selectionMode != "none") {
					initializeSelection();
				}
				if (spacerElement) {
					spacerElement.style.height = (data.first * rowHeight) + "px";
				}
			}
		};
		
		jQuery(document).ready(initialize);
		jQuery(window).bind("resize", updateLayout);
		jQuery(scrollElement).bind("scroll", updateScrollPosition);
		var bindHeaderHandlers = function () {
			header.find(".rf-edt-rs").bind("mousedown", beginResize);
			header.find(".rf-edt-hc").bind("mousedown", beginReorder);
		}
		bindHeaderHandlers();
		jQuery(element).bind("rich:onajaxcomplete", ajaxComplete);
		
		//JS API
		element["richfaces"] = element["richfaces"] || {}; // TODO ExtendedDataTable should extend richfaces.BaseComponent instead of using it.
		element.richfaces.component = this;
		
		this.getColumnPosition = function(id) {
			var position;
			var headers = header.find(".rf-edt-hc");
			for (var i = 0; i < headers.length; i++) {
				if (id == headers[i].className.match(new RegExp(WIDTH_CLASS_NAME_BASE + "([^\\W]*)"))[1]) {
					position = i;
				}
			}
			return position;
		}
		
		this.setColumnPosition = function(id, position) {
			var colunmsOrder = "";
			var before;
			var headers = header.find(".rf-edt-hc");
			for (var i = 0; i < headers.length; i++) {
				var current = headers[i].className.match(new RegExp(WIDTH_CLASS_NAME_BASE + "([^\\W]*)"))[1];
				if (i == position) {
					if (before) {
						colunmsOrder += current + "," + id + ",";
					} else {
						colunmsOrder += id + "," + current + ",";
					}
				} else {
					if (id != current) {
						colunmsOrder += current + ",";
					} else {
						before = true;
					}
				}
			}
			sendAjax(null, {"rich:columnsOrder" : colunmsOrder}); // TODO Maybe, event model should be used here.
		}
		
		this.setColumnWidth = function(id, width) {
			setColumnWidth(id, width);
		}
		
		this.filter = function(colunmId, filterValue, isClear) {
			if (typeof(filterValue) == "undefined" || filterValue == null) {
				filterValue = "";
			}
			var map = {}
			map[id + "rich:filtering"] = colunmId + ":" + filterValue + ":" + isClear;
			sendAjax(null, map); // TODO Maybe, event model should be used here.
		}
		
		this.clearFiltering = function() {
			this.filter("", "", true);
		}
		
		this.sort = function(colunmId, sortOrder, isClear) {
			if (typeof(sortOrder) == "string") {
				sortOrder = sortOrder.toUpperCase();
			}
			var map = {}
			map[id + "rich:sorting"] = colunmId + ":" + sortOrder + ":" + isClear;
			sendAjax(null, map); // TODO Maybe, event model should be used here.
		}
		
		this.clearSorting = function() {
			this.filter("", "", true);
		}
	};
}(window.RichFaces, jQuery));





© 2015 - 2024 Weber Informatics LLC | Privacy Policy