e callback
* called with the given context or node (W3C).
*
* @return function
*/
createResponder: function ( uid, type, originalType, handler, context ) {
return function ( event ) {
event = _Event.extend( event, uid );
if( !_Event.supportsMouseenterMouseleave && originalType === 'mouseleave' ) {
if( event.currentTarget.contains(event.relatedTarget) ) {
return;
}
}
handler.call( context || _Event.cache[uid].node, event );
};
},
/**
* Purge events from given element
*
* @param number
* @return void
*/
purge: function ( uid ) {
var cache = _Event.cache[uid],
element,
key;
if( !cache ) {
return;
}
element = PB(cache.node);
for( key in cache ) {
if( cache.hasOwnProperty(key) && key !== 'node' ) {
element.off( key );
}
}
delete _Event.cache[uid];
cache.node = null;
return;
},
/**
* Extend event object with functionality, on event fire
* this function will be called.
*
* For legacy browsers this also extend the event object with
* with de Event specications.
*
* Returns an extended Event object
*
* @param Event
* @param number
* @return Event
*/
extend: function ( event, uid ) {
if( event.type.indexOf('touch') === 0 && event.touches[0] ) {
event.touchX = event.touches[0].pageX;
event.touchY = event.touches[0].pageY;
}
if( event.type === 'DOMMouseScroll' || event.type === 'mousewheel' || event.type === 'wheel' ) {
event.wheel = event.wheelDelta ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
}
if( _Event.manualExtend === false ) {
return event;
}
PB.overwrite( event, _Event.methods );
event.target = event.srcElement || _Event.cache[uid].node;
event.currentTarget = _Event.cache[uid].node;
switch ( event.type ) {
case 'mouseover':
case 'mouseenter':
event.relatedTarget = event.fromElement;
break;
case 'mouseout':
case 'mouseleave':
event.relatedTarget = event.toElement;
break;
}
if( event.pageX === undefined ) {
event.pageX = event.clientX + (docElement.scrollLeft || body.scrollLeft) - (docElement.clientLeft || 0);
event.pageY = event.clientY + (docElement.scrollTop || body.scrollTop) - (docElement.clientTop || 0);
}
event.which = (event.keyCode === undefined) ? event.charCode : event.keyCode;
event.which = (event.which === 0 ? 1 : (event.which === 4 ? 2: (event.which === 2 ? 3 : event.which)));
return event;
}
};
/**
*
*/
_Event.methods = {
/**
* Short for preventDefault and stopPropagation
*
* Tnx prototypejs! :)
*/
stop: function () {
this.preventDefault();
this.stopPropagation();
}
};
/**
* Extend event prototype with DOM level 2 events
*/
if (window.addEventListener) {
PB.overwrite(Event.prototype, _Event.methods);
}
/**
* Add methods for non DOM level 2 events
*/
if( window.attachEvent && !window.addEventListener ) {
_Event.manualExtend = true;
PB.overwrite(_Event.methods, {
/**
* Normalize stopPropagation
*/
stopPropagation: function () {
this.cancelBubble = true;
},
/**
* Normalize stopPropagation
*/
preventDefault: function () {
this.returnValue = false;
}
});
}
/**
*
*/
PB.overwrite(PB.dom, {
/**
* DOM event binding
*
* Example
* PB('element').on('click', function ( e ){ alert(e.type) })
*
* @param string
* @param Function
* @param Object
* @return Dom
*/
on: function ( type, handler, context ) {
var types = type.split(' ');
if ( typeof handler !== 'function' ) {
throw new TypeError('element.on(\''+type+'\'), handler is not a function');
}
if( types.length > 1 ) {
types.forEach(function ( type ) {
this.on( type, handler, context );
}, this);
return this;
}
var node = this.node,
uid = node.__PBJS_ID__,
events = _Event.cache[uid],
originalType = type,
eventsType,
i;
if( _Event.supportsMouseenterMouseleave === false ) {
type = (type === 'mouseenter' ? 'mouseover' : (type === 'mouseleave' ? 'mouseout' : type));
}
if( !events ) {
_Event.cache[uid] = events = {node: node};
}
if( !events[type] ) {
events[type] = [];
}
eventsType = events[type];
i = eventsType.length;
while( i-- ) {
if( eventsType[i].handler === handler ) {
return this;
}
}
var entry = {
handler: handler,
responder: _Event.createResponder( uid, type, originalType, handler, context )
};
eventsType.push( entry );
if( node.addEventListener ) {
node.addEventListener( type, entry.responder, false );
} else {
node.attachEvent( 'on'+type, entry.responder );
}
node = null;
return this;
},
/**
* Bind the event, and removes it after use
*
* @param string
* @param Function
* @param Object
* @return Dom
*/
once: function ( types, handler, context ) {
var me = this;
types.split(' ').forEach(function ( type ) {
var _handler = function () {
me.off( type, _handler );
handler.apply( context || me.node, PB.toArray(arguments) );
};
me.on(type, _handler);
});
return this;
},
/**
* Removes the given: (type + handler) or (type) or (all)
*
* @param string
* @param Function
* @return Dom
*/
off: function ( type, handler ) {
var node = this.node,
uid = node.__PBJS_ID__,
events = _Event.cache[uid],
eventsType,
entry,
i;
if( !events ) {
return this;
}
if( !type ) {
_Event.purge( uid );
return this;
}
eventsType = events[type];
if( !eventsType ) {
return this;
}
i = eventsType.length;
if( !handler ) {
while( i-- ) {
this.off( type, eventsType[i].handler );
}
return this;
}
while( i-- ) {
if( eventsType[i].handler === handler ) {
entry = eventsType[i];
eventsType.splice( i, 1 );
if( !eventsType.length ) {
delete _Event.cache[uid][type];
}
break;
}
}
if( !entry ) {
return this;
}
if( node.removeEventListener ) {
node.removeEventListener( type, entry.responder, false );
} else {
node.detachEvent( 'on'+type, entry.responder );
}
node = null;
return this;
},
/**
* Dispatch the event on the element
*
* @param string
* @return Dom
*/
emit: function ( type ) {
var evt;
if( _Event.HTMLEvents.test(type) || this.nodeName === 'INPUT' ) {
this.node[type]();
}
else if( doc.createEvent ) {
if ( _Event.MouseEvents.test(type) ) {
evt = doc.createEvent('MouseEvents');
evt.initMouseEvent(
type, true, true, window, // type, canBubble, cancelable, view,
0, 0, 0, 0, 0, // detail, screenX, screenY, clientX, clientY,
false, false, false, false, // ctrlKey, altKey, shiftKey, metaKey,
0, null); // button, relatedTarget
this.node.dispatchEvent(evt);
} else {
evt = doc.createEvent('Events');
evt.initEvent( type, true, true );
this.node.dispatchEvent(evt);
}
}
else {
evt = doc.createEventObject();
this.node.fireEvent('on'+type, evt);
}
return this;
}
});
/**
*
*/
PB.ready = (function () {
var ready = doc.readyState === 'complete',
queue = [];
function execQueue () {
var callback;
ready = true;
body = doc.body;
while( callback = queue.shift() ) {
callback();
}
}
function domready () {
try {
doc.body.scrollLeft;
execQueue();
} catch ( e ) {
setTimeout(domready, 16.7);
return;
}
}
if( doc.addEventListener ) {
PB(doc).once('DOMContentLoaded', execQueue);
} else {
domready();
}
return function ( callback ) {
if( !ready ) {
return queue.push( callback );
}
callback();
};
})();
PB.overwrite(PB.dom, {
set: function ( key, value ) {
this.storage[key] = value;
return this;
},
get: function ( key ) {
return this.storage[key];
},
unset: function ( key ) {
return delete this.storage[key];
},
isset: function ( key ) {
return this.storage[key] !== undefined;
}
});
PB.overwrite(PB.dom, {
/**
* Set/get/remove attribute
* null values will remove attribute
*
* @return mixed
*/
attr: function ( key, value ) {
if( PB.type(key) === 'object' ) {
PB.each(key, this.attr, this);
return this;
}
if( value === undefined ) {
return this.node.getAttribute(key);
} else if ( value === null ) {
this.node.removeAttribute(key);
} else {
this.node.setAttribute(key, value);
}
return this;
},
/**
* Set / get value from form element
*/
val: function ( value ) {
if( value === undefined ) {
return this.node.value;
}
this.node.value = value;
return this;
},
/**
* Set or retrieve 'data-' attribute
*/
data: function ( key, value ) {
return this.attr( 'data-'+key, value );
},
/**
* Select the content in the provided range
*/
select: function( start, length ) {
var node = this.node,
value = this.val(),
range;
if ( value ) {
if ( !length ){ // default: select all
length = ( start ) ? start : value.length;
start = 0;
}
if ( node.createTextRange ) {
document.selection.empty();
range = node.createTextRange();
range.collapse( true );
range.moveStart( 'character', start );
range.moveEnd( 'character', start + length );
range.select();
} else {
window.getSelection().removeAllRanges();
node.setSelectionRange( start, start+length );
}
}
}
});
var unit = /^-?[\d.]+px$/i,
opacity = /alpha\(opacity=(.*)\)/i,
noPixel = /(thin|medium|thick|em|ex|pt|%)$/,
computedStyle = doc.defaultView && doc.defaultView.getComputedStyle,
vendorDiv = doc.createElement('div'),
supportsOpacity = vendorDiv.style.opacity !== undefined,
supportsCssFloat = vendorDiv.style.cssFloat !== undefined,
skipUnits = 'zIndex zoom fontWeight opacity',
cssPrefixProperties = 'animationName transform transition transitionProperty transitionDuration transitionTimingFunction boxSizing backgroundSize boxReflect'.split(' '),
cssPropertyMap = {},
vendorPrefixes = 'O ms Moz Webkit'.split(' '),
i = vendorPrefixes.length;
/**
* Add prefixes to cssPropertyMap map if needed/supported
*/
cssPrefixProperties.forEach(function ( property ) {
if( property in vendorDiv.style ) {
return cssPropertyMap[property] = property;
}
var j = i,
prop = property.charAt(0).toUpperCase()+property.substr(1);
while( j-- ) {
if( vendorPrefixes[j]+prop in vendorDiv.style ) {
return cssPropertyMap[property] = vendorPrefixes[j]+prop;
}
}
});
cssPrefixProperties = vendorDiv = null;
PB.support.CSSTransition = !!cssPropertyMap.transition;
PB.support.CSSAnimation = !!cssPropertyMap.animationName;
/**
* Add px numeric values
*/
function addUnits ( property, value ) {
if( skipUnits.indexOf(property) >= 0 ) {
return value;
}
return typeof value === 'string' ? value : value+'px';
}
/**
* Remove units from px values
*/
function removeUnits ( value ) {
return unit.test( value ) ? parseInt( value, 10 ) : value;
}
/**
* Get the right property name for this browser
*/
function getCssProperty ( property ) {
if( property === 'float' ) {
property = supportsCssFloat ? 'cssFloat' : 'styleFloat';
}
return cssPropertyMap[property] || property;
}
PB.overwrite(PB.dom, {
/**
* Set CSS styles
*
* @param object
* @return PB.Dom
*/
setStyle: function ( values ) {
var property;
if( arguments.length === 2 ) {
var property = values;
values = {};
values[property] = arguments[1];
}
for( property in values ) {
if( values.hasOwnProperty(property) ) {
if( property === 'opacity' && !supportsOpacity ) {
if( !this.node.currentStyle || !this.node.currentStyle.hasLayout ) {
this.node.style.zoom = 1;
}
this.node.style.filter = 'alpha(opacity='+(values[property]*100)+')';
} else {
this.node.style[getCssProperty( property )] = addUnits( property, values[property] );
}
}
}
return this;
},
/**
* Get CSS style
*
* @param string
* @return number/string
*/
getStyle: function ( property, calculated ) {
property = getCssProperty( property );
var node = this.node,
value = node.style[property],
o;
if( calculated || !value || value === 'auto' ) {
if( computedStyle ) {
value = doc.defaultView.getComputedStyle( node, null )[property];
} else {
value = node.currentStyle[property];
if( noPixel.test(value) ) {
var style = value.lastIndexOf('%') > -1 ? 'height: '+value : 'border: '+value+' solid #000; border-bottom-width: 0',
div = PB('
')
.appendTo(body);
value = div.node.offsetHeight;
div.remove();
return value;
}
}
}
if( property === 'opacity' ) {
if( node.style.filter && (o = node.style.filter.match(opacity)) && o[1] ) {
return parseFloat(o[1]) / 100;
}
return value ? parseFloat(value) : 1.0;
}
return value === 'auto' ? 0 : removeUnits(value);
}
});
PB.browser.supportsCSSAnimation = !!cssPropertyMap['animationName'];
function morphArgs ( args ) {
var options = {
duration: .4,
effect: 'ease'
};
for( var i = 1 ; i < args.length; i++ ) {
switch( typeof args[i] ) {
case 'function':
options.callback = args[i];
break;
case 'number':
options.duration = args[i];
break;
case 'string':
options.effect = PB.String.decamelize(args[i]);
break;
}
}
return options;
}
/**
* @todo add 'effect' arguments
*
* Firefox bug, https://bugzilla.mozilla.org/show_bug.cgi?id=604074
* https://developer.mozilla.org/en-US/docs/CSS/transition-timing-function
*/
PB.dom.morph = PB.support.CSSTransition ?
function ( to ) {
var me = this,
options = morphArgs( arguments ),
morph = this.get('__morph') || {},
from = {};
if( morph.running ) {
this.stop(false, true);
}
morph.to = to;
morph.callback = options.callback;
morph.running = true;
from.transition = 'all '+options.duration+'s '+options.effect+' 0s';
PB.each(to, function ( property ) {
from[property] = me.getStyle( property, true );
});
this.setStyle(from);
PB.each(to, function ( property ) {
me.getStyle( property, true );
});
/* Example code to force `GPU` */
/*
if( (to.left !== undefined && to.top !== undefined) && !to.transform ) {
to.transform = 'translate('+addUnits('left', to.left)+', '+addUnits('top', to.top)+')';
delete to.left;
delete to.top;
}
*/
me.setStyle(to);
morph.endTimer = setTimeout(function() {
if( !me.node ) {
return;
}
morph.running = false;
me.setStyle({
transition: ''
});
if( options.callback ) {
options.callback( me );
}
}, (options.duration*1000));
this.set('__morph', morph);
return this;
} :
function ( to ) {
var options = morphArgs( arguments );
this.setStyle(to);
if( options.callback ) {
options.callback( this );
}
};
/**
* Stop morphing
*
* @param {boolean} (optional)
* @param {boolean} (optional)
* @return this
*/
PB.dom.stop = function ( skipToEnd, triggerCallback ) {
var me = this,
morph = this.get('__morph');
if( !morph || !morph.running ) {
return this;
}
triggerCallback = (triggerCallback === undefined) ? true : !!triggerCallback;
morph.running = false;
clearTimeout( morph.endTimer );
if( skipToEnd ) {
PB.each(morph.to, function ( property ) {
me.setStyle(property, '');
me.getStyle(property, true);
});
}
else {
PB.each(morph.to, function ( property ) {
morph.to[property] = me.getStyle(property, true);
});
}
morph.to.transition = '';
this.setStyle(morph.to);
if( triggerCallback && morph.callback ) {
morph.callback( this );
}
return this;
}
PB.overwrite(PB.dom, {
/**
* Check if element has class
*/
hasClass: function ( className ) {
return (new RegExp( "(^|\\s)"+className+"($|\\s)" )).test(this.node.className);
},
/**
* Add class to element
*/
addClass: function ( classNames ) {
classNames = classNames.split(' ')
for( var i = 0; i < classNames.length; i++ ) {
if( this.hasClass(classNames[i]) ) {
continue;
}
this.node.className += (this.node.className ? ' ' : '')+classNames[i];
}
return this;
},
/**
* Remove class from element
*/
removeClass: function ( classNames ) {
var node = this.node,
classes = node.className,
className;
classNames = classNames.split(' ')
for( var i = 0; i < classNames.length; i++ ) {
className = classNames[i];
classes = classes.replace( new RegExp( "(^|\\s)"+className+"($|\\s)" ), ' ' );
classes = classes.trim();
}
if( classes === '' ) {
node.className = null;
} else {
node.className = classes;
}
return this;
},
/**
*
*/
show: function () {
this.node.style.display = this.get('css-display') || 'block';
return this;
},
hide: function () {
var display = this.getStyle('display');
if( display === 'none' ) {
return this;
}
this.set('css-display', display);
this.node.style.display = 'none';
return this;
},
isVisible: function () {
return this.getStyle('display') !== 'none' && this.getStyle('opacity') > 0;
},
getXY: function ( fromBody ) {
var node = this.node,
x = 0,
y = 0;
while( node ) {
x += node.offsetLeft;
y += node.offsetTop;
node = node.offsetParent;
if( !node || (!fromBody && Dom.get(node).getStyle('position') !== 'static') ) {
break;
}
}
return {
left: x,
top: y
};
},
getScroll: function () {
var node = this.node,
scroll = {};
if( node.nodeType === 9 || node === window ) {
scroll.left = Math.max( docElement.scrollLeft, body.scrollLeft );
scroll.top = Math.max( docElement.scrollTop, body.scrollTop );
} else {
scroll.left = node.scrollLeft;
scroll.top = node.scrollTop;
}
return scroll;
},
width: function ( value ) {
if( value !== undefined ) {
return this.setStyle('width', value);
}
var node = this.node;
if( node === window ) {
return window.innerWidth || docElement.offsetWidth;
} else if ( node.nodeType === 9 ) {
return Math.max(docElement.clientWidth, body.scrollWidth, docElement.offsetWidth);
}
if( value = this.getStyle('width', true) && typeof value === 'number' ) {
return value;
}
if( !this.isVisible() ) {
this.show();
value = node.offsetWidth;
this.hide()
} else {
value = node.offsetWidth;
}
return value;
},
innerWidth: function () {
return this.width() + (this.getStyle('paddingLeft', true) || 0) + (this.getStyle('paddingRight', true) || 0);
},
outerWidth: function () {
return this.width() + (this.getStyle('borderLeftWidth', true) || 0) + (this.getStyle('borderRightWidth', true) || 0);
},
scrollWidth: function () {
return this.node.scrollWidth;
},
height: function ( value ) {
if( value !== undefined ) {
return this.setStyle('height', value);
}
var node = this.node;
if( node === window ) {
return window.innerHeight || docElement.offsetHeight;
} else if ( node.nodeType === 9 ) {
return Math.max(docElement.clientHeight, body.scrollHeight, docElement.offsetHeight);
}
if( value = this.getStyle('height', true) && typeof value === 'number' ) {
return value;
}
if( !this.isVisible() ) {
this.show();
value = node.offsetHeight;
this.hide()
} else {
value = node.offsetHeight;
}
return value;
},
innerHeight: function () {
return this.height() + (this.getStyle('paddingTop', true) || 0) + (this.getStyle('paddingBottom', true) || 0);
},
outerHeight: function () {
return this.height() + (this.getStyle('borderTopWidth', true) || 0) + (this.getStyle('borderBottomWidth', true) || 0);
},
scrollHeight: function () {
return this.node.scrollHeight;
}
});
PB.each({ left: 'Left', top: 'Top' }, function ( lower, upper ) {
PB.dom['scroll'+upper] = function ( value ) {
if( value !== undefined ) {
var node = this.node;
if( node === win || node === doc || node === docElement ) {
window.scrollTo( lower === 'left' ? value : this.scrollLeft(), lower === 'top' ? value : this.scrollTop() );
} else {
node['scroll'+upper] = value;
}
return this;
}
return this.getScroll()[lower];
}
PB.dom[lower] = function ( fromBody ) {
if( fromBody !== undefined && fromBody !== true ) {
this.setStyle(lower, fromBody);
return this;
}
return this.getXY(fromBody)[lower];
}
});
PB.overwrite(PB.dom, {
/**
* Retrieve parent node
*
* @return
*/
parent: function () {
return Dom.get( this.node.parentNode );
},
/**
* Retrieve the first child that is an ELEMENT_NODE
*
* @return
*/
first: function () {
var first = this.node.firstChild;
while( first && first.nodeType !== 1 ) {
first = first.nextSibling;
}
return first === null
? null
: Dom.get(first);
},
/**
* Retrieve the last child that is an ELEMENT_NODE
*
* @return
*/
last: function () {
var last = this.node.lastChild;
while( last && last.nodeType !== 1 ) {
last = last.previousSibling;
}
return last === null
? null
: Dom.get(last);
},
/**
* Retrieve the next sibling that is an ELEMENT_NODE
*
* @return
*/
next: function () {
var sibling = this.node;
while( sibling = sibling.nextSibling ) {
if( sibling.nodeType === 1 ) {
return Dom.get( sibling );
}
}
return null;
},
/**
* Retrieve the previous sibling that is an ELEMENT_NODE
*
* @return
*/
prev: function () {
var sibling = this.node;
while( sibling = sibling.previousSibling ) {
if( sibling.nodeType === 1 ) {
return Dom.get( sibling );
}
}
return null;
},
/**
* Retrieve childs of the current node
*
* @return
*/
childs: function () {
var childs = new PB.Collection, // new Collection
node = this.first();
if( node === null ) {
return childs;
}
do {
childs.push( node );
} while ( node = node.next() );
return childs;
},
/**
* Tries to find a matching parent that matches the given expression
*
* @param string CSS expression
* @param number max parents to crawl up
* @return
*/
closest: function ( expression, maxDepth ) {
var node = this;
maxDepth = maxDepth || 50;
do {
if( qwery.is( node.node, expression ) ) {
return node;
}
if( !--maxDepth ) {
break;
}
} while ( node = node.parent() );
return null;
},
/**
* Current node is a descendant of the given element?
*
* @param string/node/PBDom
* @return boolean
*/
descendantOf: function ( element ) {
element = Dom.get(element);
return element
? element.contains( this )
: false;
},
/**
* Current node contains the given element?
*
* @param string/node/PBDom
* @return boolean
*/
contains: function ( element ) {
var node = this.node;
element = Dom.get(element).node;
return node.contains
? node !== element && node.contains( element )
: !!(node.compareDocumentPosition( element ) & 16);
},
/**
* Find elements trough CSS expression, searching from within
* the current element.
*
* @param string
* @return
*/
find: function ( expression ) {
return new PB.Collection( qwery( expression, this.node ).map(Dom.get) );
}
});
var tableInnerHTMLbuggie = false;
try {
doc.createElement('table').innerHTML = '
';
} catch (e) {
tableInnerHTMLbuggie = true;
}
PB.overwrite(PB.dom, {
/**
* Append element to self
*/
append: function ( element ) {
if( (element = Dom.get(element)) === null ) {
return null;
}
this.node.appendChild( element.node );
return this;
},
/**
* Append self to target element
*/
appendTo: function ( target ) {
if( (target = Dom.get(target)) === null ) {
return null;
}
target.append( this );
return this;
},
/**
* Insert self before target element
*/
insertBefore:Presentatoren > > over Tuffie Vos