Skip to content

Instantly share code, notes, and snippets.

@manusa
Created June 2, 2014 07:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save manusa/e94dbcb4342b4a4643f3 to your computer and use it in GitHub Desktop.
Save manusa/e94dbcb4342b4a4643f3 to your computer and use it in GitHub Desktop.
Modified version of script-jq.js fo wicketstuff InMethodGrid (https://github.com/wicketstuff/core/tree/master/jdk-1.7-parent/inmethod-grid-parent)
/**
* Copyright (c) 2007 Matej Knopp, InMethod s.r.o.
* All rights reserved.
*/
if (typeof(InMethod) === "undefined") {
InMethod = { };
}
(function() {
var empty = function() { };
var genIdCounter = 0;
/**
* YAHOO event cleanups the listeners only on page unload. However, if the page lives long
* enough the elements removed from document that have listener attached cause IE GC not free the
* memory. So we manually register each element with listener and then periodically check
* whether the element is still in document. If it's not the element's listeners are removed.
*/
var elementsWithListeners = new Array();
var addListener = function(element, event, fn, obj, override) {
// attach event
$(element).on(event, obj, fn);
if (element !== document && element !== window) {
elementsWithListeners.push(element);
}
};
// temporary array of elements being processed during purge
var beingPurged = null;
// count of purged elements (debug)
var purgedCount = 0;
var purgeDebug = false;
// periodically called to initiate the purging process
var purgeInactiveListeners = function() {
// if purge is in progress don't do anything
if (typeof beingPurged !== 'undefined' && beingPurged !== null) {
return;
}
// the the elements
beingPurged = elementsWithListeners;
elementsWithListeners = new Array();
if (purgeDebug)
Wicket.Log.info("Purge begin");
purgedCount = 0;
// start the process
purge();
};
var purge = function()
{
if (typeof beingPurged !== 'undefined' && beingPurged !== null) {
var done = 0;
// it is necessary to limit amount of items being purged in one go otherwise
// IE will complain about script being slow
var max = 50;
var a = beingPurged;
for (var i = 0; i < a.length && done < max; ++i)
{
var e = a[i];
if (e !== null)
{
++done;
if ($(e).length === 0) {
// remove all events.
$(e).off();
++purgedCount;
} else {
// element is still in document, return it
elementsWithListeners.push(e);
}
a[i] = null;
}
}
if (i === a.length)
{
// we are done with purging
beingPurged = null;
if (purgeDebug)
Wicket.Log.info("Purge End; purged: " + purgedCount + ", total: " + elementsWithListeners.length);
}
else
{
// not yet done, continue after 50ms
window.setTimeout(purge, 50);
}
}
};
/**
* Prelight functionality
*/
// map of elements with prelight
var curPrelight = new Object();
var mouseIn = function(ev, instance) {
var e = this;
// if the old and new prelight are not related in any way make sure that
// the prelight is disabled on the old ones
for (property in curPrelight) {
var id = curPrelight[property];
if (id) {
var c = Wicket.$(id);
if (c != null && $(e).parents().index($(c))==0 && $(c).parents().index($(e))==0) {
Wicket.bind(mouseOut,c)(null, instance);
}
}
}
var id = getElementId(e);
curPrelight[id] = id;
e.imxtPrelight = true;
if(ev !== null) {
ev.data.updatePrelight(e);
} else if(instance !== null) {
instance.updatePrelight(e);
}
};
var mouseOut = function(ev, instance) {
var e = this;
curPrelight[getElementId(e)] = null;
e.imxtPrelight = false;
ev.data.updatePrelight(e);
};
var ignorePrelight = function(element)
{
// We need to ignore prelight on table rows for all browsers but IE<=6.
// For all other browsers we use :hover, it's much faster in IE7
return !(element.tagName.toLowerCase() === "tr" && Wicket.Browser.isIELessThan7()) && hasClass(element, "imxt-grid-row");
}
var attachPrelight = function(elements, instance) {
for (var i = 0; i < elements.length; ++i) {
var element = elements[i];
if (element.imxtPrelightAttached !== true && !ignorePrelight(element)) {
if (curPrelight != null && curPrelight[getElementId(element)] != null) {
Wicket.bind(mouseIn,element)(null, instance);
}
addListener(element, "mouseover", mouseIn, instance, false);
addListener(element, "mouseout", mouseOut, instance, false);
element.imxtPrelightAttached = true;
}
}
};
window.setInterval(purgeInactiveListeners, 10000);
$(window).on("unload", function() { elementsWithListeners = null; });
var getElementId = function(element) {
if (typeof(element.getAttribute("id")) === "string" && element.getAttribute("id").length > 0) {
return element.getAttribute("id");
} else {
var id = "imxt-generated-id-" + (++genIdCounter);
element.setAttribute("id", id);
return id;
}
};
InMethod.Drag = Wicket.Class.create();
InMethod.Drag.prototype = {
initialize: function(element, onDragBegin, onDragEnd, onDrag, thisRef) {
this.elementId = getElementId(element);
this.onDragBegin = onDragBegin || empty;
this.onDrag = onDrag || empty;
this.onDragEnd = onDragEnd || empty;
this.thisRef = thisRef || new Object();
this.noTree = null;
addListener(element, "mousedown", this.onMouseDown, this, true);
element.imDrag = this;
},
onMouseDown: function(e) {
if (typeof(e.ignore) === "undefined") {
e.stopPropagation();
e.data.lastMouseX = e.clientX;
e.data.lastMouseY = e.clientY;
addListener(document, "selectstart", e.data.onSelectStart);
addListener(document, "mousemove", e.data.onMouseMove, e.data, true);
addListener(document, "mouseup", e.data.onMouseUp, e.data, true);
Wicket.bind(e.data.onDragBegin,e.data.thisRef)(Wicket.$(e.data.elementId), e);
}
return false;
},
onMouseMove: function(e) {
e.stopPropagation();
// this happens sometimes in Safari
if (e.clientX < 0 || e.clientY < 0) {
return;
}
var deltaX = e.clientX - e.data.lastMouseX;
var deltaY = e.clientY - e.data.lastMouseY;
var res = Wicket.bind(e.data.onDrag,e.data.thisRef)(Wicket.$(e.data.elementId), deltaX, deltaY, e);
if (typeof res === 'undefined' || res === null){
res = [0, 0];
}
e.data.lastMouseX = e.clientX + res[0];
e.data.lastMouseY = e.clientY + res[1];
return false;
},
onMouseUp: function(e) {
e.stopPropagation();
// cleanup
$(document).off("selectstart", e.data.onSelectStart);
$(document).off("mousemove", e.data.onMouseMove);
$(document).off("mouseup", e.data.onMouseUp);
Wicket.bind(e.data.onDragEnd,e.data.thisRef)(Wicket.$(e.data.elementId), e);
return false;
},
onSelectStart: function(e) {
return false;
},
cleanUp: function() {
$(Wicket.$(this.elementId)).off("mousedown");
this.element = null;
this.onDragBegin = null;
this.onDrag = null;
this.onDragEnd = null;
this.thisRef = null;
}
};
var colSpanRegex = /imxt-colspan-([\d]+)/;
var nextSibling = function(element) {
return $(element).next(element.tagName)[0];
};
var prevSibling = function(element) {
return $(element).prev(element.tagName)[0];
};
var hasClass = function(e, c) {
return $(e).hasClass(c);
};
var addClass = function(e, c) {
$(e).addClass(c);
};
var removeClass = function(e, c) {
$(e).removeClass(c);
};
var contains = function(array, value) {
for (var i = 0; i < array.length; ++i) {
if (array[i] == value) {
return true;
}
}
return false;
};
var isEmpty = function(string) {
return !(typeof string === 'string' && string.length > 0);
}
var getChildren = function(parent, tagName) {
if ($.isArray(tagName)) {
return $(parent).children(tagName.toString());
} else {
return $(parent).children(tagName);
}
};
var arrayEq = function(a1, a2) {
if (!$.isArray(a1) || !$.isArray(a2)) {
return false;
}
if (a1 == a2) {
return true;
}
if (a1.length != a2.length) {
return false;
}
for (var i = 0; i < a1.length; ++i) {
if (a1[i] != a2[i]) {
return false;
}
}
return true;
};
var getFirstChild = function(parent, tagName) {
for (var i = 0; i < parent.childNodes.length; ++i) {
var c = parent.childNodes[i];
if (c && c.tagName == tagName) {
return c;
}
}
return null;
}
Wicket.Browser.isIE9 = Wicket.Browser.isIE9 || function() {
var index = window.navigator.userAgent.indexOf("MSIE");
var version = parseFloat(window.navigator.userAgent.substring(index + 5));
return Wicket.Browser.isIE() && version == 9;
};
// Issue #44:
// This bug can also be triggered when hiding a modal dialog or similar and the behind the dialog is a grid which
// would be hovered at that moment.
function globalIE9HoverLayoutFix() {
$(".imxt-body-container2").css({
"overflow" : "auto"
});
}
if (Wicket.Browser.isIE9())
Wicket.Event.subscribe('/ajax/call/complete', globalIE9HoverLayoutFix);
InMethod.XTable = Wicket.Class.create();
InMethod.XTable.prototype = {
initialize: function(id, columnsData, columnsStateCallback) {
this.id = id;
//Store browser version in variables, calls to Wicket.Browser methods burn CPU.
this.isIE = Wicket.Browser.isIE();
this.isIEQuirks = Wicket.Browser.isIEQuirks();
this.isIELessThan7 = Wicket.Browser.isIELessThan7();
this.isGecko = Wicket.Browser.isGecko();
this.isOpera = Wicket.Browser.isOpera();
this.isSafari = Wicket.Browser.isSafari();
this.initColumns(columnsData);
this.attachEventHandlers();
this.prevColumnWidths = null;
this.updateScrollTop = this.lastScrollTop;
this.updateScrollLeft = this.lastScrollLeft;
//this.updateColumnWidths();
this.update();
this.columnsStateCallback = columnsStateCallback;
// IE needs update called twice, otherwise the top right corner flashes or there is a horizontal
// scrollbar where it shouldn't be
if (this.isIE && !this.isIEQuirks) {
var bodyContainer1 = this.getElement("div", "imxt-body-container1");
bodyContainer1.imxtOldOffsetWidth = null;
this.update();
}
if (this.isIE) {
addClass(Wicket.$(id), "imxt-ie");
} else if (this.isGecko) {
addClass(Wicket.$(id), "imxt-ff");
} else if (this.isSafari){
addClass(Wicket.$(id), "imxt-safari");
}
this.updateSelectCheckBoxes();
this.initCells(this.getBodyTable());
},
initColumns: function(columnsData) {
var r = this.getColumnsRow();
var ths = getChildren(r, "TH");
for (var i = 0; i < columnsData.length && i < ths.length; ++i) {
var c = columnsData[i];
var th = ths[i];
th.imxtMinSize = c.minSize;
th.imxtMaxSize = c.maxSize;
th.imxtId = c.id;
th.imxtResizable = c.resizable;
th.imxtReorderable = c.reorderable;
}
},
/**
* Returns the element that contains the XTable
*/
getTopContainer: function() {
return Wicket.$(this.id);
},
/**
* Returns the table that contains header (part that doesn't scroll vertically, e.g. columns)
*/
getHeadTable: function() {
return this.getElement("table", "imxt-head");
},
/**
* Returns the table that contains the body (part that scrolls vertically)
*/
getBodyTable: function() {
return this.getElement("table", "imxt-body");
},
/**
* Returns the elements that have specified tagName and css class.
* Optionally root parameter can narrow the searching scope.
* @param tagName
* @param className
* @param root
*/
getElements: function(tagName, className, root) {
if (typeof(root) === "undefined")
root = this.getTopContainer();
if (typeof className === 'undefined' || className === null) {
return $(root).find(tagName);
} else {
var selector = tagName + '.' + className;
return $(root).find(selector);
}
},
/**
* Mapping between cacheId and real Id. We need caching because retrieving the elements
* based on the CSS class name is relative expensive. So once we retrieve en element
* (single component, not a group of components), we set it's id to generated id that
* can be used to retrieve the element quickly. If element has already id set, we use
* idCache to map between elementId (real Id) and the generatedId (cacheId).
*/
idCache: new Object(),
/**
* Constructs cache id unique for this XTable.
* @param suffix
*/
getCacheId: function(suffix) {
return "imxt-" + this.id + "-" + suffix;
},
/**
* Returns element with specified id. If the element can't be found based on the id,
* it calls getElementFunc to find the element and then either sets element id or
* put a mapping to real id to idCache.
* @param id
* @param {function} getElementFunc
*/
getElementCached: function(id, getElementFunc) {
var actualId = this.idCache[id] || id;
var e = Wicket.$(actualId);
if (typeof e === 'undefined' || e === null) {
e = getElementFunc();
actualId = e.getAttribute("id");
if (!isEmpty(actualId)) {
this.idCache[id] = actualId;
} else {
e.setAttribute("id", id);
}
}
return e;
},
/**
* Returns element in this XTable with specified tagName and className,
* optionally narrowing the search scope with the root parameter.
* @param tagName
* @param className
* @param root
*/
getElement: function(tagName, className, root) {
var id = this.getCacheId(className);
return this.getElementCached(id, Wicket.bind(function() {
return this.getElements(tagName, className, root)[0];
},this));
},
/**
* Returns array of the TR elements inside the head table.
*/
getHeadRows: function() {
var id = this.getCacheId("head-first-row");
var firstRow = this.getElementCached(id, Wicket.bind(function() {
var head = this.getHeadTable();
return head.getElementsByTagName("tr")[0];
},this));
return getChildren(firstRow.parentNode, "TR");
},
/**
* Returns array of the TR elements inside the body table.
*/
getBodyRows: function() {
var id = this.getCacheId("body-first-row");
var firstRow = this.getElementCached(id, Wicket.bind(function() {
var body = this.getBodyTable();
return body.getElementsByTagName("tr")[0];
},this));
return getChildren(firstRow.parentNode, "TR");
},
/**
* Returns the TR element that contains column headers.
*/
getColumnsRow: function() {
return this.getElement("tr", "imxt-columns", this.getHeadTable());
},
updateSelectCheckBoxes: function() {
var head = this.getElements("input", "imxt-select", this.getHeadTable());
if (head.length > 0) {
var body = this.getElements("input", "imxt-select", this.getBodyTable());
var selected = body.length > 0;
for (var i = 0; i < body.length; ++i) {
if (body[i].checked != true) {
selected = false;
break;
}
}
for (var i = 0; i < head.length; ++i) {
head[i].checked = selected;
}
}
},
/**
* Returns array of column width strings.
* E.g. ["100px", "300px", "250px"];
*/
getColumnWidths: function() {
var row = this.getColumnsRow();
var ths = getChildren(row, "TH");
var res = new Array();
for (var i = 0; i < ths.length; ++i) {
res.push(ths[i].offsetWidth);
}
return res;
},
/**
* Updates the widths of columns in table body according to the headers
*/
updateColumnWidths: function() {
var widths = this.getColumnWidths();
var scroll;
if (this.isOpera) {
// for some reason opera doesn't preserve the scroll offset
bodyContainer1 = this.getElement("div", "imxt-body-container1");
scroll = bodyContainer1.scrollLeft;
}
if (arrayEq(this.prevColumnWidths, widths) == false) {
var header = this.getColumnsRow();
var rows = this.getBodyRows();
if (rows.length > 0) {
var first = rows[0];
var ths = getChildren(header, "TH");
var tds = getChildren(first, "TD");
for (var i = 0; i < ths.length - 2; ++i) {
var h = ths[i];
var r = tds[i];
var width = widths[i] + "px";
// opera fails to refresh the table properly, so we need to hide it and show
// after the width is set, that way it's refreshed properly
if (r.style.width != width) {
if (this.isOpera)
this.getBodyTable().style.display = "none";
r.style.width = width;
}
if (this.isOpera) {
this.getBodyTable().style.display = "";
}
}
}
this.prevColumnWidths = widths;
// When there is horizontal scrollbar present firefox flickers on ajax replacement.
// The reason is that when the table is rendered the data columns have zero size, thus the scrollbar is not visible.
// Only in updateColumnWidths the column sizes are set and the scrollbar is displayed. However, this is too late for
// firefox and in some cases in shifts the content after the table and immediately moves it back which results in nasty
// flicker. This fix creates a style rule for imxt-body-cotnainer2 that forces it to have width of body table, so the
// scrollbar is immediately visible (if necessary) after ajax replacement.
if (this.isGecko) {
var c2 = this.getElement("div", "imxt-body-container2");
c2.style.width="auto"; // we want the stylesheet rule only applied on fresh replaced component
var x = document.styleSheets[0];
if (this.cssRulePos != null) {
x.deleteRule(this.cssRulePos);
}
this.cssRulePos = x.cssRules.length;
x.insertRule('div#' + this.id + ' div.imxt-body-container2 {width: ' + this.getBodyTable().offsetWidth + 'px}', this.cssRulePos)
var c1 = this.getElement("div", "imxt-body-container1");
// sometimes firefox displays "fake" horizontal scrollbar, this is a workaround
if (c1.scrollWidth <= c1.offsetWidth) {
c1.style.overflowX = "hidden";
} else {
c1.style.overflowX = "scroll";
}
}
}
if (this.isOpera) {
bodyContainer1 = this.getElement("div", "imxt-body-container1");
bodyContainer1.scrollLeft = scroll;
}
},
/**
* Updates the table state
* @param force
*/
updateInternal: function(force) {
//This Function is run periodically, old calls to Wicket.Browser burnt CPU in
//newest chrome versions.
var topContainer = this.getTopContainer();
if (topContainer == null) {
// the table was probably removed
return false;
}
var head = this.getHeadTable();
var body = this.getBodyTable();
var headContainer1 = this.getElement("div", "imxt-head-container1");
var headContainer2 = this.getElement("div", "imxt-head-container2");
var bodyContainer1 = this.getElement("div", "imxt-body-container1");
if (this.isIE || this.isGecko) {
bodyContainer1.style.width = topContainer.offsetWidth + "px";
}
var padding = (bodyContainer1.offsetWidth - bodyContainer1.clientWidth);
// count new header width
var newWidth = (body.offsetWidth + padding);
// sometimes newWidth is negative in IE, we have to ignore it
if (this.isIE&& newWidth > 0 && head.style.width != newWidth) {
head.style.width = newWidth + "px";
} else if (this.isSafari) {
var form = this.getElement("*", "imxt-form");
var fieldset = this.getElement("fieldset", "imxt-fieldset", form);
fieldset.style.width = form.offsetWidth + "px";
}
if (padding > 0) {
// compensate for scrollbar
var e = this.getElement("th", "imxt-padding-right", head);
e.style.width = padding + "px";
}
this.updateHandles(force);
this.updateColumnWidths();
this.updatePreservedScrollOffsets();
// scroll the header if body is scrolled
var scroll = bodyContainer1.scrollLeft;
if (headContainer2.scrollLeft != scroll)
headContainer2.scrollLeft = scroll;
this.fixIEStd();
// update ok
return true;
},
/**
* After (ajax) refresh this updates the scroll offsets to match previous offset, so that the scrolling
* position remains same after component update.
*/
updatePreservedScrollOffsets: function() {
if ($.isNumeric(this.updateScrollTop) ||
$.isNumeric(this.updateScrollLeft)) {
var bodyContainer1 = this.getElement("div", "imxt-body-container1");
bodyContainer1.style.visibility="hidden";
if ($.isNumeric(this.updateScrollLeft)) {
bodyContainer1.scrollLeft = this.lastScrollLeft;
this.updateScrollLeft = null;
}
if ($.isNumeric(this.updateScrollTop)) {
bodyContainer1.scrollTop = this.updateScrollTop;
this.updateScrollTop = null;
}
bodyContainer1.style.visibility="visible";
bodyContainer1.imxtOldOffsetWidth = null;
}
},
fixIEStd: function() {
if (this.isIE && !this.isIEQuirks) {
var bodyContainer1 = this.getElement("div", "imxt-body-container1");
// IE in standard compliance mode fails to display the scrollbars properly
// so we need to manually change the overflow mode
if (this.dragging || bodyContainer1.imxtOldOffsetWidth != bodyContainer1.offsetWidth) {
var body = this.getBodyTable();
// this forces IE to recalculate the width. setting to 1px "hides" the scrollbar
// so that bodyContainer1 gives us real scrollWidth
body.style.width = "1px";
body.style.width = bodyContainer1.scrollWidth + "px";
bodyContainer1.imxtOldOffsetWidth = bodyContainer1.offsetWidth;
}
}
},
/**
* On IE 6 or IE7 quirks this sets the drag handles height same as parent (column) height
*/
updateHandles: function(force) {
// this is slow on IE so we only do it on every tenth update
if (this.isIELessThan7 || this.isIEQuirks) {
var c = this.counter || 0;
if (force)
c = 0;
if (c == 0) {
var header = this.getColumnsRow();
var handles = this.getElements("a", "imxt-handle", header);
for (var i = 0; i < handles.length; ++i) {
var h = handles[i];
h.style.height = h.parentNode.offsetHeight + "px";
}
} else if (c == 10) {
c = -1;
}
++c;
this.counter = c;
}
},
/**
* Updates the XTable. This is called during resizing, after column drag and also periodically.
* @param force
*/
update: function(force) {
if (this.dragging === true) {
return true;
} else {
return this.updateInternal(force);
}
},
getColumnMinSize: function(column) {
var res = column.imxtMinSize;
return $.isNumeric(res) && res > 0 ? res : 20;
},
getColumnMaxSize: function(column) {
var res = column.imxtMaxSize;
return $.isNumeric(res) && res > 0 ? res : -1;
},
getResizeProxy: function() {
var e = Wicket.$("imxt-resize-proxy");
if (typeof e === 'undefined' || e === null) {
e = document.createElement("div");
e.setAttribute("id", "imxt-resize-proxy");
document.body.appendChild(e);
}
return e;
},
showResizeProxy: function(column) {
var headContainer = this.getElement("div", "imxt-head-container");
var bodyContainer = this.getElement("div", "imxt-body-container1");
var columnPos = $(column).offset();
var headContainerPos = $(headContainer).offset();
var bodyContainerPos = $(bodyContainer).offset();
var proxy = this.getResizeProxy();
proxy.style.display = "";
proxy.style.height = (bodyContainer.offsetHeight + bodyContainerPos.top - headContainerPos.top) + "px";
var top = headContainerPos.top;
if (this.isIE) { // weird IE bug
top = top - 2;
}
proxy.style.top = top + "px";
proxy.style.left = (columnPos.left + column.imxtWidth - proxy.offsetWidth) + "px";
},
hideResizeProxy: function(column) {
var proxy = this.getResizeProxy();
proxy.style.display="none";
},
/**
* Called when column resizing handle dragging started.
* @param handle
*/
handleDragBegin: function(handle) {
Wicket.Focus.lastFocusId = null;
this.dragging = true;
var column = handle;
// find the column itself
do {
column = column.parentNode;
} while (column.tagName.toLowerCase() !== "th");
// add the css dragging class
addClass(column, "imxt-dragging");
addClass(this.getColumnsRow(), "imxt-dragging");
column.min = this.getColumnMinSize(column);
column.max = this.getColumnMaxSize(column);
column.imxtWidth = parseInt(column.style.width, 10);
this.showResizeProxy(column);
},
/**
* Called when column resizing handle dragging ended
* @param handle
*/
handleDragEnd: function(handle) {
this.dragging = false;
this.hideResizeProxy();
var column = handle;
do {
column = column.parentNode;
} while (column.tagName.toLowerCase() !== "th");
// opera has weird redrawing problems, to get over it we hide the handle and show
// it after setting the width
if (this.isOpera) {
column.style.display = "none";
}
column.style.width = column.imxtWidth + "px";
if (this.isOpera) {
column.style.display = "";
}
// remove the css classes
removeClass(column, "imxt-dragging");
removeClass(this.getColumnsRow(), "imxt-dragging");
// fix for IE quirks mode (to force recalculating of table layout)
this.getElement("div", "imxt-body-container1").imxtOldOffsetWidth = null;
// the prelight has not been updated during dragging, update it now
if (this.isIE) // flickers otherwise
window.setTimeout(Wicket.bind(this.updatePrelight,this), 100);
else
this.updatePrelight();
this.updateInternal();
this.submitColumnState();
},
/**
* Called during a column handle dragging
* @param handle
* @param dX
* @param dY
*/
handleDrag: function(handle, dX, dY) {
var column = handle;
do {
column = column.parentNode;
} while (column.tagName.toLowerCase() !== "th");
var current = column.imxtWidth;
var newWidth = current + dX;
var delta = 0;
if (column.min != -1 && newWidth < column.min) {
delta = column.min - newWidth;
newWidth = column.min;
} else if (column.max != -1 && newWidth > column.max) {
delta = column.max - newWidth;
newWidth = column.max;
}
column.imxtWidth = newWidth;
this.showResizeProxy(column);
return [delta, 0];
},
handleDoubleClick: function(event) {
var table = event.data;
var column = event.target;
do {
column = column.parentNode;
} while (column.tagName.toLowerCase() !== "th");
var min = table.getColumnMinSize(column);
column.style.width = min + "px";
table.updateInternal();
table.submitColumnState();
},
/**
* Returns the array of TH elements from the column row
*/
getColumns: function() {
var columnsRow = this.getColumnsRow();
return getChildren(columnsRow, "TH");
},
/**
* Returns array of handle links inside the columns row
*/
getHandles: function() {
var columns = this.getColumnsRow();
var handles = this.getElements("a", "imxt-handle", columns);
return handles;
},
/**
* Calback invoked when the body is scrolled.
* @param event
*/
onScroll: function(event) {
var bodyContainer = event.data.getElement("div", "imxt-body-container1");
// only update on horizontal scrolling
if (bodyContainer.scrollLeft != bodyContainer.imxtPrevScrollLeft) {
event.data.update();
bodyContainer.imxtPrevScrollLeft = bodyContainer.scrollLeft;
}
event.data.lastScrollLeft = bodyContainer.scrollLeft;
event.data.lastScrollTop = bodyContainer.scrollTop;
},
/**
* Attach the various event handler to elements in this XTable.
*/
attachEventHandlers: function() {
// 1 scroll handler
var bodyContainer = this.getElement("div", "imxt-body-container1");
if (bodyContainer.imxtAttached != true) {
addListener(bodyContainer, "scroll", this.onScroll, this, true);
bodyContainer.imxtAttached = true;
}
// 2 column resize handlers
var handles = this.getHandles();
for (var i = 0; i < handles.length; ++i) {
var h = handles[i];
if (h.imxtAttached != true) {
new InMethod.Drag(h, this.handleDragBegin, this.handleDragEnd,
this.handleDrag, this);
addListener(h, "dblclick", this.handleDoubleClick, this);
h.imxtAttached = true;
}
}
// 3 columns reorder handlers
var columns = getChildren(this.getColumnsRow(), "TH");
for (var i = 0; i < columns.length; ++i) {
var c = columns[i];
if (!hasClass(c, "imxt-padding") && c.imxtReorderable == true && c.imxtAttached != true) {
new InMethod.Drag(c, this.columnDragBegin, this.columnDragEnd, this.columnDrag, this);
c.imxtAttached = true;
}
}
// 4 prelights
this.attachPrelight();
},
/**
* Called when user starts to drag a column (reorder)
* @param column
* @param event
*/
columnDragBegin: function(column, event) {
Wicket.Focus.lastFocusId = null;
addClass(column, "imxt-dragging");
this.dragging = true;
this.initDragProxy(event, column);
this.columnDragBeginX = event.pageX;
this.columnDragBeginY = event.pageY;
this.columnDragBeginTime = new Date().getTime();
},
/**
* Called after user finished dragging a column (reorder)
* @param column
*/
columnDragEnd: function(column) {
this.cachedColumns = null;
// targetColumn is array [column, position] where position is 0 or 1
var target = this.targetColumn;
var submitState = false;
if ($.isArray(target)) {
// find the delta - how many columns right or left has the source
// column been moved
var delta = 0;
var c = column;
// try moving left
do {
c = prevSibling(c);
--delta;
} while (typeof c !== 'undefined' && c !== null && c != target[0]);
// if the column hasn't been found in left siblings, try moving right
if (typeof c === 'undefined' || c === null) {
c = column;
delta = 0;
do {
c = nextSibling(c);
++delta;
} while (typeof c !== 'undefined' && c !== null && c != target[0]);
--delta;
}
// if we found target column, do the actual reordering
if (typeof c !== 'undefined' && c !== null) {
delta += target[1];
this.moveColumn(column, delta);
submitState = true;
}
}
this.dragging = false;
removeClass(column, "imxt-dragging");
this.hideArrows();
this.hideDragProxy();
this.updatePrelight();
if (submitState) {
this.submitColumnState();
}
},
/**
* Initializes the drag proxy. If the drag proxy doesn't exist yet, creates it.
* This method doesn't make the proxy visible. Proxy is made visible in updateDragProxy.
* @param ev
* @param column
*/
initDragProxy: function(ev, column) {
var p = Wicket.$("imxt-drag-proxy");
if (typeof p === 'undefined' || p === null) {
p = document.createElement("div");
document.body.appendChild(p);
p.setAttribute("id", "imxt-drag-proxy");
p.innerHTML = "<div id='imxt-drag-proxy1'></div>";
}
var p1 = Wicket.$("imxt-drag-proxy1");
var columnPos = $(column).offset();
var mousePos = [ev.pageX, ev.pageY];
// get the initial proxy position relative to mouse cursor
ev.data.dragProxyDX = -column.offsetWidth / 2; //columnPos[0] - mousePos[0];
ev.data.dragProxyDY = -column.offsetHeight / 2; //columnPos[1] - mousePos[1];
// initialize dimensions
p.style.width = column.offsetWidth + "px";
p1.style.height = (column.offsetHeight - 3) + "px";
p.style.display = "none";
this.setPosition(p, [columnPos.left,columnPos.top]);
},
/**
* Moves the drag proxy to mouse cursor position.
* @param ev
*/
updateDragProxy: function(ev) {
var p = Wicket.$("imxt-drag-proxy");
p.style.display = "";
this.setPosition(p, [ev.pageX + ev.data.dragProxyDX, ev.pageY + ev.data.dragProxyDY]);
},
/**
* Hides the drag proxy.
*/
hideDragProxy: function() {
var p = Wicket.$("imxt-drag-proxy");
p.style.display = "none";
},
/**
* Returns the arrow element with specified id (should be either 'imxt-arrow-down' or 'imxt-arrow-up').
* If the arrow element doesn't exist, creates it.
* @param id
*/
getArrow: function(id) {
var a = Wicket.$(id);
if (typeof a === 'undefined' || a === null) {
a = document.createElement("a");
a.setAttribute("id", id);
document.body.appendChild(a);
}
return a;
},
setPosition: function(a, xy) {
$(a).css({left: xy[0], top: xy[1]});
},
/**
* Shows arrows (both up and down) at the specified position)
* @param column
* @param pos
*/
showArrows: function(column, pos) {
var a1 = this.getArrow("imxt-arrow-down");
var a2 = this.getArrow("imxt-arrow-up");
a1.style.display = "";
a2.style.display = "";
var xy = $(column).offset();
var x = xy.left + (pos * column.offsetWidth);
var y = xy.top;
this.setPosition(a1, [x - 7, y - 14]);
this.setPosition(a2, [x - 7, y - 2 + column.offsetHeight]);
// mark the target column - used in columnDragEnd()
this.targetColumn = [column, pos];
},
/**
* Hides the arrows
*/
hideArrows: function() {
var a1 = this.getArrow("imxt-arrow-down");
var a2 = this.getArrow("imxt-arrow-up");
a1.style.display = "none";
a2.style.display = "none";
this.targetColumn = null;
},
columnAndPrevSiblingsNotReorderable: function(column) {
while (typeof column !== 'undefined' && column !== null) {
if (column.imxtReorderable == true) {
return false;
}
column = prevSibling(column);
}
return true;
},
columnAndNextSiblingsNotReorderable: function(column) {
while (typeof column !== 'undefined' && column !== null) {
if (column.imxtReorderable == true) {
return false;
}
column = nextSibling(column);
}
return true;
},
/**
* Invoked when user is dragging a column (reorder).
* @param column
* @param dX
* @param dY
* @param event
*/
columnDrag: function(column, dX, dY, event) {
var columns = this.cachedColumns;
if (typeof(columns) === "undefined" || columns === null) {
columns = getChildren(this.getColumnsRow(), "TH");
this.cachedColumns = columns;
}
var x = event.pageX;
var y = event.pageY;
if (Math.abs(this.columnDragBeginY - y) < 10 &&
Math.abs(this.columnDragBeginX - x) < 15 &&
new Date().getTime() - this.columnDragBeginTime < 1000) {
return;
}
// find the column on cursor position
var c = null;
for (var i = 0; i < columns.length; ++i) {
cx = columns[i];
var pos = $(cx).offset();
if (x >= pos.left && x < pos.left + cx.offsetWidth) {
c = cx;
break;
}
}
// if the column is a column other than the column being dragged
if (typeof c !== 'undefined' && c != column && c !== null) {
// find if the cursor is in left or right half of column
var c_x = $(cx).offset().left;
var x = event.pageX - c_x;
var w = c.offsetWidth;
var pos = x < w / 2 ? 0 : 1;
if ((pos == 0 && c == nextSibling(column)) ||
(pos == 0 && this.columnAndPrevSiblingsNotReorderable(c)) ||
(pos == 1 && !hasClass(nextSibling(c), "imxt-reorderable") && this.columnAndPrevSiblingsNotReorderable(c)) ||
(pos == 1 && c == prevSibling(column)) ||
(pos == 1 && this.columnAndNextSiblingsNotReorderable(c)) ||
(pos == 0 && !hasClass(prevSibling(c), "imxt-reorderable") && this.columnAndNextSiblingsNotReorderable(c)) ||
(pos == 1 && hasClass(c, "imxt-padding"))) {
// the source column can't be dragged here
this.hideArrows();
} else {
// this is a valid destination
this.showArrows(c, pos);
}
} else {
this.hideArrows();
}
// move drag proxy to follow the cursor
this.updateDragProxy(event);
},
getOriginalColSpan: function(column) {
var res = column.className.match(colSpanRegex);
if (res) {
return parseInt(res[1], 10);
} else {
return 1;
}
},
/**
* Reorders the column according to the delta (positive or negative number)
* @param column
* @param delta
*/
moveColumn: function(column, delta) {
while (column.tagName.toLowerCase() !== "th") {
column = column.parentNode;
}
// bug in opera - we need to hide both tables during reordering,
// otherwise the tables will not be refreshed afterwards
var bodyContainer1;
var scroll;
if (this.isOpera) {
bodyContainer1 = this.getElement("div", "imxt-body-container1");
scroll = bodyContainer1.scrollLeft;
this.getHeadTable().style.display="none";
this.getBodyTable().style.display="none";
}
// the column might not be prelight after reordering
removeClass(column, "imxt-prelight");
var header = this.getColumnsRow();
var ths = getChildren(header, "TH");
// get the source column index
var index;
for (var i = 0; i < ths.length; ++i) {
if (ths[i] == column) {
index = i;
break;
}
}
var other = ths[i + delta];
var fixSpans = Wicket.bind(function(cells) {
var hide = 0;
for (var i = 0; i < cells.length; ++i) {
var cell = cells[i];
var left = cells.length - i - 1;
if (hide > 0) {
cell.setAttribute("colSpan", 1);
cell.style.display = "none";
--hide;
} else {
var span = this.getOriginalColSpan(cell);
if (span > left) {
span = left;
}
hide = span - 1;
cell.style.display = "";
cell.setAttribute("colSpan", span > 0 ? span : 1);
}
}
}, this);
// helper function that reorders columns in the given rows
var updateRows = Wicket.bind(function(rows, fixSpans) {
for (var j = 0; j < rows.length; ++j) {
var row = rows[j];
var tds = getChildren(row, ["TD", "TH"]);
var current = tds[i];
if (typeof(current) === "undefined") {
continue;
}
var index = i + delta;
if (delta > 0) {
++index;
}
other = tds[index];
row.insertBefore(current, other);
if ($.isFunction(fixSpans)) {
fixSpans(getChildren(row, ["TD", "TH"]));
}
}
}, this);
// update rows in both tables
updateRows(this.getHeadRows());
updateRows(this.getBodyRows(), fixSpans);
// show the tables in opera
if (this.isOpera) {
this.getHeadTable().style.display="";
this.getBodyTable().style.display="";
bodyContainer1.scrollLeft = scroll;
}
// we might have changed the layout
this.update(true);
},
/**
* To get around IE not supporting :hover on all elements but links, we attach special
* onmouseover and onmouseout handlers on elements with "imxt-want-prelight" class set
* that add and remove the imxt-prelight css class on element when mouse hovers it.
*/
attachPrelight: function() {
var elements = this.getElements("*", "imxt-want-prelight");
attachPrelight(elements, this);
// Issue #44:
// Workaround for an IE9 rendering bug. IE9 miscalculates
// the tables sizes on hover in/out ... This leads to issue #44.
//
// We just force the browser to do a full recaluclate on hover.
if (Wicket.Browser.isIE9()) {
var _this = this;
function fixIE9Layout() {
$(_this.getBodyTable()).parent().css({
'overflow' : 'auto'
});
}
var elements = _this.getElements("tr", "imxt-want-prelight");
for (var i = 0; i < elements.length; i++)
$(elements[i]).hover(fixIE9Layout);
$(_this.getBodyTable()).hover(fixIE9Layout);
$(_this.getElement("div", "imxt-bottom-toolbar-container")).hover(fixIE9Layout);
// If there is a custom link in a table cell it can trigger the same problem on hover ...
$(_this.getBodyTable()).find("a").hover(fixIE9Layout);
}
},
/**
* When the mouseover/mouseout event handlers set a prelight flag, this method adds/removes
* the actual css class depending on the flag. This behavior is intentionally supressed
* during column resizing/reordering.
* If the specified element is undefined, the css class is updated on all elements with
* "imxt-want-prelight" css class.
* @param element
*/
updatePrelight: function(element) {
if (this.dragging != true) {
var update = Wicket.bind(function(e) {
var scrollLeft;
if (this.isOpera) {
// for some reason opera doesn't preserve the scroll offset when changing/removing style
bodyContainer1 = this.getElement("div", "imxt-body-container1");
scrollLeft = bodyContainer1.scrollLeft;
//e.style.visibility = "hidden";
}
if (e.imxtPrelight == true) {
addClass(e, "imxt-prelight");
} else {
removeClass(e, "imxt-prelight");
}
if (this.isOpera) {
//e.style.visibility = "";
bodyContainer1 = this.getElement("div", "imxt-body-container1");
bodyContainer1.scrollLeft = scrollLeft;
}
}, this);
if (typeof(element) !== "undefined") {
update(element);
} else {
var elements = this.getElements("*", "imxt-want-prelight", this.getHeadTable());
for (var i = 0; i < elements.length; ++i) {
update(elements[i]);
}
}
}
},
/**
* Returns the state of columns (order and widths) represented as string.
*/
getColumnState: function() {
var r = this.getColumnsRow();
var ths = getChildren(r, "TH");
var state = "";
for (var i = 0; i < ths.length - 1; ++i) {
var th = ths[i];
state += th.imxtId;
state += ",";
var width = th.style.width;
if (width.match(/[\d]+px/))
state += parseInt(th.style.width, 10);
else
state += "-1";
state += ";";
};
return state;
},
submitColumnState: function() {
this.columnsStateCallback(this.getColumnState());
},
/**
* Needs to be called when a row was added or updated.
* @param rowElement
*/
rowUpdated: function(rowElement) {
var elements = this.getElements("*", "imxt-want-prelight", rowElement);
if (hasClass(rowElement, "imxt-want-prelight")) {
elements.push(rowElement);
}
attachPrelight(elements, this);
this.updateSelectCheckBoxes();
this.initCells(rowElement);
},
getCellId: function(cell) {
if (typeof(cell.imxtId) !== "string") {
var index = 0;
var c = cell;
while ((c = prevSibling(c)) != null) {
++index;
}
var row = this.getColumnsRow();
var headerCells = getChildren(row, "TH");
var headerCell = headerCells[index];
if (typeof(headerCell.imxtId) === "string") {
cell.imxtId = headerCell.imxtId;
} else {
cell.imxtId = "";
}
}
return cell.imxtId;
},
initCellsEventHandler: function(event) {
var cell = event.target;
var table = event.data;
//Needed because when jquery handles cell event, return cell contents instead
//of td cell.
if(cell.tagName.toLowerCase() !== 'td' | !$(cell).hasClass('imxt-cell')){
cell =$(cell).parents("td.imxt-cell")[0];
}
cell.parentNode.imxtClickedColumn = table.getCellId(cell);
},
initCells: function(container) {
var elements = this.getElements("td", "imxt-cell", container);
for (var i = 0; i < elements.length; ++i) {
var cell = elements[i];
if (cell.imxtInitialized != true) {
//var addListener = function(element, event, fn, obj, override) {
addListener(cell, "click", this.initCellsEventHandler, this, false);
cell.imxtInitialized = true;
}
}
}
};
InMethod.XTable.canSelectRow = function(event) {
var e = Wicket.Event.fix(event);
var element = e.target ? e.target : e.srcElement;
while (typeof element !== 'undefined' && element !== null && element != document.documentElement) {
var tn = element.tagName.toLowerCase();
if (tn === "a" || tn === "input" || tn === "select" || tn === "button") {
return false;
}
element = element.parentNode;
}
return true;
};
InMethod.XTableManager = Wicket.Class.create();
InMethod.XTableManager.prototype = {
current: new Object(),
initialize: function() {
var interval = 100;
if (Wicket.Browser.isIELessThan7()) {
interval = 500;
}
window.setInterval(Wicket.bind(this.update, this), interval);
},
update: function() {
for (var property in this.current) {
var table = this.current[property];
if (typeof table !== 'undefined' && table !== null) {
var res = table.update();
if (res != true) {
this.current[property] = null;
}
}
};
},
updateTreeColumns: function() {
for (var property in this.current) {
var table = this.current[property];
if (typeof table !== 'undefined' && table !== null) {
table.updateTreeColumns();
}
};
},
register: function(id, columnsData, columnsStateCallback) {
var existing = this.current[id];
if (typeof existing === 'undefined' || existing === null) {
existing = new InMethod.XTable(id, columnsData, columnsStateCallback);
this.current[id] = existing;
} else {
// update existing
existing.initialize(id, columnsData, columnsStateCallback);
}
},
updateRow: function(id, row) {
var table = this.current[id];
if (typeof table !== 'undefined' && table !== null) {
table.rowUpdated(row);
}
}
};
InMethod.XTableManager.instance = new InMethod.XTableManager();
InMethod.setCursorPos = function(elm, begin, end) {
if (typeof elm.selectionStart !== "undefined" && typeof elm.selectionEnd !== "undefined") {
elm.setSelectionRange (begin, end);
elm.focus ();
} else if (document.selection && document.selection.createRange) {
var range = elm.createTextRange ();
range.move ("character", begin);
range.moveEnd ("character", end - begin);
range.select ();
}
};
function findParent(node, tagName) {
return $(node).parents(tagName);
}
onKeyEvent = function(element, event) {
var e = Wicket.Event.fix(event)
var key = event.keyCode;
if (key == 13 || key == 27) {
if (key == 13 && Wicket.Browser.isSafari()) {
// somewhat ugly fix but this is the only thing preventing safari from submitting the form on enter
var form = findParent(element, "FORM");
if (form != null) {
form.imxtOldOnSubmit = form.onsubmit;
form.onsubmit=function() { return false; };
window.setTimeout(function() { form.onsubmit = form.imxtOldOnSubmit; form.imxtOldOnSubmit = null;}, 100);
}
}
var row = element;
do {
row = findParent(row, "TR");
} while (typeof row !== 'undefined' && row !== null
&& !hasClass(findParent(row, "TABLE"), "imxt-body"));
if (typeof row !== 'undefined' && row !== null) {
var elements;
if (key == 13) {
elements = $(row).find("a.imxt-edit-submit");
} else {
elements = $(row).find("a.imxt-edit-submit");
}
if (typeof elements !== 'undefined' && elements !== null && elements.length > 0) {
$(elements[0]).click();
}
}
}
};
InMethod.editKeyUp = function(element, event) {
if (!Wicket.Browser.isOpera() && !Wicket.Browser.isSafari()) {
onKeyEvent(element, event);
}
var e = Wicket.Event.fix(event)
var key = event.keyCode;
if (key == 13 || key == 27) {
return false;
} else {
return true;
}
};
InMethod.editKeyPress = function(element, event) {
if (Wicket.Browser.isOpera() || Wicket.Browser.isSafari()) {
return onKeyEvent(element, event);
}
var e = Wicket.Event.fix(event)
var key = event.keyCode;
if (key == 13 || key == 27) {
return false;
} else {
return true;
}
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment