// x.js, X v3.15.4, Cross-Browser.com DHTML Library
// Copyright (c) 2004 Michael Foster, Licensed LGPL (gnu.org)
// havily edited (c) 2005-2016 Walter Bislin, walter.bislins.ch
//
// 15.02.2007: Change: xOnLoad()/xOnUnload(): functions are called in order of registration
// 24.11.2009: Fix: xPageX(), xPageY(): correction with xScrollLeft() and xScrollTop()
// 10.12.2009: new: xDisplay(), xIsDisplayed(), xOnDisplay()
// 30.01.2013: new: xAddClass(), xHasClass(), xRemoveClass(), xSetCookie(), xGetCookie(), xDeleteCookie()
// 16.06.2014: new: xObj()..xBool(), xIsNumeric(), xDefAny()..xDefBool(); see Wiki Page: "xDef, xAny, xObj and more"
// 13.10.2014: New: xTransform() functions
// 14.10.2014: Improved type checking using methods from jQuery 1.11.1
// 15.10.2014: New: xFStr(), xTransformOrigin()
// 01.12.2014: Added: requestAnimationFrame() support
// 05.12.2014: New: support for classList added in xAddClass(), xHasClass(), xRemoveClass(); added xToggleClass()
// 13.12.2014: Optimizations
// 16.04.2015: Changed: xOpacity(): opacity range = 0..1 (no more 0..100)
// 01.05.2015: New: xDataset()
// 05.05.2015: New: xNaturalWidth() and xNaturalHeight() for Image elements; xImgOnLoad(); xOnDomReady()
// 08.05.2015: New: Polyfill for Object.create(), SubClass.inheritsFrom(parentClass), this.parentClass.constructor.call(...)
// 20.01.2016: Support for IE8 removed!
// Changed: xGetElementById() -> xElement(); xClipboard() -> xToClipboard()
// New: xGet() (shortcut for document.getElementById())
// 12.02.2016: Changed: xAddEvent() registers events such, so they return an xEvent instead a nativ event
// xAddEventListener() -> xAddEvent();
// xRemoveEventListener() -> xRemoveEvent();
// xGetElementsByTagName() -> xGetByTag()
// 24.02.2016: Fixed: xSetCW(), xSetCH(): BorderBoxSizing was inverted
// 12.06.2016: Fixed: xIsDisplayed() gets false when an elements display is set none via a class too
// New: xGetByClass(), xIsElementAndNotRoot()
// Changed: xIsRoot(e) returns false, if e is not defined or null
// Fixed: xHasClass(), xAddClass(), xRemoveClass() are now working correct if xIsRoot(element)
// 29.06.2016 Changed: Functions in CallbackChains are now called in order of registration
// 20.07.2016 New: xOnResize()
// 14.08.2016 New: xArrForEach,xArrFind,xArrRemove..
// Change: xCallbackChain: linked list implementation changed to array
// New/Change: xEventManager implements all event functions; some event functions renamed
// 13.09.2016 New: xGetFirst(), xGetAll()
// 11.10.2016 New: polyfill für Math.log10()
var xClass2Type = {};
(function(){
// see jQuery 1.11.1
var types = "Boolean Number String Function Array Date RegExp Object Error".split(" ");
var len = types.length;
for (var i = 0; i < len; i++) {
var name = types[i];
xClass2Type[ "[object " + name + "]" ] = name.toLowerCase();
}
})();
function xType(obj) {
// see jQuery 1.11.1
if (obj == null) return obj + "";
return typeof obj === "object" || typeof obj === "function" ?
xClass2Type[Object.prototype.toString.call(obj)] || "object" :
typeof obj;
}
function xDef(x) { // same as xAnyOrNull()
// true if x is defined (null -> true)
return (typeof(x) !== 'undefined');
}
function xAny(x) {
// true if x is any object (defined and not null, null -> false)
return (typeof(x) !== 'undefined' && x !== null);
}
function xObj(x) {
// true if x is object (not: null, array, boolean, number, string, function)
return (typeof(x) === 'object' && !xArray(x) && x !== null);
}
function xObjOrNull(x) {
// true if x is object or null (not: array, boolean, number, string, function)
return (typeof(x) === 'object' && !xArray(x));
}
function xFunc(x) {
// true if x is Function and not null
// see jQuery 1.11.1
return xType(x) === 'function';
}
function xFuncOrNull(x) {
// true if x is Function or null
// see jQuery 1.11.1
return (x === null || xType(x) === 'function');
}
var xArray = Array.isArray || function( obj ) {
// true if x is array (not object, null, ...)
// see jQuery 1.11.1
return xType(obj) === 'array';
};
function xStr(x) {
// true if x is String
return (typeof(x) === 'string');
}
function xNum(x) {
// true if x is Number (not string)
return (typeof(x) === 'number');
}
function xBool(x) {
// true if x is Boolean (not: number, string)
return (typeof(x) === 'boolean');
}
function xIsNumeric(x) {
// returns true if x is type number or plain numeric string. '100%' -> false!
// see jQuery 1.11.1
return (!xArray(x) && (x - parseFloat(x) >= 0));
}
function xDefAny( aRef, aDefault ) {
// if aRef is not undefined and not null return aRef else aDefault (aRef === null -> aDefault)
return (typeof(aRef) === 'undefined' || aRef === null) ? aDefault : aRef;
}
function xDefAnyOrNull( aRef, aDefault ) {
// if aRef is not undefined return aRef else aDefault (aRef === null -> null (aRef))
return (typeof(aRef) === 'undefined') ? aDefault : aRef;
}
function xDefObj( aRef, aDefault ) {
// if aRef is object and not null return aRef else aDefault (aRef === null -> aDefault)
return (typeof(aRef) === 'object' && !xArray(aRef) && aRef !== null) ? aRef : aDefault;
}
function xDefObjOrNull( aRef, aDefault ) {
// if aRef is object or null return aRef else aDefault (aRef === null -> null (aRef))
return (typeof(aRef) === 'object' && !xArray(aRef)) ? aRef : aDefault;
}
function xDefFunc( aRef, aDefault ) {
// if aRef is function and not null return aRef else aDefault (aRef === null -> aDefault)
return xFunc(aRef) ? aRef : aDefault;
}
function xDefFuncOrNull( aRef, aDefault ) {
// if aRef is function or null return aRef else aDefault (aRef === null -> aRef)
return xFuncOrNull(aRef) ? aRef : aDefault;
}
function xDefArray( aRef, aDefault ) {
// if aRef is array and not null return aRef else aDefault (aRef === null -> aDefault)
return xArray(aRef) ? aRef : aDefault;
}
function xDefStr( aRef, aDefault ) {
// if aRef is a string return aRef else aDefault (aRef === null or undefined -> aDefault)
return (typeof(aRef) === 'string') ? aRef : aDefault;
}
function xDefNum( aRef, aDefault ) {
// if aRef is is a number return aRef else aDefault (aRef === null or undefined -> aDefault)
return (typeof(aRef) === 'number') ? aRef : aDefault;
}
function xDefBool( aRef, aDefault ) {
// if aRef is a boolean return aRef else aDefault (aRef === null or undefined -> aDefault)
return (typeof(aRef) === 'boolean') ? aRef : aDefault;
}
function xArgsToArray( args ) {
// converts an object of type arguments to an array
return Array.prototype.slice.call(args);
}
function xFStr( format, etc ) {
// builds a string: xFStr( 'translate(#px,#px)', 20, 30 ) -> 'translate(20px,30px)'
var arg = arguments;
var i = 1;
return format.replace( /(#(#)?)/g, function(match,p1,p2){ return p2 || arg[i++]; } );
}
// some Array functions ---------------------------
function xArrFind( start, arr, func, thisArg ) {
// or xArrFind( arr, func, thisArg )
// func.call( thisArg, arr[i], i, arr ): bool -> true if arr[i] maches
// require start>=0 && xArray(arr) && xFunc(func)
// returns arr element or undefined
if (!xNum(start)) return xArrFind( 0, start, arr, func );
var t, undef
if (arguments.lenth > 3) t = thisArg;
var n = arr.length;
for (var i = start; i < n; i++) {
if (func.call( t, arr[i], i, arr )) return arr[i];
}
return undef;
}
function xArrFindIndex( start, arr, func, thisArg ) {
// or xArrFindIndex( arr, func, thsArg )
// func.call( thisArg, arr[i], i, arr ): bool -> true if arr[i] maches
// require start>=0 && xArray(arr) && xFunc(func)
// returns element index or -1
if (!xNum(start)) return xArrFindIndex( 0, start, arr, func );
var t
if (arguments.lenth > 3) t = thisArg;
var n = arr.length;
for (var i = start; i < n; i++) {
if (func.call( t, arr[i], i, arr )) return i;
}
return -1;
}
function xArrForEach( arr, func, thisArg ) {
// func.call( thisArg, arr[i], i, arr )
// require xArray(arr) && xFunc(func)
var t
if (arguments.length > 2) t = thisArg;
var n = arr.length;
for (var i = 0; i < n; i++) {
func.call( t, arr[i], i, arr );
}
}
function xArrayMap( arr, func, thisArg ) {
// func.call( thisArg, arr[i], i, arr ): any -> newArr[i]
// require xArray(arr) && xFunc(func)
// returns newArr
var t
if (arguments.length > 2) t = thisArg;
var n = arr.length;
var newArr = Array(n);
for (var i = 0; i < n; i++) {
newArr[i] = func.call( t, arr[i], i, arr );
}
return newArr;
}
function xArrRemove( arr, func, thisArg ) {
// func.call( thisArg, arr[i], i, arr ): bool
// removes first element from arr where func returns true and stops there
// returns removed element or undefined
// require xArray(arr) && xFunc(func)
var t, undef
if (arguments.length > 2) t = thisArg;
var i = 0;
while (i < arr.length) {
if (func.call( t, arr[i], i, arr )) {
var ele = arr[i];
arr.splice( i, 1 );
return ele;
} else {
i++;
}
}
return undef;
}
function xArrRemoveAll( arr, func, thisArg ) {
// func.call( thisArg, arr[i], i, arr ): bool
// removes all elements from arr where func returns true
// returns number of removed elements
// require xArray(arr) && xFunc(func)
var t
if (arguments.length > 2) t = thisArg;
var n = 0;
var i = 0;
while (i < arr.length) {
if (func.call( t, arr[i], i, arr )) {
arr.splice( i, 1 );
n++;
} else {
i++;
}
}
return n;
}
// DOM functions ----------------------------
function xGet(id) {
return document.getElementById(id);
}
function xGetFirst(selectors) {
return document.querySelector(selectors);
}
function xGetAll(selectors) {
return document.querySelectorAll(selectors);
}
function xElement(e) {
return (typeof(e) === 'string') ? document.getElementById(e) : e;
}
function xDataset(e,name) {
return xElement(e).getAttribute( 'data-'+name );
}
function xInnerHTML(e,t) {
if (xStr(t)) {
xElement(e).innerHTML = t;
} else {
t = xElement(e).innerHTML;
}
return t;
}
function xInnerText(e,defaultText) {
e = xElement(e);
if (xDef(e.innerText)) {
return e.innerText;
}
if (xDef(e.textContent)) {
return e.textContent;
}
return defaultText;
}
function xTagName(e) {
// returns tag name in upper case
return xElement(e).tagName;
}
function xShow(e) { xVisibility(e, 1); }
function xHide(e) { xVisibility(e, 0); }
function xVisibility(e,v)
// v: Boolean or Integer (0,>0) or String ('visible', 'hidden')
{
if (xDef(v)) {
if (!xStr(v)) v = v ? 'visible' : 'hidden';
xElement(e).style.visibility = v;
} else {
v = xElement(e).style.visibility;
}
return v;
}
function xDisplay(e,v) {
// v = '' (default), 'none', 'inline', 'block', ...
// calls functions registered with xOnDisplay()
e = xElement(e);
var old = e.style.display;
if (xStr(v)) {
e.style.display = v;
if (old != v) xTriggerEventDisplayChange(e);
} else {
v = old;
}
return v;
}
function xIsDisplayed(e) {
// checks wether e and all parents are displayed by checking the computed style 'display'
// note: visibility states caused by css rules are taken into account!
e = xElement(e);
while (xIsElementAndNotRoot(e)) {
if (getComputedStyle(e).getPropertyValue('display') == 'none') return false;
e = e.parentNode;
}
return true;
}
function xMoveTo(e,iX,iY) {
// iX, iY: Integer
e = xElement(e);
e.style.left = iX + 'px';
e.style.top = iY + 'px';
}
function xLeft(e,iX) {
// iX: Integer; return Integer
e = xElement(e);
if (xStr(e.style.left)) {
if (xNum(iX)) {
e.style.left = iX + 'px';
} else {
iX = parseInt(e.style.left,10);
if (isNaN(iX)) iX = 0;
}
}
else if (xDef(e.style.pixelLeft)) {
if (xNum(iX)) {
e.style.pixelLeft = iX;
} else {
iX = e.style.pixelLeft;
}
} else {
iX = 0;
}
return iX;
}
function xTop(e,iY) {
// iY: Integer; return Integer
e = xElement(e);
if (xStr(e.style.top)) {
if (xNum(iY)) {
e.style.top = iY + 'px';
} else {
iY = parseInt(e.style.top,10);
if (isNaN(iY)) iY=0;
}
}
else if (xDef(e.style.pixelTop)) {
if (xNum(iY)) {
e.style.pixelTop = iY;
}
else {
iY = e.style.pixelTop;
}
} else {
iY = 0;
}
return iY;
}
function xOpacity(e,uO) {
if (xNum(uO)) {
xElement(e).style.opacity = uO;
} else {
uO = xElement(e).style.opacity;
}
return uO;
}
function xResizeTo(e,uW,uH,bBorderBoxSizing) {
// bBorderBoxSizing: bool true -> use border box sizing model: uW, uH are the outer most pixels without margin
xWidth(e,uW,bBorderBoxSizing);
xHeight(e,uH,bBorderBoxSizing);
}
function xElementWidth(e,uW) {
if (xNum(uW)) {
xElement(e).width = uW;
}
else {
uW = xElement(e).width;
}
return uW;
}
function xWidth(e,uW,bBorderBoxSizing) {
// returns style.offsetWidth (visible element width + scrollbar + padding + border, not including margin)
// or sets style.width to pixels:
// * bBorderBoxSizing = true (default): style.width = uW - (padding + border) -> offsetWith = uW
// * bBorderBoxSizing = false: style.width = uW -> offsetWidth => uW + padding + border
// width returned is not affected by transform scale
// use xElementWidth to set or read e.width instead of e.style.width
// use xStyle(e,'width') to set or read e.style.width (my be undefined)
if (xNum(uW)) {
if (uW < 0) uW = 0;
uW = Math.round(uW);
xSetCW(xElement(e),uW,bBorderBoxSizing);
} else {
uW = xElement(e).offsetWidth;
}
return uW;
}
function xScrollWidth(e) {
// returns the total width with padding, regardless how much of it is displayed
return xElement(e).scrollWidth;
}
function xNaturalWidth(img) {
// returns the natural width of an image element
img = xElement(img);
if (xDef(img.naturalWidth)) return img.naturalWidth;
var tmpImg = new Image();
tmpImg.src = img.src;
return tmpImg.width;
}
function xElementHeight(e,uH) {
if (xNum(uH)) {
xElement(e).height = uH;
} else {
uH = xElement(e).height;
}
return uH;
}
function xHeight(e,uH,bBorderBoxSizing) {
// returns style.offsetHeight (visible element height + scrollbar + padding + border, not including margin)
// or sets style.height to pixels:
// * bBorderBoxSizing = true (default): style.height = uH - (padding + border) -> offsetHeight = uH
// * bBorderBoxSizing = false: style.height = uH -> offsetHeight => uH + padding + border
// height returned is not affected by transform scale
// use xElementHeight to set or read e.height instead of e.style.height
if (xNum(uH)) {
if (uH < 0) uH = 0;
uH = Math.round(uH);
xSetCH(xElement(e),uH,bBorderBoxSizing);
}
else {
uH = xElement(e).offsetHeight;
}
return uH;
}
function xScrollHeight(e) {
// returns the total height with padding, regardless how much of it is displayed
return xElement(e).scrollHeight;
}
function xNaturalHeight(img) {
// returns the natural height of an image element
img = xElement(img);
if (xDef(img.naturalHeight)) return img.naturalHeight;
var tmpImg = new Image();
tmpImg.src = img.src;
return tmpImg.height;
}
function xGetCS(ele,sP){
return parseInt(window.getComputedStyle(ele,'').getPropertyValue(sP),10);
}
function xSetCW(ele,uW,bBorderBoxSizing){
// sets style.width to uW, if bBoderBoxSizing is false,
// or to uW - (padding + border) if bBorderBoxSizing is true (corresponding to style.offsetWidth)
var pl = 0, pr = 0, bl = 0, br = 0;
bBorderBoxSizing = xDefBool( bBorderBoxSizing, true );
var cssW = uW;
if (bBorderBoxSizing) {
if (window.getComputedStyle) {
pl = xGetCS(ele,'padding-left');
pr = xGetCS(ele,'padding-right');
bl = xGetCS(ele,'border-left-width');
br = xGetCS(ele,'border-right-width');
}
else if (xDef(ele.currentStyle)){
pl = parseInt(ele.currentStyle.paddingLeft,10);
pr = parseInt(ele.currentStyle.paddingRight,10);
bl = parseInt(ele.currentStyle.borderLeftWidth,10);
br = parseInt(ele.currentStyle.borderRightWidth,10);
}
else {
ele.style.width = uW + 'px';
pl = ele.offsetWidth - uW;
}
if (isNaN(pl)) pl = 0;
if (isNaN(pr)) pr = 0;
if (isNaN(bl)) bl = 0;
if (isNaN(br)) br = 0;
cssW -= (pl + pr + bl + br);
}
if (cssW < 0) cssW = 0;
ele.style.width = cssW + 'px';
}
function xSetCH(ele,uH,bBorderBoxSizing){
// sets style.height to uH, if bBoderBoxSizing is false,
// or to uH - (padding + border) if bBorderBoxSizing is true (corresponding to style.offsetHeight)
var pt = 0, pb = 0, bt = 0, bb = 0;
bBorderBoxSizing = xDefBool( bBorderBoxSizing, true );
var cssH = uH;
if (bBorderBoxSizing) {
if (window.getComputedStyle) {
pt = xGetCS(ele,'padding-top');
pb = xGetCS(ele,'padding-bottom');
bt = xGetCS(ele,'border-top-width');
bb = xGetCS(ele,'border-bottom-width');
}
else if (xDef(ele.currentStyle)){
pt = parseInt(ele.currentStyle.paddingTop,10);
pb = parseInt(ele.currentStyle.paddingBottom,10);
bt = parseInt(ele.currentStyle.borderTopWidth,10);
bb = parseInt(ele.currentStyle.borderBottomWidth,10);
}
else {
ele.style.height = uH + 'px';
pt = ele.offsetHeight - uH;
}
if (isNaN(pt)) pt = 0;
if (isNaN(pb)) pb = 0;
if (isNaN(bt)) bt = 0;
if (isNaN(bb)) bb = 0;
cssH -= (pt + pb + bt + bb);
}
if (cssH < 0) cssH = 0;
ele.style.height = cssH + 'px';
}
function xClientWidth() {
// returns width of viewport (inner width of window)
var w = 0;
if (document.compatMode == 'CSS1Compat' && !window.opera && document.documentElement && document.documentElement.clientWidth) {
w = document.documentElement.clientWidth;
}
else if (document.body && document.body.clientWidth) {
w = document.body.clientWidth;
}
else if (xDef(window.innerWidth) && xDef(window.innerHeight) && xDef(document.height)) {
w = window.innerWidth;
if (document.height > window.innerHeight) {
w -= 16;
}
}
return w;
}
function xClientHeight() {
// returns height of viewport (inner height of window)
var h = 0;
if (document.compatMode == 'CSS1Compat' && !window.opera && document.documentElement && document.documentElement.clientHeight) {
h = document.documentElement.clientHeight;
}
else if (document.body && document.body.clientHeight) {
h = document.body.clientHeight;
}
else if (xDef(window.innerWidth) && xDef(window.innerHeight) && xDef(document.width)) {
h = window.innerHeight;
if (document.width > window.innerWidth) {
h -= 16;
}
}
return h;
}
function xPageX(e) {
// returns x position in pixels relative to page
e = xElement(e);
var x = 0;
var n = e;
while (xIsElementAndNotRoot(e)) {
x += e.offsetLeft;
e = e.offsetParent;
}
// correct by all scrollings
e = n;
while (xIsElementAndNotRoot(e)) {
x -= e.scrollLeft;
e = e.parentNode;
}
return x;
}
function xPageY(e) {
// returns y position in pixels relative to page
e = xElement(e);
var y = 0;
var n = e;
while (xIsElementAndNotRoot(e)) {
y += e.offsetTop;
e = e.offsetParent;
}
// correct by all scrollings
e = n;
while (xIsElementAndNotRoot(e)) {
y -= e.scrollTop;
e = e.parentNode;
}
return y;
}
function xIsRoot(e) {
// e: dom element or null or undefined.
// return true if e is defined and not null and a root element like document, html or body
return (xAny(e) && (e == document || e.tagName == 'HTML' || e.tagName == 'BODY'));
}
function xIsElementAndNotRoot(e) {
// e: dom element or null or undefined. Use xGet() to find dom elements by id
// return true, if e is not undefined and not null and no dom element like document, html or body
return (xAny(e) && !(e == document || e.tagName == 'HTML' || e.tagName == 'BODY'));
}
function xScrollLeft(e,bWin,val) {
var offset = 0;
if (!xDef(e) || bWin || xIsRoot(e)) {
var w = window;
if (bWin && e) w = e;
if (w.document.documentElement && w.document.documentElement.scrollLeft) {
if (xNum(val)) {
w.document.documentElement.scrollLeft = val;
} else {
offset = w.document.documentElement.scrollLeft;
}
}
else if (w.document.body && xDef(w.document.body.scrollLeft)) {
if (xNum(val)) {
w.document.body.scrollLeft = val;
} else {
offset = w.document.body.scrollLeft;
}
}
}
else {
if (xNum(val)) {
xElement(e).scrollLeft = val;
} else {
offset = xElement(e).scrollLeft;
}
}
return offset;
}
function xScrollTop(e,bWin,val)
{
var offset = 0;
if (!xDef(e) || bWin || xIsRoot(e)) {
var w = window;
if (bWin && e) w = e;
if (w.document.documentElement && w.document.documentElement.scrollTop) {
if (xNum(val)) {
w.document.documentElement.scrollTop = val;
} else {
offset = w.document.documentElement.scrollTop;
}
}
else if (w.document.body && xDef(w.document.body.scrollTop)) {
if (xNum(val)) {
w.document.body.scrollTop = val;
} else {
offset = w.document.body.scrollTop;
}
}
}
else {
if (xNum(val)) {
xElement(e).scrollTop = val;
} else {
offset = xElement(e).scrollTop;
}
}
return offset;
}
function xZIndex(e,uZ)
{
if (xNum(uZ)) {
xElement(e).style.zIndex = uZ;
} else {
uZ = parseInt(xElement(e).style.zIndex,10);
}
return uZ;
}
function xCursor(e,c)
{
if (xStr(c)) {
xElement(e).style.cursor = c;
} else {
c = xElement(e).style.cursor;
}
return c;
}
function xStyle(e,sStyle,sVal) {
if (xDef(sVal)) {
xElement(e).style[sStyle] = sVal;
} else {
sVal = xElement(e).style[sStyle];
}
return sVal;
}
function xMaskRegExp(s) {
return s.replace( /\-/g, '\\-' );
}
function xHasClass(e,cls) {
// returns false if e is undefined or null or root element
if (!(e = xElement(e))) return false;
if (xDef(e.classList)) {
// assert xIsElementAndNotRoot()
return e.classList.contains(cls);
} else {
if (xIsRoot(e)) return false;
return e.className.match(new RegExp('(\\s|^)' + xMaskRegExp(cls) + '(\\s|$)'));
}
}
function xAddClass(e,cls) {
// does nothing if e is undefined or null or root element
if (!(e = xElement(e))) return;
if (xDef(e.classList)) {
// assert xIsElementAndNotRoot()
e.classList.add(cls);
} else {
if (xIsRoot(e)) return;
if (!this.xHasClass(e,cls)) e.className += ' ' + cls;
}
}
function xRemoveClass(e,cls) {
// does nothing if e is undefined or null or root element
if (!(e = xElement(e))) return;
if (xDef(e.classList)) {
// assert xIsElementAndNotRoot()
e.classList.remove(cls);
} else {
if (xIsRoot(e)) return;
if (xHasClass(e,cls)) {
var reg = new RegExp('(\\s|^)' + xMaskRegExp(cls) + '(\\s|$)');
e.className = e.className.replace( reg, ' ' ).replace( /^\s+|\s+$/g, '' );
}
}
}
function xToggleClass(e,cls) {
// does nothing if e is undefined or null or root element
if (!(e = xElement(e))) return;
if (xDef(e.classList)) {
// assert xIsElementAndNotRoot()
e.classList.toggle(cls);
} else {
if (xHasClass(e,cls)) {
xRemoveClass(e,cls);
} else {
xAddClass(e,cls);
}
}
}
function xSetClassIf(e,cond,cls) {
// does nothing if e is undefined or null or root element
// if cond is true then cls is added else removed from e
if (cond) {
xAddClass(e,cls);
} else {
xRemoveClass(e,cls);
}
}
function xSetEnabled(e,enabled,cls) {
xSetClassIf(e,enabled,xDefStr(cls,'enabled'));
}
function xSetDisabled(e,disabled,cls) {
xSetClassIf(e,disabled,xDefStr(cls,'disabled'));
}
// HTML-Tree functions
function xParent(e,bNode) {
bNode = xDefBool( bNode, true );
if (bNode) {
return xElement(e).parentNode;
}
else {
return xElement(e).offsetParent;
}
}
function xCreateElement(sTag) {
return document.createElement(sTag);
}
function xCreateTextNode(s) {
return document.createTextNode(s);
}
function xHasChildNodes(oParent) {
return oParent.hasChildNodes();
}
function xChildNodes(oParent) {
return oParent.childNodes;
}
function xAppendChild(oParent, oChild) {
return oParent.appendChild(oChild);
}
function xInsertBefore(oParent, oChild, oRef) {
return oParent.insertBefore(oChild,oRef);
}
function xRemoveChild(oParent, oChild) {
return oParent.removeChild(oChild);
}
function xGetByTag(t,p) {
t = t || '*';
p = p || document;
return p.getElementsByTagName(t);
}
function xGetByClass(className,p) {
p = p || document;
return p.getElementsByClassName(className);
}
function xAddEvent(e,eventType,callback,cap)
// e: string element id or element
// eventType: string event type (click, load, resize, ...)
// callback: function(xEvent) to call back
// cap: false -> call on bubble up phase like IE
// Note: callback is passed an new xEvent not the nativ event object, so its the same in all browsers!
{
cap = xDefBool( cap, false );
var wrapper = function xOnCallbackEventWrapper(e){ callback(new xEvent(e)); };
callback.xWrapperFunc = wrapper;
xElement(e).addEventListener(eventType,wrapper,cap);
}
function xRemoveEvent(e,eventType,callback,cap){
cap = xDefBool( cap, false );
xElement(e).removeEventListener(eventType,callback.xWrapperFunc,cap);
}
function xEvent(evt){
// standardizes xEvent object defines the following properties for all brwosers:
// event: Event = native Event object (evt)
// type: String
// target: dom object
// relatedTarget: dom object
// pageX, pageY: Integer
// offsetX, offsetY: Integer
// keyCode: Integer
// shiftKey,ctrlKey, altKey: Boolean
// button: Integer = mouse button pressed (0 = left, 1 = middle, 2 = rigth)
this.Init(evt);
}
xEvent.prototype.Init = function(evt){
var e = evt || window.event;
if (!e) return;
this.event = e;
this.type = e.type;
this.target = e.target || e.srcElement;
// defeat Safari bug
if (this.target.nodeType == 3) this.target = this.target.parentNode;
this.relatedTarget = e.relatedTarget;
if (e.type == 'mouseover') {
this.relatedTarget = e.fromElement;
}
else if (e.type == 'mouseout') {
this.relatedTarget = e.toElement;
}
if (xDef(e.pageX)) {
this.pageX = e.pageX;
this.pageY = e.pageY;
}
else if (xDef(e.clientX)) {
this.pageX = e.clientX + xScrollLeft();
this.pageY = e.clientY + xScrollTop();
}
if (xDef(e.offsetX)) {
this.offsetX = e.offsetX;
this.offsetY = e.offsetY;
}
else {
this.offsetX = this.pageX - xPageX(this.target);
this.offsetY = this.pageY - xPageY(this.target);
}
this.keyCode = e.keyCode || e.which || 0;
this.shiftKey = e.shiftKey;
this.ctrlKey = e.ctrlKey;
this.altKey = e.altKey;
if (typeof e.type == 'string') {
if (e.type.indexOf('click') != -1) {
this.button = 0;
}
else if (e.type.indexOf('mouse') != -1) {
this.button = e.button;
if (e.button & 1) {
this.button = 0;
}
else if (e.button & 4) {
this.button = 1;
}
else if (e.button & 2) {
this.button = 2;
}
}
}
};
xEvent.prototype.PreventDefault = function() {
if (!this.event) return;
if (this.event.preventDefault) this.event.preventDefault();
this.event.returnValue = false;
};
xEvent.prototype.StopPropagation = function() {
if (!this.event) return;
if (this.event.stopPropagation) this.event.stopPropagation();
this.event.cancelBubble = true;
};
function xImgOnLoad(img,loadCallback,errorCallback) {
// calls loadCallback as soon as img is loaded. loadCallback is always called, even if img is already loaded!
// loadCallback( img )
// errorCallback( img, abort ); abort = false -> error loading image; abort = true -> loading aborted
// Implementation: a helper image is created, callbacks are installed there, then helper.src = img.src
// Note: callbacks are freed after first event happens by freeing helper image
img = xElement(img);
loadCallback = xDefFuncOrNull( loadCallback, null );
errorCallback = xDefFuncOrNull( errorCallback, null );
var helperImg = new Image();
img._xHelperImg = helperImg;
if (loadCallback) {
helperImg.addEventListener(
'load',
function CB_OnImgLoad(){
img._xHelperImg = null;
loadCallback(img);
},
false
);
}
if (errorCallback) {
helperImg.addEventListener(
'error',
function CB_OnImgError(){
img._xHelperImg = null;
errorCallback(img,false);
},
false
);
helperImg.addEventListener(
'abort',
function CB_OnImgAbort(){
img._xHelperImg = null;
errorCallback(img,true);
},
false
);
}
helperImg.src = img.src;
}
function xCallbackChain() {
this.FuncList = [];
this.Active = false;
}
xCallbackChain.prototype.Add = function( aFunc, once ) {
// added functions are called in order of registration
// once = true -> only add aFunc if not already in list
// returns true, if aFunc is added to list
once = xDefBool( once, false );
if (once && this.Containes(aFunc)) return false;
this.FuncList.push( aFunc );
return true;
}
xCallbackChain.prototype.Contains = function( aFunc ) {
return xDef(
xArrFind(
this.FuncList,
function CB_Compare_Funcs(func){
return func == aFunc;
}
)
);
}
xCallbackChain.prototype.Remove = function( aFunc ) {
// returns number of removed elements
return xArrRemoveAll(
this.FuncList,
function CB_Compare_Funcs(func){
return func == aFunc;
}
);
}
xCallbackChain.prototype.Call = function( aArg, aExceptionFunc ) {
if (this.FuncList.length == 0 || this.Active) return; // prevent recursion
this.Active = true;
// make a copy of FuncList because FuncList may be changed in callbacks!
var funcListCopy = this.FuncList.slice();
xArrForEach(
funcListCopy,
function CB_Call_CallbackChainFunc(func) {
try {
func(aArg);
} catch(e) {
if (xFunc(aExceptionFunc)) aExceptionFunc(e);
}
},
aArg
);
this.Active = false;
}
// xEventManager -------------------------------------------------------------------------
var xOnLoadFinished = false; // synchroniced with xEventManager.PageLoadFired
var xEventManager = {
DomReadyHandlers: new xCallbackChain(),
MyDomReadyHandlers: [],
DomReadyFired: false,
PageLoadHandlers: new xCallbackChain(),
MyPageLoadHandler: null,
PageLoadFired: false, // synchroniced with xOnLoadFinished
PageUnloadHandlers: new xCallbackChain(),
OldWindowOnUnloadHandler: null,
MyPageUnloadHandler: null,
LayoutChangeHandlers: new xCallbackChain(),
MyLayoutChangeHandler: null,
WindowResizeHandlers: new xCallbackChain(),
WindowResizeTimer: null,
MyWindowResizeHandler: null,
DisplayChangeHandlers: new xCallbackChain(),
AddDomReadyHandler: function( aFunc ) {
var myDomReadyHandler = function xOnEventManager_DomReady(){
xEventManager.DomReadyFired = true;
xEventManager.RemoveDomReadyHandler( aFunc );
try {
aFunc();
} catch(e) {};
}
this.MyDomReadyHandlers.push( { Func: aFunc, Handler: myDomReadyHandler } );
if (this.DomReadyFired) {
// exec aFunc as soon as possible
setTimeout( myDomReadyHandler, 1 );
} else if (document.addEventListener) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", myDomReadyHandler, false );
} else {
// use xOnLoad but with a separate callback chain that is called before other onload callbacks
this.DomReadyHandlers.Add( myDomReadyHandler );
}
},
RemoveDomReadyHandler: function( aFunc ) {
var handlerInfo = xArrFind(
this.MyDomReadyHandlers,
function CB_Compare_Funcs(item){
return item.Func == aFunc;
}
);
if (!handlerInfo) return;
var myDomReadyHandler = handlerInfo.Handler;
if (document.addEventListener) {
document.removeEventListener( "DOMContentLoaded", myDomReadyHandler, false );
} else {
this.DomReadyHandlers.Remove( myDomReadyHandler );
}
xArrRemoveAll(
this.MyDomReadyHandlers,
function CB_Compare_Funcs(item){
return item.Func == aFunc;
}
);
},
AddPageLoadHandler: function( aFunc ) {
// installed functions are called in order of registration
// if this function is called after onload is fired then func is called via timer as soon as possible
if (!this.MyPageLoadHandler) {
this.MyPageLoadHandler = function xOnEventManager_PageLoad() {
xEventManager.PageLoadFired = true;
// first call functions registered in xOnDomReady() on old brwosers
xEventManager.DomReadyHandlers.Call();
// call registered handlers
xEventManager.PageLoadHandlers.Call();
xOnLoadFinished = true;
}
window.addEventListener( 'load', this.MyPageLoadHandler );
}
if (this.PageLoadFired) {
// call aFunc as soon as possible
setTimeout(
function CB_OnTimeout_PageLoadFired(){
try {
aFunc();
}catch(e){}
}, 1
);
} else {
this.PageLoadHandlers.Add( aFunc );
}
},
RemovePageLoadHander: function( aFunc ) {
this.PageLoadHandlers.Remove( aFunc );
},
TriggerPageLoad: function() {
this.PageLoadHandlers.Call(window);
},
AddPageUnloadHandler: function( aFunc ) {
// installed functions are called in order of registration
if (!this.MyPageUnloadHandler) {
this.OldWindowOnUnloadHandler = window.onunload;
this.MyPageUnloadHandler = function xOnEventManager_CallingOldUnloadHandler() {
// call previous installed window.onload handler
if (xEventManager.OldWindowOnUnloadHandler) {
try {
xEventManager.OldWindowOnUnloadHandler();
} catch(e) {}
}
// call registered handlers
xEventManager.PageUnloadHandlers.Call();
}
window.onunload = this.MyPageUnloadHandler;
}
this.PageUnloadHandlers.Add( aFunc );
},
RemovePageUnloadHander: function( aFunc ) {
this.PageUnloadHandlers.Remove( aFunc );
},
TriggerPageUnload: function() {
this.PageUnloadHandlers.Call(window);
},
AddLayoutChangeHandler: function( aFunc ) {
// registers aFunc to be called when a page layout changes via call to TriggerLayoutChang()
// or when a window resize occours
// aFunc = function(arg)
this.LayoutChangeHandlers.Add( aFunc );
if (!this.MyLayoutChangeHandler) {
this.MyLayoutChangeHandler = function xOnEventManager_OnLayoutChange(arg){
xEventManager.TriggerLayoutChange(arg);
}
this.AddWindowResizeHandler( this.MyLayoutChangeHandler );
}
},
RemoveLayoutChangeHandler: function( aFunc ) {
this.LayoutChangeHandlers.Remove( aFunc );
},
TriggerLayoutChange: function( aArg ) {
xOptions.Transform.OffsetElement = null;
this.LayoutChangeHandlers.Call( aArg );
},
AddWindowResizeHandler: function( aFunc ) {
// aFunc is only called after the size of the window is unchanged for some time (250ms) after a resize
if (!this.MyWindowResizeHandler) {
this.MyWindowResizeHandler = function xOnEventManager_OnWindowResize() {
if (xEventManager.WindowResizeTimer) {
clearTimeout( xEventManager.WindowResizeTimer );
}
xEventManager.WindowResizeTimer = setTimeout(
function CB_OnTimeout_WindowResize() {
clearTimeout( xEventManager.WindowResizeTimer );
xEventManager.WindowResizeTimer = null;
xEventManager.WindowResizeHandlers.Call();
},
250
);
}
xAddEvent( window, 'resize', this.MyWindowResizeHandler );
}
this.WindowResizeHandlers.Add( aFunc );
},
RemoveWindowResizeHandler: function( aFunc ) {
this.WindowResizeHandlers.Remove( aFunc );
},
TriggerWindowResize: function( aArg ) {
this.WindowResizeHandlers.Call( aArg );
},
AddDisplayChangeHandler: function( aFunc ) {
// registers aFunc to be called when a elements display property changes via TriggerDisplayChange()
// aFunc = function( aEle ) where aEle is element that changes display property
this.DisplayChangeHandlers.Add( aFunc );
},
RemoveDisplayChangeHandler: function( aFunc ) {
this.DisplayChangeHandlers.Remove( aFunc );
},
TriggerDisplayChange: function( aArg ) {
this.DisplayChangeHandlers.Call( aArg );
},
};
// LayoutChange Event Handling
function xAddEventLayoutChange( aFunc ) { xEventManager.AddLayoutChangeHandler( aFunc ); }
function xRemoveEventLayoutChange( aFunc ) { xEventManager.RemoveLayoutChangeHandler( aFunc ); }
function xTriggerEventLayoutChange( aArg ) { xEventManager.TriggerLayoutChange( aArg ); }
// DisplayChange Event Handling
function xAddEventDisplayChange( aFunc ) { xEventManager.AddDisplayChangeHandler( aFunc ); }
function xRemoveEventDisplayChange( aFunc ) { xEventManager.RemoveDisplayChangeHandler( aFunc ); }
function xTriggerEventDisplayChange( aEle ) { xEventManager.TriggerDisplayChange( aEle ); }
// Window Resize Event Handling
function xAddEventWindowResize( aFunc ) { xEventManager.AddWindowResizeHandler( aFunc ); }
function xRemoveEventWindowResize( aFunc ) { xEventManager.RemoveWindowResizeHandler( aFunc ); }
// DOM Event Handling
function xOnDomReady( aFunc ) { xEventManager.AddDomReadyHandler( aFunc ); }
function xOnLoad( aFunc ) { xEventManager.AddPageLoadHandler( aFunc ); }
function xOnUnload( aFunc ) { xEventManager.AddPageUnloadHandler( aFunc ); }
// Transform Functions (faster than xMoveTo and xResizeTo ----------
var xOptions = {
Transform: {
PropertyNames: [ 'webkitTransform', 'MozTransform', 'msTransform', 'OTransform', 'transform' ],
PropertyName: '?',
OffsetElement: null, // caching
OffsetX: 0,
OffsetY: 0
}
};
function xGetTransformPropertyName(e) {
// returns property name of transform style (e.g. 'msTransform')
// or '' if transforms are not supported
var tf = xOptions.Transform;
if (tf.PropertyName != '?') return tf.PropertyName;
var names = tf.PropertyNames;
var len = names.length;
for (var i = 0; i < len; i++) {
var name = names[i];
if (xDef(e.style[name])) {
tf.PropertyName = name;
return name;
}
}
tf.PropertyName = '';
return '';
}
function xSupportsTransform(e) {
// e: htmlElement or undefined
if (!e) e = document.createElement('div');
return xGetTransformPropertyName(e) != '';
}
function xTransform( e, trans ) {
// trans: valid transform string (e.g. 'translate(10px,20px) scale(2,3)')
// returns true if transforms are supported and transform is applied to e's style
// Tip: use xFStr() to build trans strings
e = xElement(e);
var name = xGetTransformPropertyName(e);
if (name == '') return false;
e.style[name] = trans;
return true;
}
function xTransformOrigin( e, origin ) {
// origin: valid transform-origin string (e.g. 'left top', '50% 50%')
// returns true if transforms are supported and transform is applied to e's style
e = xElement(e);
var name = xGetTransformPropertyName(e);
if (name == '') return false;
e.style[name+'Origin'] = origin;
return true;
}
function xGetTransformDocOffset( e ) {
// returns offset of doc origin refered to window origin
// Note: translate(0,0) moves to doc origin, xMoveTo(0,0) moves to window origin
var tf = xOptions.Transform;
if (tf.OffsetElement != e) {
tf.OffsetElement = e;
tf.OffsetX = 0;
tf.OffsetY = 0;
if (!xTransform( e, 'translate(0px,0px)' )) return tf;
tf.OffsetX = xPageX(e);
tf.OffsetY = xPageY(e);
}
return tf; // = { OffsetX, OffsetY }
}
function xTransformNone( e ) {
// removes all transformations if supported, else moves e via xMoveTo(0,0)
e = xElement(e);
if (!xTransform( e, 'none' )) xMoveTo( e, 0, 0 );
}
function xTransformTranslate( e, x, y, useWinOrigin ) {
// uses style transforms to move e if supported, else uses xMoveTo
e = xElement(e);
useWinOrigin = xDefBool( useWinOrigin, false );
var tx = x, ty = y;
if (useWinOrigin) {
var tf = xGetTransformDocOffset( e );
tx -= tf.OffsetX;
ty -= tf.OffsetY;
}
if (!xTransform( e, xFStr('translate(#px,#px)',tx,ty) )) xMoveTo( e, x, y );
}
function xTransformTranslateScale( e, x, y, w, h, wRef, hRef, useWinOrigin ) {
// uses style transforms to move upper left point of e to (x,y) and size e to (w,h)
// falls back to xMoveTo and xResizeTo if transforms are not supported
// Note: to compute the transform matrix, the original size of e must be supported in (wRef,hRef)
e = xElement(e);
useWinOrigin = xDefBool( useWinOrigin, false );
var tx = x, ty = y;
if (useWinOrigin) {
var t = xGetTransformDocOffset( e );
tx -= t.OffsetX;
ty -= t.OffsetY;
}
var xScale = w / wRef;
var yScale = h / hRef;
var xMove = (wRef/2)*(xScale-1) + tx;
var yMove = (hRef/2)*(yScale-1) + ty;
var trans = xFStr( 'matrix(#,0,0,#,#,#)', xScale, yScale, xMove, yMove );
if (!xTransform( e, trans )) {
xMoveTo( e, x, y );
xResizeTo( e, w, h );
}
}
// ---------
var xClipboardBuffer = null;
function xToClipboard(text) {
// tryes to copy text to clipboard, returns true on success
// must take place as a direct result of a user action, e.g. onClick event handler.
if (xDef(window.clipboardData)) {
window.clipboardData.setData('Text',text);
return true;
}
else if (document.queryCommandSupported && document.queryCommandSupported('copy')) {
var textArea = document.createElement("textarea");
textArea.style.position = 'fixed';
textArea.style.width = '2em';
textArea.style.height = '2em';
textArea.style.padding = 0;
textArea.style.border = 'none';
textArea.style.outline = 'none';
textArea.style.boxShadow = 'none';
textArea.style.background = 'transparent';
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
var done = false;
try {
done = document.execCommand('copy');
} catch (clipboardError) { }
document.body.removeChild(textArea);
return done;
}
return false;
}
function xTimeMS() {
return (new Date()).getTime();
}
function xImage(aImgFilename) {
var img = new Image();
img.src = aImgFilename;
return img;
}
function xChangeImage(aImgID,aImg) {
var img = xElement(aImgID);
if (img) {
img.src = aImg.src;
}
}
// var b1Img = new xMultiImage( 'b1', 'stuff/b1_up.gif', 'stuff/b1_down.gif' );
//
// <a href="next.html" onmouseover="b1Img.Show(1)" onmouseout="b1Img.Show(0)">
// <img id="b1" src="stuff/b1_up.gif" width="123" height="45" alt="next"></a>
function xMultiImage(aImgID) {
this.ImgID = aImgID;
this.Images = [];
var a = xMultiImage.arguments;
for (var i = 1; i < a.length; i++) {
this.Images[i-1] = xImage(a[i]);
}
}
xMultiImage.prototype.Show = function( aImageNumber ) {
xChangeImage(this.ImgID,this.Images[aImageNumber]);
};
var xDbgMess = '';
var xDbgSep = '\n';
function xDbg( aMess ) {
if (aMess) {
xDbgMess += aMess + xDbgSep;
}
else {
alert(xDbgMess);
}
}
// uses a <textarea id="xdbgout" style="width:100%" rows=12></textarea>
function xDbgOut(x) {
var o = xGet('xdbgout');
if (o) {
o.value = x;
}
}
function xDbgApp(x) {
var o = xGet('xdbgout');
if (o) {
o.value += x+'\n';
}
}
// cookie functions
function xSetCookie(name,value,days) {
days = days || 1;
var date = new Date();
date.setTime( date.getTime() + (days*24*60*60*1000) );
var expires = '; expires=' + date.toGMTString();
document.cookie = name + '=' + escape(value) + expires + '; path=/';
}
function xGetCookie(name) {
var cName;
var ca = document.cookie.split( ';' );
for( var i = 0; i < ca.length; i++ ) {
var c = ca[i];
var eqPos = c.indexOf( '=' );
if (eqPos >= 0) {
cName = c.substr( 0, eqPos ).replace( /^\s+|\s+$/g, '' );
} else {
cName = c.replace( /^\s+|\s+$/g, '' );
}
if (name == cName) {
if (eqPos < 0) return '';
return unescape( c.substr( eqPos + 1 ) );
}
}
return null;
}
function xDeleteCookie(name) {
xSetCookie( name, '', -1 );
}
// some utility functions
function htmlString( aStr ) {
var s = aStr;
s = s.replace( /</g, '<' );
s = s.replace( />/g, '>' );
return s;
}
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
// requestAnimationFrame polyfill by Erik Möller
// fixes from Paul Irish and Tino Zijdel
(function() {
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
|| window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}());
// This polyfill covers the main use case which is creating a new object
// for which the prototype has been chosen but doesn't take the second argument into account.
// https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Object/create
// usage:
//
// function SuperClass( params ) {
// :
// }
//
// SuperClass.prototype.Override = function( arg ) {}
//
// function SubClass( params ) {
// this.parentClass.constructor.call( this, params );
// :
// }
//
// SubClass.inheritsFrom( SuperClass );
//
// SubClass.prototype.Override = function( arg ) {
// this.parentClass.Override.call( this, arg );
// :
// }
if (typeof Object.create != 'function') {
Object.create = (function() {
var Temp = function() {};
return function (prototype) {
if (arguments.length > 1) {
throw Error('Second argument not supported');
}
if (typeof prototype != 'object') {
throw TypeError('Argument must be an object');
}
Temp.prototype = prototype;
var result = new Temp();
Temp.prototype = null;
return result;
};
})();
}
Function.prototype.inheritsFrom = function( parentClass ){
this.prototype = Object.create( parentClass.prototype );
this.prototype.constructor = this;
this.prototype.parentClass = parentClass.prototype;
return this;
}
// polyfill für Math.log10()
Math.log10 = Math.log10 || function(x) {
return Math.log(x) / Math.LN10;
};