/**
* core.js
*
* @version 0.1.8 1
* @copyright Copyright (c) 2012 stadt.werk GmbH (http://stadtwerk.org)
* @license http://creativecommons.org/licenses/by-nc/3.0/ CC BY-NC 3.0
* @author Finn Rudolph <rudolph@stadtwerk.org>
*/
/**
* Intro
*
* The head of the core.js framework. The Core constructor
* is wrapped inside an anonymous function which will be
* called immediately passing the global window object.
*/
(function( window )
{
'use strict';
/**
* Sandbox the main document object
*/
var document = window.document;
/**
* Core constructor.
*
* @global
* @constructor
*/
var Core = function()
{
/**
* Closure for this.
*/
var my = this;
/**
* DomReady
*
* <p>Normalize the DOM ready event.</p>
*
* @constructor
* @augments Core
*/
this['DomReady'] =
{
/**
* Flag that is raised on the DOM ready event.
*
* @private
* @type {bool}
*/
isReady: false,
/**
* Queued functions that will be called on DOM ready.
*
* @private
* @type {object}
*/
queue: {},
/**
* Counter for the number of functions in the queue.
*
* @private
* @type {number}
*/
count: 0,
/**
* This function will be called directly after Core has been exposed to the window object.
*
* @private
*/
initialize: function()
{
/* If the DOMContentLoaded event is supported */
if(document.addEventListener)
{
/* Bind run to the DOMContentLoaded event */
document.addEventListener('DOMContentLoaded', window['Core']['DomReady'].run, false);
/* Fallback to the load event */
window.addEventListener('load', window['Core']['DomReady'].run, false);
}
/* If the Internet Explorer event model is used */
else if(document.attachEvent)
{
/* Bind run to the onreadystatchange event */
document.attachEvent('onreadystatechange', window['Core']['DomReady'].run);
/* Fallback to the window.onload event */
window.attachEvent('onload', window['Core']['DomReady'].run);
/* Check if the window object is not embedded in an iFrame or object */
var isTopLevel = false;
try
{
isTopLevel = !window.frameElement;
}
catch(error) {}
/* Only run the scroll check method if the window is on the top level */
if(isTopLevel)
{
window['Core']['DomReady'].scrollCheck();
}
}
},
/**
* An DOMContentLoaded alternative for the Internet Explorer.
*
* @private
* @see <a href="http://javascript.nwbox.com/IEContentLoaded/">IEContentLoaded</a>
*/
scrollCheck: function()
{
/* Exit early */
if(my['DomReady'].isReady)
{
return;
}
/* Try to call the doScroll function until it fails no more */
try
{
/* This fails before the ondocumentready event */
document.documentElement.doScroll('left');
}
catch(error)
{
/* Recheck every 50 ms */
setTimeout(window['Core']['DomReady'].scrollCheck, 50);
return;
}
/* The DOM is ready */
window['Core']['DomReady'].run();
},
/**
* Adds a function handler to the DomReady queue.
*
* @example
* // Create a function that needs to be executed when the DOM is ready.
* var displayMessage = function()
* {
* document.getElementsByTagName('html')[0].innerHTML = 'The DOM is ready!';
* }
*
* // Add the function to the DomReady queue.
* Core.DomReady.add(displayMessage);
*
* @public
* @param {function} handler Handler function that will be called when the DOM is ready.
*/
'add': function(handler)
{
/* Just call the handler if the DOM is ready */
if(my['DomReady'].isReady)
{
handler();
}
else
{
/* Check if the passed handler has already an id */
if(!handler['CoreDomReadyId'])
{
/* Add a unique id to the handler and increase the counter */
handler['CoreDomReadyId'] = my['DomReady'].count++;
/* Add the handler to the queue */
my['DomReady'].queue[handler['CoreDomReadyId']] = handler;
}
}
},
/**
* Run all handlers in the queue.
*
* @private
*/
run: function()
{
/* Make sure, that this method is called once */
if(my['DomReady'].isReady === false)
{
/* Set the isReady flag */
my['DomReady'].isReady = true;
/* Call every function in the queue */
var count = my['DomReady'].count,
index;
for(index = 0; index < count ; index++)
{
if(my['DomReady'].queue[index])
{
my['DomReady'].queue[index]();
}
}
}
}
};
/**
* Event
*
* @constructor
* @augments Core
*/
this['Event'] =
{
/* Add an event */
'add': function(obj, type, fn)
{
/* The good browsers */
if(obj.addEventListener)
{
obj.addEventListener( type, fn, false );
}
/* Internet Explorer */
else if(obj.attachEvent)
{
/* Use resize with the document.documentElement and not the window object */
if(obj === window && type === 'resize')
{
obj = document.documentElement;
}
obj['e'+type+fn] = fn;
obj[type+fn] = function() { obj['e'+type+fn]( window.event ); };
obj.attachEvent( 'on'+type, obj[type+fn] );
}
},
/* Remove an event */
'remove': function(obj, type, fn)
{
if (obj.removeEventListener)
{
obj.removeEventListener( type, fn, false );
}
else if (obj.detachEvent)
{
/* The IE breaks if you're trying to detach an unattached event */
if(obj[type+fn] !== undefined)
{
obj.detachEvent( 'on'+type, obj[type+fn] );
obj[type+fn] = null;
obj['e'+type+fn] = null;
}
}
},
/* Fire an event */
'fire': function(element, event)
{
var eventObject;
/* The good browsers */
if(document.createEvent)
{
eventObject = document.createEvent('HTMLEvents');
/* Type event, bubbles and is cancelable */
eventObject.initEvent(event, true, true);
return !element.dispatchEvent(eventObject);
}
/* The Internet Explorer */
else if(document.createEventObject)
{
eventObject = document.createEventObject();
/* If window.fireEvent() is unknown use document.documentElement.fireEvent() */
if(element === window && window.fireEvent === undefined)
{
element = document.documentElement;
}
return element.fireEvent('on'+event, eventObject);
}
},
/* Check for event bubbling */
'isBubbling': function(event, element)
{
var isBubbling = false,
target = event.relatedTarget || event.toElement;
if(target)
{
/* Walk up the parents of the target until it reaches the element or the body */
while(target !== element && target !== document.body)
{
target = target.parentNode;
if(target === null)
{
break;
}
}
/* The event is bubbling if the target is a child of the element */
isBubbling = (element === target) ? true : false;
}
return isBubbling;
},
/* Suppress the event bubbling */
'preventBubbling': function(event)
{
event.cancelBubble = true;
if(event.stopPropagation)
{
event.stopPropagation();
}
},
/* Suppress default browser behaviour */
'preventDefault': function(event)
{
if(event.preventDefault)
{
event.preventDefault();
}
else
{
event.returnValue = false;
}
return false;
}
};
/**
* Window
*
* @constructor
* @augments Core
*/
this['Window'] =
{
/* Gets the inner width of the window */
'getInnerWidth': function()
{
return window.innerWidth || (window.document.documentElement.clientWidth || window.document.body.clientWidth);
},
/* Gets the inner height of the window */
'getInnerHeight': function()
{
return window.innerHeight || (window.document.documentElement.clientHeight || window.document.body.clientHeight);
}
};
/**
* Mouse
*
* <p>Normalizes mouse properties across browsers.</p>
*
* @constructor
* @augments Core
*/
this['Mouse'] =
{
/* Gets the mouse position */
'getPosition': function(event)
{
var x = 0,
y = 0;
if (event.pageX || event.pageY)
{
x = event.pageX;
y = event.pageY;
}
else if (event.clientX || event.clientY)
{
x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
return { 'x': x, 'y': y };
}
};
/**
* Class constructor.
*
* @constructor
*/
this['Class'] =
{
/**
* Set the class attribute of an element.
*
* @public
* @param {object} element Reference to a DOM element.
* @param {string} className CSS class name string.
*/
'set': function(element, className)
{
if(element && className !== undefined)
{
/* Trim the className */
className = my['String']['trim'](className);
/* Set the className */
element.className = className;
}
},
/**
* Get the class attribute.
*
* @public
* @param {object} element Reference to a DOM element.
* @returns {string} A CSS class name.
*/
'get': function(element)
{
var className = '';
if(element)
{
/* Get the class or className attribute */
className = element.getAttribute('class') || element.getAttribute('className');
/* Replace a null result with an empty string and trim a string result */
className = (className === null) ? '' : my['String']['trim'](className);
}
return className;
},
/**
* Add a string to the class attribute.
*
* @public
* @param {object} element Reference to a DOM element.
* @param {string} className CSS class name string.
*/
'add': function(element, className)
{
if(element && className !== undefined)
{
/* Add the className to the existing one */
className = my['Class']['get'](element) + ' ' + my['String']['trim'](className);
my['Class']['set'](element, className);
}
},
/**
* Remove a string from the class attribute .
*
* @public
* @param {object} element Reference to a DOM element.
* @param {string} className CSS class name string.
*/
'remove': function(element, className)
{
if(element && className !== undefined)
{
/* Trim the className */
className = my['String']['trim'](className);
var classNames = my['Class']['get'](element).split(' '),
max = classNames.length,
newClassName = '',
index,
name;
for(index = 0; index < max; index++)
{
name = classNames[index];
if(name !== '' && name !== className)
{
newClassName += name + ' ';
}
}
/* Set the new class name */
my['Class']['set'](element, newClassName);
}
}
};
/**
* Offset
*
* @constructor
* @augments Core
*/
this['Offset'] =
{
/* Gets the left offset of an element */
'getLeft': function(element)
{
var left = 0;
while( element !== null )
{
left += element.offsetLeft;
element = element.offsetParent;
}
return left;
},
/* Gets the top offset of an element */
'getTop': function(element)
{
var top = 0;
while( element !== null )
{
top += element.offsetTop;
element = element.offsetParent;
}
return top;
}
};
/**
* Opacity
*
* @constructor
* @augments Core
*/
this['Opacity'] =
{
/* Set the opacity for an element */
'set': function(element, opacity)
{
var opacityInPercent = opacity * 100;
/* The good browsers */
element.style.opacity = opacity;
/* Internet Explorer < 9.0 */
element.style.filter = 'alpha(opacity=' + opacityInPercent + ')';
}
};
/**
* Cookie
*
* @constructor
* @augments Core
*/
this['Cookie'] =
{
'create': function(name, value, days)
{
var expires = '';
if(days)
{
var date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
expires = "; expires="+date.toGMTString();
}
document.cookie = name + '=' + value + expires + '; path=/';
},
'read': function(name)
{
name = name + "=";
var cookieArray = document.cookie.split(';'),
max = cookieArray.length,
cookie,
i;
for(i=0;i<max;i++)
{
cookie = cookieArray[i];
while(cookie.charAt(0) === ' ')
{
cookie = cookie.substring(1, cookie.length);
}
if(cookie.indexOf(name) === 0)
{
return cookie.substring(name.length, cookie.length);
}
}
return null;
},
'erase': function(name)
{
my['Cookie']['create'](name, '', -1);
}
};
/**
* XMLHttpRequest (XHR)
*
* <p>This object normalizes the use of the XMLHttpRequest object across
* browsers.</p>
*
* @example
* // Define a request url and a callback function
* var options =
* {
* url: 'http://stadtwerk.org/',
* callback: function(response) { alert(response); }
* }
*
* // Make the XHR call
* Core.XHR.call(options);
*
* @constructor
* @augments Core
*/
this['XHR'] =
{
/* Default options */
defaults:
{
'mode': 'GET',
'url': '',
'asynchronus': true,
'contentType': 'application/x-www-form-urlencoded',
'data': null,
'responseType': 'TEXT',
'callback': null
},
/**
* Call
*/
'call': function(options)
{
var name, request;
/* Evaluate options */
for(name in my['XHR'].defaults)
{
this['call'][name] = (options !== undefined && options[name] !== undefined)
? options[name]
: my['XHR'].defaults[name];
}
/* Create an request object */
request = my['XHR'].createRequestObject();
/* Bind callback and response type settings to request object */
request['coreOptions'] = {'callback' : my['XHR']['call']['callback'],
'responseType' : my['XHR']['call']['responseType']};
/* Open */
request.open(my['XHR']['call']['mode'],
my['XHR']['call']['url'],
my['XHR']['call']['asynchronus']);
/* Bind the getReadyStateHandler method to the readystatechange event */
request.onreadystatechange = my['XHR'].getReadyStateHandler(request);
/* Set content type header */
request.setRequestHeader('Content-Type', my['XHR']['call']['contentType']);
/* Set a non-standard request header to identify XHR requests on the server side */
request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
/* Send the XHR request with data */
request.send(my['XHR']['call']['data']);
},
/* Create an XML HTTP request object */
createRequestObject: function()
{
var request = null;
try
{
request = new XMLHttpRequest();
}
catch(isIE)
{
try
{
request = new ActiveXObject('Msxml2.XMLHTTP');
}
catch(isNotIE)
{
try
{
request = new ActiveXObject('Microsoft.XMLHTTP');
}
catch(failed)
{
request = null;
}
}
}
if(request === null)
{
alert('ERROR: Failed to create an XMLHttpRequest object.');
}
/* Return the request object */
return request;
},
/* Return an anonymous function that listens to the XMLHttpRequest instance */
getReadyStateHandler: function(request)
{
return function()
{
/* Proceed if readyState is DONE - http://www.w3.org/TR/XMLHttpRequest/#states */
if(request.readyState == 4)
{
/* Get HTTP status code */
statusCode = request.status;
/* Proceed if HTTP Status Code is OK - http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1.1 */
if(statusCode === 200)
{
/* If there is a callback function */
if(typeof(request['coreOptions']['callback']) === 'function')
{
/* Get the response */
var response = (request['coreOptions']['responseType'].toLowerCase() === 'xml')
? request.responseXml
: request.responseText;
/* Run the callback function passing the response */
request['coreOptions']['callback'](response);
}
}
}
else
{
return false;
}
};
}
};
/**
* Animation Frame
*
* This is a polyfill for the requestAnimationFrame and cancelAnimationFrame methods.
*
* @see {@link https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/RequestAnimationFrame/Overview.html|Timing control for script-based animations}
*
* @constructor
*/
this['AnimationFrame'] =
{
/**
* Now
*
* @private
* @type {object}
*/
now: Date.now || (new Date).getTime,
/**
* Set timeout
*
* @private
* @type {object}
*/
setTimeout: window.setTimeout,
/**
* Native requestAnimationFrame
*
* This will favour the native requestAnimationFrame implementation.
*
* @private
* @type {object|undefined}
*/
nativeRequestAnimationFrame: window.requestAnimationFrame,
/**
* Native cancelAnimationFrame
*
* This will favour the native cancelAnimationFrame implementation.
*
* @private
* @type {object|undefined}
*/
nativeCancelAnimationFrame: window.cancelAnimationFrame,
/**
* Is native
*
* @private
* @type {bool}
*/
isNative: false,
/**
* Has high resolution timestamp
*
* @see {@link http://www.w3.org/TR/hr-time/#sec-DOMHighResTimeStamp|DOMHighResTimeStamp}
* @private
* @type {bool}
*/
hasHighResolutionTimestamp: false,
/**
* The timestamp when the AnimationFrame has been initialized.
*
* @private
* @type {number}
*/
startTimestamp: 0,
/**
* The timestamp of the last frame of the setTimeout fallback method.
*
* @private
* @type {number}
*/
lastTimestamp: 0,
/**
* The desired frame rate of the setTimeout fallback method.
*
* @private
* @type {number}
*/
frameRate: 60,
/**
* The calculated frame length of the setTimeout fallback method.
*
* @private
* @type {number}
*/
frameLength: 0,
/**
* The timeout identifier of the setTimeout fallback method.
*
* @private
* @type {(null|object)}
*/
timeoutId: null,
/**
* Array of callback functions for the setTimeout fallback method.
*
* @private
* @type {function[]}
*/
callbacks: {},
/**
* Counter for the setTimeout fallback method.
*
* @private
* @type {number}
*/
counter: 0,
/**
* This function will be called directly after Core has been exposed to the window object.
* It sets the frame length and detects all native animationFrame implementations.
*
* @private
*/
initialize: function()
{
var vendorPrefixes = ['o', 'moz', 'webkit', 'ms'],
index = vendorPrefixes.length;
/* Set frame length. */
my['AnimationFrame'].frameLength = 1000 / my['AnimationFrame'].frameRate;
/* Set start timestamp */
my['AnimationFrame'].startTimestamp = my['AnimationFrame'].now();
/* Detect vendor specific native implementations. */
while(index-- && !my['AnimationFrame'].nativeRequestAnimationFrame)
{
my['AnimationFrame'].nativeRequestAnimationFrame = top[vendorPrefixes[index] + 'RequestAnimationFrame'];
my['AnimationFrame'].nativeCancelAnimationFrame = top[vendorPrefixes[index] + 'CancelAnimationFrame'] || top[vendorPrefixes[index] + 'CancelRequestAnimationFrame'];
}
/* If a native implementation has been found. */
if(my['AnimationFrame'].nativeRequestAnimationFrame)
{
/* Request a animation frame to detect features and confirm that the native implementation actually works */
my['AnimationFrame'].nativeRequestAnimationFrame.call(window, window['Core']['AnimationFrame'].detectFeatures);
}
},
/**
* Detects if the native implementation works and which timestamp format is used.
*
* @private
*/
detectFeatures: function(timestamp)
{
/* If the passed timestamp is not a unix timestamp */
if(timestamp < 1e12)
{
/* Assume that the timestamp is a DOMHighResTimeStamp */
my['AnimationFrame'].hasHighResolutionTimestamp = true;
}
/* If this code executes the nativeRequestAnimationFrame implementation works */
my['AnimationFrame'].isNative = true;
},
/**
* Requests an animation frame.
*
* @public
* @param {function} callback
* @return {number} Requested animation frame id.
*/
'request': function(callback)
{
if(my['AnimationFrame'].isNative)
{
if(my['AnimationFrame'].hasHighResolutionTimestamp)
{
/* Return early using the native implementation. */
return my['AnimationFrame'].nativeRequestAnimationFrame.call(window, callback);
}
else
{
/* Wrap the callback to transform the UNIX timestamp into an DOMHighResTimeStamp format. */
return my['AnimationFrame'].nativeRequestAnimationFrame.call(window, function(timestamp)
{
return callback(timestamp - my['AnimationFrame'].startTimestamp);
});
}
}
/* Fallback to the timeout method. */
return my['AnimationFrame'].simulatedRequestAnimationFrame(callback);
},
/**
* Cancels an animation frame.
*
* @public
* @param {number} Requested animation frame id.
*/
'cancel': function(id)
{
if(my['AnimationFrame'].isNative)
{
/* Cancel the animation frame. */
my['AnimationFrame'].nativeCancelAnimationFrame.call(window, id);
}
/* Delete the callback. */
delete my['AnimationFrame'].callbacks[id];
},
/**
* Simulated request animation frame.
*
* @private
* @param {function} callback
* @return {number} Requested animation frame id.
*/
simulatedRequestAnimationFrame: function(callback)
{
/* Increase the counter. */
my['AnimationFrame'].counter++;
/* If there is no timeout running. */
if(my['AnimationFrame'].timeoutId === null)
{
/* Calculate the delay. */
var delay = my['AnimationFrame'].frameLength + my['AnimationFrame'].lastTimestamp - my['AnimationFrame'].now();
/* If the delay is negative reset it to zero. */
if(delay < 0) { delay = 0; }
/* Start the timeout with the delay. */
my['AnimationFrame'].timeoutId = setTimeout(function()
{
var id;
/* Memorize the time of the last frame. */
my['AnimationFrame'].lastTimestamp = my['AnimationFrame'].now();
/* Reset the timeout id. */
my['AnimationFrame'].timeoutId = null;
/* Loop through the callback queue. */
for(id in my['AnimationFrame'].callbacks)
{
/* Call the callback function passing the time difference since the first timestamp in milliseconds */
my['AnimationFrame'].callbacks[id](my['AnimationFrame'].lastTimestamp - my['AnimationFrame'].startTimestamp);
/* Delete the function from the queue. */
delete my['AnimationFrame'].callbacks[id];
}
}, delay);
}
/* Add a new callback function to the queue. */
my['AnimationFrame'].callbacks[my['AnimationFrame'].counter] = callback;
/* Return the counter number as a reference. */
return my['AnimationFrame'].counter;
}
};
/**
* Animation
*
* @constructor
*/
this['Animation'] =
{
/**
* The animation queue holding all animation objects.
*
* @private
* @type {object[]}
*/
queue: [],
/**
* The animation id counter.
*
* @private
* @type {number}
*/
id: 0,
/**
* Start an animation.
*
* <p>This methods takes an object that configures the animation
* object details.</p>
* @example
* Core.DomReady.add(function()
* {
* // The action function for the animation
* var rainbow = function(easing)
* {
* // Get the body element and calculate nice RGB values
* var body = document.getElementsByTagName('body')[0],
* length = 50 * easing,
* center = 128,
* width = 127,
* thirdOfPi = Math.PI / 3;
* rgb = [ ~~(Math.sin(0.3 * length + 0) * width + center),
* ~~(Math.sin(0.3 * length + (2 * thirdOfPi)) * width + center),
* ~~(Math.sin(0.3 * length + (4 * thirdOfPi)) * width + center) ];
*
* // Set the CSS background color of the body element
* body.style.backgroundColor = 'rgb(' + rgb.join(',') + ')';
* }
*
* // Animation options for an endless loop
* var options =
* {
* duration: 23.14159265,
* action: rainbow,
* callback: function(){ Core.Animation.start(options); }
* }
*
* // Start the animation
* Core.Animation.start(options);
* });
*
* @public
* @param {object} options Animation configuration object.
*/
'start': function(options)
{
/* Increase the animation id counter */
my['Animation'].id++;
/* Build the animation object */
var object = { id: my['Animation'].id,
startTime: undefined,
duration: options['duration'] * 1000,
easing: options['easing'] || my['Animation']['linearEasing'],
action: options['action'],
callback: options['callback'] };
/* Add the new animation object to the queue */
my['Animation'].queue.push(object);
/* Start the animation interval with the first element added to the queue */
if(my['Animation'].queue.length === 1)
{
my['AnimationFrame']['request'](my['Animation'].action);
}
return object.id;
},
/**
* Remove an animation object from the queue.
*
* The animation object will be removed by force from the animation
* queue. This will not check if the animation is finished or not!
*
* @public
* @param {number} id Animation object ID.
*/
'remove': function(id)
{
var index = my['Animation'].queue.length;
while(index--)
{
if(my['Animation'].queue[index].id === id)
{
/* Delete the object from the queue */
my['Animation'].queue.splice(index, 1);
break;
}
}
},
/**
* Animate the objects in the queue.
*
* @private
* @param {number} timestamp The animation timestamp.
*/
action: function(timestamp)
{
var index = my['Animation'].queue.length,
object,
elapsedTime;
/* Loop through queue */
while(index--)
{
object = my['Animation'].queue[index];
/* If the start time of the objects animation is not defined yet */
if(typeof object.startTime === 'undefined')
{
/* Set the start time to the timestamp of the current frame */
object.startTime = timestamp;
}
/* Calculate the elapsed time since the beginning of this objects animation */
elapsedTime = (timestamp - object.startTime);
/* If the objects animation is finished */
if(elapsedTime >= object.duration)
{
/* Make sure, that the animation easing ends with 1 */
object.action(1);
/* If there is a callback function */
if(typeof(object.callback) === 'function')
{
/* Call the callback function */
object.callback();
}
/* Delete the object from the queue. This can be done within the
loop without skipping items in the queue because the loop is
negative. The next item in the loop will always have a smaller
index than the deleted one. */
my['Animation'].queue.splice(index, 1);
}
else
{
/* Call the objects action function with the easing */
object.action(object.easing(elapsedTime, object.duration));
}
}
/* If there are objects left to animate */
if(my['Animation'].queue.length > 0)
{
/* Request another frame */
my['AnimationFrame']['request'](my['Animation'].action);
}
},
/**
* Linear easing.
*
* @public
* @see {@link http://www.wolframalpha.com/input/?i=plot+x+from+x+%3D+0+to+1|plot x from x = 0 to 1}
*/
'linearEasing': function(elapsedTime, duration)
{
return (elapsedTime / duration);
},
/**
* Sinusoidal easing in and out - accelerating until halfway, then decelerating.
*
* @public
* @see {@link http://www.wolframalpha.com/input/?i=plot+%281+-+cos%28x+*+PI%29%29+%2F+2+from+x+%3D+0+to+1|plot (1 - cos(x * PI)) / 2 from x = 0 to 1}
*/
'sinusoidalEasingInOut': function(elapsedTime, duration)
{
return (1 - Math.cos((elapsedTime / duration) * Math.PI)) / 2;
}
};
/**
* Get DOM elements by class name
*
* @constructor
* @augments Core
*/
this['getElementsByClass'] = function (className, tag, node)
{
var classElements = [],
pattern = new RegExp('(^|\\s)' + className + '(\\s|$)'),
elements = node.getElementsByTagName(tag),
max = elements.length,
i,
j;
for(i=0, j=0; i<max; i++)
{
if(pattern.test(elements[i].className))
{
classElements[j] = elements[i];
j++;
}
}
return classElements;
};
/**
* Get computed style
*
* <p>Get the computed style of an element by a CSS property name.</p>
*
* @constructor
* @augments Core
*/
this['getComputedStyle'] = function(element, property)
{
if(window.getComputedStyle)
{
return window.getComputedStyle(element).getPropertyValue(property);
}
else
{
if(document.defaultView && document.defaultView.getComputedStyle)
{
return document.defaultView.getComputedStyle.getPropertyValue(Property);
}
else
{
/* Convert hyphenated property syntax to camel case */
property.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); });
if(element.currentStyle)
{
return element.currentStyle(property);
}
else
{
return element.style[property];
}
}
}
};
/**
* String
*
* @constructor
* @augments Core
*/
this['String'] =
{
/* Trim */
'trim': function(string)
{
/* Make sure to deal with a string */
if(typeof string !== 'string')
{
string = string.toString();
};
string = string.replace(/^\s\s*/, '');
var whitespace = /\s/,
end = string.length - 1;
while(whitespace.test(string.charAt(end)))
{
end--;
}
return string.slice(0, end + 1);
},
/**
* Encode HTML entities
*
* Escapes HTML entities that can cause Cross Site Scripting (XSS).
* This function follows the OWASP guideline and replaces the
* following characters:
*
* & --> &
* < --> <
* > --> >
* " --> "
* ' --> '
* / --> /
*
* @param {string} string The string that will be encoded.
* @return {string} string Encoded string.
*/
'encodeHtmlEntities': function(string)
{
/* The ampersand (&) character replacement must be the first
* in line because otherwise it could replace '&' characters
* of the html entites. Since the loop is negative it has to
* be the last item in the array to be executed first.
*/
var find = [/</g, />/g, /"/g, /'/g, /\//g, /&/g],
replace = [ '<', '>', '"', ''', '/', '&'],
index = find.length;
while(index--)
{
string = string.replace(find[index], replace[index]);
}
return string;
}
};
};
/* Expose the Core framework to the global object */
window['Core'] = new Core();
/* Initialize the DomReady handler */
window['Core']['DomReady'].initialize();
/* Initialize the AnimationFrame polyfill */
window['Core']['AnimationFrame'].initialize();
})( window );