/**
* The root namespace of Humanise User Interface
* @namespace
*/
hui = window.hui || {};
(function(hui,agent,window) {
/**
* @namespace
*/
hui.browser = {};
var browser = hui.browser;
/** If the browser is any version of InternetExplorer */
browser.msie = !/opera/i.test(agent) && /MSIE/.test(agent) || /Trident/.test(agent);
/** If the browser is InternetExplorer 6 */
browser.msie6 = agent.indexOf('MSIE 6') !== -1;
/** If the browser is InternetExplorer 7 */
browser.msie7 = agent.indexOf('MSIE 7') !== -1;
/** If the browser is InternetExplorer 8 */
browser.msie8 = agent.indexOf('MSIE 8') !== -1;
/** If the browser is InternetExplorer 9 */
browser.msie9 = agent.indexOf('MSIE 9') !== -1;
/** If the browser is InternetExplorer 9 in compatibility mode */
browser.msie9compat = browser.msie7 && agent.indexOf('Trident/5.0') !== -1;
/** If the browser is InternetExplorer 10 */
browser.msie10 = agent.indexOf('MSIE 10') !== -1;
/** If the browser is InternetExplorer 11 */
browser.msie11 = agent.indexOf('Trident/7.0') !== -1;
/** If the browser is WebKit based */
browser.webkit = agent.indexOf('WebKit') !== -1;
/** If the browser is any version of Safari */
browser.safari = agent.indexOf('Safari') !== -1;
/** If the browser is any version of Chrome */
//browser.chrome = agent.indexOf('Chrome') !== -1;
/** The version of WebKit (null if not WebKit) */
browser.webkitVersion = null;
/** If the browser is Gecko based */
browser.gecko = !browser.webkit && !browser.msie && agent.indexOf('Gecko') !== -1;
/** If the browser is Gecko based */
//browser.chrome = agent.indexOf('Chrome') !== -1;
/** If the browser is safari on iPad */
browser.ipad = browser.webkit && agent.indexOf('iPad') !== -1;
/** If the browser is on Windows */
browser.windows = agent.indexOf('Windows') !== -1;
/** If the browser supports CSS opacity */
browser.opacity = !browser.msie6 && !browser.msie7 && !browser.msie8;
/** If the browser supports CSS Media Queries */
browser.mediaQueries = browser.opacity;
/** If the browser supports CSS animations */
browser.animation = !browser.msie6 && !browser.msie7 && !browser.msie8 && !browser.msie9;
browser.wordbreak = !browser.msie6 && !browser.msie7 && !browser.msie8;
browser.touch = (!!('ontouchstart' in window) || (!!('onmsgesturechange' in window) && !!window.navigator.maxTouchPoints)) ? true : false;
var result = /Safari\/([\d.]+)/.exec(agent);
if (result) {
browser.webkitVersion = parseFloat(result[1]);
}
})(hui,navigator.userAgent,window);
/**
* Log something
* @param {Object} obj The object to log
*/
hui.log = function(obj) {
if (window.console && console.log) {
try { // Somehow it fails in IE if dev tools are not open
console.log.apply(null,arguments);
} catch (e) {}
}
};
// Postponed listeners
hui._postponed = [];
/**
* Register code as ready
* @param name The name of the module
* @param obj The object, function, namespace
*/
hui.define = function(name, obj) {
var postponed = hui._postponed;
for (var i = postponed.length - 1; i >= 0; i--) {
var item = postponed[i];
if (!item) continue;
var deps = [];
for (var j = 0; j < item.requirements.length; j++) {
var path = item.requirements[j];
var found = hui.evaluate(path); // TODO Handle objects defined by name but not evaluated by that name
if (found) {
deps.push(found);
} else {
break;
}
}
if (item.requirements.length == deps.length) {
hui.array.remove(postponed, item);
item.callback.apply(null, deps);
}
}
};
hui._runOrPostpone = function() {
var dependencies = [];
var callback = null;
var i;
for (i = 0; i < arguments.length; i++) {
if (typeof(arguments[i])=='function') {
callback = arguments[i];
} else if (hui.isArray(arguments[i])) {
dependencies = arguments[i];
}
}
var found = [];
for (i = 0; i < dependencies.length; i++) {
var vld = hui.evaluate(dependencies[i]);
if (!vld) {
found = false;
break;
}
found[i] = vld;
}
if (found) {
callback.apply(null, found);
} else {
hui.log('Postponing: ' + dependencies);
hui._postponed.push({requirements:dependencies,callback:callback});
}
};
/**
* Evaluate an expression
*/
hui.evaluate = function(expression, context) {
var path = expression.split('.');
var cur = context || window;
for (var i = 0; i < path.length && cur!==undefined; i++) {
cur = cur[path[i]];
}
return cur;
};
/**
* Defer a function so it will fire when the current "thread" is done
* @param {Function} func The function to defer
* @param {Object} ?bind Optional, the object to bind "this" to
*/
hui.defer = function(func,bind) {
if (bind) {
func = func.bind(bind);
}
window.setTimeout(func);
};
hui.extend = function(subClass, superClass) {
var methods = subClass.prototype;
for (var p in superClass) {
if (superClass.hasOwnProperty(p)) {
subClass[p] = superClass[p];
}
}
function __() { this.constructor = subClass; }
__.prototype = superClass.prototype;
subClass.prototype = new __();
if (methods) {
for (var pr in methods) {
subClass.prototype[pr] = methods[pr];
}
}
};
/**
* Override the properties on the first argument with properties from the last object
* @param {Object} original The object to override
* @param {Object} subject The object to copy the properties from
* @return {Object} The original
*/
hui.override = function(original,subject) {
if (subject) {
for (var prop in subject) {
original[prop] = subject[prop];
}
}
return original;
};
/**
* Loop through items in array or properties in an object.
* If «items» is an array «func» is called with each item.
* If «items» is an object «func» is called with each (key,value)
* @param {Object | Array} items The object or array to loop through
* @param {Function} func The callback to handle each item
*/
hui.each = function(items, func) {
var i;
if (hui.isArray(items)) {
for (i = 0; i < items.length; i++) {
func(items[i], i);
}
} else if (items instanceof NodeList) {
for (i = 0; i < items.length; i++) {
func(items.item(i), i);
}
} else if (hui.isDefined(items)) {
for (var key in items) {
if (items.hasOwnProperty(key)) {
func(key, items[key]);
}
}
}
};
/**
* Return text if condition is met
* @param {Object} condition The condition to test
* @param {String} text The text to return when condition evaluates to true
*/
hui.when = function(condition,text) {
return condition ? text : '';
};
/**
* Converts a string to an int if it is only digits, otherwise remains a string
* @param {String} str The string to convert
* @returns {Object} An int of the string or the same string
*/
hui.intOrString = function(str) {
if (hui.isDefined(str)) {
var result = /[0-9]+/.exec(str);
if (result !== null && result[0] == str) {
if (parseInt(str,10) == str) {
return parseInt(str, 10);
}
}
}
return str;
};
/**
* Make sure a number is between a min / max
*/
hui.between = function(min, value, max) {
var result = Math.min(max, Math.max(min, value));
return isNaN(result) ? min : result;
};
/**
* Fit a box inside a container while preserving aspect ratio (note: expects sane input)
* @param {Object} box The box to scale {width : 200, height : 100}
* @param {Object} container The container to fit the box inside {width : 20, height : 40}
* @returns {Object} An object of the new box {width : 20, height : 10}
*/
hui.fit = function(box,container,options) {
options = options || {};
var boxRatio = box.width / box.height;
var containerRatio = container.width / container.height;
var width, height;
if (options.upscale===false && box.width<=container.width && box.height<=container.height) {
width = box.width;
height = box.height;
}
else if (boxRatio > containerRatio) {
width = container.width;
height = Math.round(container.width/box.width * box.height);
} else {
width = Math.round(container.height/box.height * box.width);
height = container.height;
}
return {width : width, height : height};
};
/**
* Checks if a string has non-whitespace characters
* @param {String} str The string
*/
hui.isBlank = function(str) {
if (str===null || typeof(str)==='undefined' || str==='') {
return true;
}
return typeof(str)=='string' && hui.string.trim(str).length === 0;
};
/**
* Checks that an object is not null and not undefined
* @param {Object} obj The object to check
*/
hui.isDefined = function(obj) {
return obj!==null && typeof(obj)!=='undefined';
};
/**
* Checks if an object is a string
* @param {Object} obj The object to check
*/
hui.isString = function(obj) {
return typeof(obj)==='string';
};
/**
* Checks if an object is a number
* @param {Object} obj The object to check
*/
hui.isNumber = function(obj) {
return typeof(obj)==='number';
};
/**
* Checks if an object is an array
* @param {Object} obj The object to check
*/
hui.isArray = function(obj) {
if (obj === null || obj === undefined) {
return false;
}
if (obj.constructor == Array) {
return true;
} else {
return Object.prototype.toString.call(obj) === '[object Array]';
}
};
///////////////////////// Strings ///////////////////////
/** @namespace */
hui.string = {
/**
* Test that a string start with another string
* @param {String} str The string to test
* @param {String} start The string to look for at the start
* @returns {Boolean} True if «str» starts with «start»
*/
startsWith : function(str, start) {
if (!hui.isString(str) || !hui.isString(start)) {
return false;
}
return str.match("^"+start) == start;
},
/**
* Test that a string ends with another string
* @param {String} str The string to test
* @param {String} end The string to look for at the end
* @returns {Boolean} True if «str» ends with «end»
*/
endsWith : function(str, end) {
if (!hui.isString(str) || !hui.isString(end)) {
return false;
}
return str.match(end+"$") == end;
},
/**
* Make a string camelized
* @param {String} str The string to camelize
* @returns {String} The camelized string
*/
camelize : function(str) {
if (str.indexOf('-')==-1) {
return str;
}
var oStringList = str.split('-');
var camelizedString = str.indexOf('-') === 0 ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1) : oStringList[0];
for (var i = 1, len = oStringList.length; i < len; i++) {
var s = oStringList[i];
camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
}
return camelizedString;
},
/**
* Trim whitespace including unicode chars
* @param {String} str The text to trim
* @returns {String} The trimmed text
*/
trim : function(str) {
if (!hui.isDefined(str)) {
return '';
}
if (!hui.isString(str)) {
str = String(str);
}
return str.replace(/^[\s\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000]+|[\s\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000]+$/g, '');
},
/**
* Inserts invisible break chars in string so it will wrap
* @param {String} str The text to wrap
* @returns {String} The wrapped text
*/
wrap : function(str) {
if (str===null || str===undefined) {
return '';
}
return str.split('').join("\u200B");
},
/**
* Shorten a string to a maximum length
* @param {String} str The text to shorten
* @param {int} length The maximum length
* @returns {String} The shortened text, '' if undefined or null string
*/
shorten : function(str,length) {
if (hui.isNumber(str)) {
str = str+'';
}
else if (!hui.isString(str)) {return '';}
if (str.length > length) {
return str.substring(0,length-3) + '...';
}
return str;
},
/**
* Escape the html in a string (robust)
* @param {String} str The text to escape
* @returns {String} The escaped text
*/
escapeHTML : function(str) {
if (!hui.isDefined(str)) {return '';}
return hui.build('div',{text:str}).innerHTML;
},
/**
* Escape the html in a string (fast)
* @param {String} str The text to escape
* @returns {String} The escaped text
*/
escape : function(str) {
if (!hui.isString(str)) {
return str;
}
var tagsToReplace = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'`': '`'
};
return str.replace(/[&<>'`"]/g, function(tag) {
return tagsToReplace[tag] || tag;
});
},
/**
* Converts a JSON string into an object
* @param json {String} The JSON string to parse
* @returns {Object} The object
*/
fromJSON : function(json) {
try {
return JSON.parse(json);
} catch (e) {
hui.log(e);
return null;
}
},
/**
* Converts an object into a JSON string
* @param obj {Object} the object to convert
* @returns {String} A JSON representation
*/
toJSON : function(obj) {
return JSON.stringify(obj);
}
};
//////////////////////// Array //////////////////////////
/** @namespace */
hui.array = {
/**
* Add an object to an array if it not already exists
* @param {Array} arr The array
* @param {Object} value The object to add
*/
add : function(arr, value) {
if (value.constructor == Array) {
for (var i=0; i < value.length; i++) {
if (!hui.array.contains(arr,value[i])) {
arr.push(value);
}
}
} else {
if (!hui.array.contains(arr,value)) {
arr.push(value);
}
}
},
/**
* Check if an array contains a value
* @param {Array} arr The array
* @param {Object} value The object to check for
* @returns {boolean} true if the value is in the array
*/
contains : function(arr,value) {
return hui.array.indexOf(arr,value) !== -1;
},
/**
* Add or remove a value from an array.
* If the value exists all instances are removed, otherwise the value is added
* @param {Array} arr The array to change
* @param {Object} value The value to flip
*/
flip : function(arr,value) {
if (hui.array.contains(arr,value)) {
hui.array.remove(arr,value);
} else {
arr.push(value);
}
},
/**
* Remove all instances of a value from an array
* @param {Array} arr The array to change
* @param {Object} value The value to remove
*/
remove : function(arr,value) {
for (var i = arr.length - 1; i >= 0; i--){
if (arr[i]==value) {
arr.splice(i,1);
}
}
},
/**
* Find the first index of a value in an array, -1 if not found
* @param {Array} arr The array to inspect
* @param {Object} value The value to find
* @returns {Number} The index of the first occurrence, -1 if not found.
*/
indexOf : function(arr,value) {
for (var i=0; i < arr.length; i++) {
if (arr[i]===value) {
return i;
}
}
return -1;
},
/**
* Split a string, like "1,4,6" into an array of integers.
* @param {String} The string to split
* @returns {Array} An array of integers
*/
toIntegers : function(str) {
var array = str.split(',');
for (var i = array.length - 1; i >= 0; i--){
array[i] = parseInt(array[i],10);
}
return array;
}
};
////////////////////// DOM ////////////////////
/** @namespace */
hui.dom = {
isElement : function(node,name) {
return node.nodeType==1 && (name===undefined ? true : node.nodeName.toLowerCase()==name);
},
isDefinedText : function(node) {
return node.nodeType==3 && node.nodeValue.length>0;
},
addText : function(node,text) {
node.appendChild(document.createTextNode(text));
},
// TODO: Move to hui.get
firstChild : function(node) {
var children = node.childNodes;
for (var i=0; i < children.length; i++) {
if (children[i].nodeType==1) {
return children[i];
}
}
return null;
},
parse : function(html) {
var dummy = hui.build('div',{html:html});
return hui.get.firstChild(dummy);
},
clear : function(node) {
var children = node.childNodes;
for (var i = children.length - 1; i >= 0; i--) {
children[i].parentNode.removeChild(children[i]);
}
},
remove : function(node) {
if (node.parentNode) {
node.parentNode.removeChild(node);
}
},
replaceNode : function(oldNode,newNode) {
if (newNode.parentNode) {
newNode.parentNode.removeChild(newNode);
}
oldNode.parentNode.insertBefore(newNode,oldNode);
oldNode.parentNode.removeChild(oldNode);
},
changeTag : function(node,tagName) {
var replacement = hui.build(tagName);
// Copy the children
while (node.firstChild) {
replacement.appendChild(node.firstChild); // *Moves* the child
}
// Copy the attributes
for (var i = node.attributes.length - 1; i >= 0; --i) {
replacement.attributes.setNamedItem(node.attributes[i].cloneNode());
}
// Insert it
node.parentNode.insertBefore(replacement, node);
// Remove the wns
node.parentNode.removeChild(node);
return replacement;
},
insertBefore : function(target,newNode) {
target.parentNode.insertBefore(newNode,target);
},
insertAfter : function(target,newNode) {
var next = target.nextSibling;
if (next) {
next.parentNode.insertBefore(newNode,next);
} else {
target.parentNode.appendChild(newNode);
}
},
replaceHTML : function(node,html) {
node = hui.get(node);
node.innerHTML = html;
},
runScripts : function(node) {
if (hui.dom.isElement(node)) {
if (hui.dom.isElement(node,'script')) {
/*jshint evil:true */
eval(node.innerHTML);
} else {
var scripts = node.getElementsByTagName('script');
for (var i=0; i < scripts.length; i++) {
/*jshint evil:true */
eval(scripts[i].innerHTML);
}
}
}
},
setText : function(node,text) {
if (text===undefined || text===null) {
text = '';
}
var c = node.childNodes;
var updated = false;
for (var i = c.length - 1; i >= 0; i--){
if (!updated && c[i].nodeType === 3) {
c[i].nodeValue=text;
updated = true;
} else {
node.removeChild(c[i]);
}
}
if (!updated) {
hui.dom.addText(node,text);
}
},
getText : function(node) {
var txt = '';
var c = node.childNodes;
for (var i=0; i < c.length; i++) {
if (c[i].nodeType === 3 && c[i].nodeValue !== null) {
txt+= c[i].nodeValue;
} else if (c[i].nodeType == 1) {
txt+= hui.dom.getText(c[i]);
}
}
return txt;
},
isVisible : function(node) {
while (node) {
if (node.style && (hui.style.get(node,'display')==='none' || hui.style.get(node,'visibility')==='hidden')) {
return false;
}
node = node.parentNode;
}
return true;
},
isDescendantOrSelf : function(element,parent) {
while (element) {
if (element==parent) {
return true;
}
element = element.parentNode;
}
return false;
}
};
///////////////////// Form //////////////////////
/** @namespace */
hui.form = {
getValues : function(node) {
var params = {};
var inputs = node.getElementsByTagName('input');
for (var i=0; i < inputs.length; i++) {
if (hui.isDefined(inputs[i].name)) {
params[inputs[i].name] = inputs[i].value;
}
}
return params;
}
};
///////////////////////////// Quering ////////////////////////
/**
* @namespace
* Functions for finding elements
*
* @function
* Get an element by ID. If the ID is not a string it is returned.
* @param {String | Element} id The ID to find
* @returns {Element} The element with the ID or null
*/
hui.get = function(id) {
if (typeof(id)=='string') {
return document.getElementById(id);
}
return id;
};
/**
* Get array of child elements of «node», not a NodeList
*/
hui.get.children = function(node) {
var children = [];
var x = node.childNodes;
for (var i=0; i < x.length; i++) {
if (hui.dom.isElement(x[i])) {
children.push(x[i]);
}
}
return children;
};
hui.get.next = function(element) {
if (!element) {
return null;
}
if (element.nextElementSibling) {
return element.nextElementSibling;
}
if (!element.nextSibling) {
return null;
}
var next = element.nextSibling;
while (next && next.nodeType!=1) {
next = next.nextSibling;
}
if (next && next.nodeType==1) {
return next;
}
return null;
};
hui.get.previous = function(element) {
if (!element) {
return null;
}
if (element.previousElementSibling) {
return element.previousElementSibling;
}
if (!element.previousSibling) {
return null;
}
var previous = element.previousSibling;
while (previous && previous.nodeType!=1) {
previous = previous.previousSibling;
}
if (previous && previous.nodeType==1) {
return previous;
}
return null;
};
hui.get.before = function(element) {
var elements = [];
if (element) {
var nodes = element.parentNode.childNodes;
for (var i=0; i < nodes.length; i++) {
if (nodes[i]==element) {
break;
} else if (nodes[i].nodeType===1) {
elements.push(nodes[i]);
}
}
}
return elements;
};
/**
* Find all sibling elements after «element»
*/
hui.get.after = function(element) {
var elements = [];
var next = hui.get.next(element);
while (next) {
elements.push(next);
next = hui.get.next(next);
}
return elements;
};
hui.get.firstByClass = function(parentElement,className,tag) {
parentElement = hui.get(parentElement) || document.body;
if (parentElement.querySelector) {
return parentElement.querySelector((tag ? tag+'.' : '.')+className);
} else {
var children = parentElement.getElementsByTagName(tag || '*');
for (var i=0;i<children.length;i++) {
if (hui.cls.has(children[i],className)) {
return children[i];
}
}
}
return null;
};
hui.get.byClass = function(parentElement,className,tag) {
parentElement = hui.get(parentElement) || document.body;
var i;
if (parentElement.querySelectorAll) {
var nl = parentElement.querySelectorAll((tag ? tag+'.' : '.')+className);
// Important to convert into array...
var l=[];
for(i=0, ll=nl.length; i!=ll; l.push(nl[i++]));
return l;
} else {
var children = parentElement.getElementsByTagName(tag || '*'),
out = [];
for (i=0;i<children.length;i++) {
if (hui.cls.has(children[i],className)) {
out[out.length] = children[i];
}
}
return out;
}
};
hui.get.byId = function(e,id) {
var children = e.childNodes;
for (var i = children.length - 1; i >= 0; i--) {
if (children[i].nodeType===1 && children[i].getAttribute('id')===id) {
return children[i];
} else {
var found = hui.get.byId(children[i],id);
if (found) {
return found;
}
}
}
return null;
};
/**
* Find first descendant by tag (excluding self)
* @param {Element} node The node to start from, will start from body if null
* @param {String} tag The name of the node to find
* @returns {Element} The found element or null
*/
hui.get.firstByTag = function(node,tag) {
node = hui.get(node) || document.body;
if (node.querySelector && tag!=='*') {
return node.querySelector(tag);
}
var children = node.getElementsByTagName(tag);
return children[0];
};
hui.get.firstChild = hui.dom.firstChild;
hui.find = function(selector,context) {
return (context || document).querySelector(selector);
};
hui.findAll = function(selector,context) {
var nl = (context || document).querySelectorAll(selector);
return Array.prototype.slice.call(nl);
};
hui.closest = function(selector, context) {
var parent = context;
while (parent && parent.nodeType === 1) {
if (hui.matches(parent, selector)) {
return parent;
}
parent = parent.parentNode;
}
};
hui.matches = function(node, selector) {
var docEl = document.documentElement,
matches = docEl.matches || docEl.webkitMatchesSelector || docEl.mozMatchesSelector || docEl.msMatchesSelector || docEl.oMatchesSelector;
return matches.call(node, selector);
};
if (!document.querySelector) {
hui.find = function(selector,context) {
context = context || document.documentElement;
if (selector[0] == '.') {
return hui.get.firstByClass(context,selector.substr(1));
} else {
return hui.get.firstByTag(context,selector);
}
};
}
hui.collect = function(selectors,context) {
var copy = {};
for (var key in selectors) {
copy[key] = hui.find(selectors[key],context);
}
return copy;
};
//////////////////////// Elements ///////////////////////////
/**
* Builds an element with the «name» and «options»
*
* @param {String} name The name of the new element (. adds class)
* @param {Object} options The options
* @param {String} options.html Inner HTML
* @param {String} options.text Inner text
* @param {String} options.className
* @param {String} options.class
* @param {Object} options.style Map of styles (see: hui.style.set)
* @param {Element} options.parent
* @param {Element} options.parentFirst
* @param {Element} options.before
* @param {Document} doc (Optional) The document to create the element for
* @returns {Element} The new element
*/
hui.build = function(name,options,doc) {
doc = doc || document;
var cls = '';
if (name.indexOf('.') !== -1) {
var split = name.split('.');
name = split[0];
for (var i = 1; i < split.length; i++) {
if (i>1) {cls+=' ';}
cls+=split[i];
}
}
var e = doc.createElement(name);
if (cls) {
e.className = cls;
}
if (options) {
for (var prop in options) {
if (prop=='text') {
e.appendChild(doc.createTextNode(options.text));
} else if (prop=='html') {
e.innerHTML=options.html;
} else if (prop=='parent' && hui.isDefined(options.parent)) {
options.parent.appendChild(e);
} else if (prop=='parentFirst') {
if (options.parentFirst.childNodes.length === 0) {
options.parentFirst.appendChild(e);
} else {
options.parentFirst.insertBefore(e,options.parentFirst.childNodes[0]);
}
} else if (prop=='children') {
for (var i = 0; i < options.children.length; i++) {
e.appendChild(options.children[i]);
}
} else if (prop=='before') {
options.before.parentNode.insertBefore(e,options.before);
} else if (prop=='className') {
hui.cls.add(e,options.className);
} else if (prop=='class') {
hui.cls.add(e,options['class']);
} else if (prop=='style' && typeof(options[prop])=='object') {
hui.style.set(e,options[prop]);
} else if (prop=='style' && (hui.browser.msie7 || hui.browser.msie6)) {
e.style.setAttribute('cssText',options[prop]);
} else if (hui.isDefined(options[prop])) {
e.setAttribute(prop,options[prop]);
}
}
}
return e;
};
/////////////////////// Position ///////////////////////
/**
* Functions for getting and changing the position of elements
* @namespace
*/
hui.position = {
getTop : function(element) {
element = hui.get(element);
if (element) {
var top = element.offsetTop,
tempEl = element.offsetParent;
while (tempEl !== null) {
top += tempEl.offsetTop;
tempEl = tempEl.offsetParent;
}
return top;
}
else return 0;
},
getLeft : function(element) {
element = hui.get(element);
if (element) {
var left = element.offsetLeft,
tempEl = element.offsetParent;
while (tempEl !== null) {
left += tempEl.offsetLeft;
tempEl = tempEl.offsetParent;
}
return left;
}
else return 0;
},
get : function(element) {
return {
left : hui.position.getLeft(element),
top : hui.position.getTop(element)
};
},
getScrollOffset : function(element) {
element = hui.get(element);
var top = 0, left = 0;
do {
top += element.scrollTop || 0;
left += element.scrollLeft || 0;
element = element.parentNode;
} while (element);
return {top:top,left:left};
},
/**
* Place on element relative to another
* Example hui.position.place({target : {element : «node», horizontal : «0-1»}, source : {element : «node», vertical : «0 - 1»}, insideViewPort:«boolean», viewPortMargin:«integer»})
*/
place : function(options) {
var left = 0,
top = 0,
src = hui.get(options.source.element),
trgt = hui.get(options.target.element),
trgtPos = {left : hui.position.getLeft(trgt), top : hui.position.getTop(trgt) };
left = trgtPos.left + trgt.clientWidth * (options.target.horizontal || 0);
top = trgtPos.top + trgt.clientHeight * (options.target.vertical || 0);
left -= src.clientWidth * (options.source.horizontal || 0);
top -= src.clientHeight * (options.source.vertical || 0);
if (options.top) {
top += options.top;
}
if (options.left) {
left += options.left;
}
if (options.insideViewPort) {
var w = hui.window.getViewWidth();
if (left + src.clientWidth > w) {
left = w - src.clientWidth - (options.viewPortMargin || 0);
}
if (left < 0) {left=0;}
if (top < 0) {top=0;}
var height = hui.window.getViewHeight();
var vertMax = hui.window.getScrollTop()+hui.window.getViewHeight()-src.clientHeight,
vertMin = hui.window.getScrollTop();
top = Math.max(Math.min(top,vertMax),vertMin);
}
src.style.top = Math.round(top)+'px';
src.style.left = Math.round(left)+'px';
},
/** Get the remaining height within parent when all siblings has used their height */
getRemainingHeight : function(element) {
var height = element.parentNode.clientHeight;
var siblings = element.parentNode.childNodes;
for (var i=0; i < siblings.length; i++) {
var sib = siblings[i];
if (sib!==element && hui.dom.isElement(siblings[i])) {
if (hui.style.get(sib,'position')!='absolute') {
height-=sib.offsetHeight;
}
}
}
return height;
}
};
////////////////////// Window /////////////////////
/** @namespace */
hui.window = {
getScrollTop : function() {
if (window.pageYOffset) {
return window.pageYOffset;
} else if (document.documentElement && document.documentElement.scrollTop) {
return document.documentElement.scrollTop;
} else if (document.body) {
return document.body.scrollTop;
}
return 0;
},
getScrollLeft : function() {
if (window.pageYOffset) {
return window.pageXOffset;
} else if (document.documentElement && document.documentElement.scrollTop) {
return document.documentElement.scrollLeft;
} else if (document.body) {
return document.body.scrollLeft;
}
return 0;
},
/**
* Scroll to an element, will try to show the element in the middle of the screen and only scroll if it makes sence
* @param {Object} options {element:«the element to scroll to»,duration:«milliseconds»,top:«disregarded pixels at top»}
*/
scrollTo : function(options) {
options = hui.override({duration:0,top:0},options);
var node = options.element;
var pos = hui.position.get(node);
var viewTop = hui.window.getScrollTop();
var viewHeight = hui.window.getViewHeight();
var viewBottom = viewTop+viewHeight;
if (viewTop < pos.top + node.clientHeight || (pos.top)<viewBottom) {
var top = pos.top - Math.round((viewHeight - node.clientHeight) / 2);
top-= options.top/2;
top = Math.max(0, top);
var startTime = new Date().getTime();
var func;
func = function() {
var pos = (new Date().getTime()-startTime)/options.duration;
if (pos>1 || isNaN(pos)) {
pos = 1;
}
var scrl = viewTop+Math.round((top-viewTop)*hui.ease.fastSlow(pos));
window.scrollTo(0, scrl);
if (pos<1) {
window.setTimeout(func);
}
};
func();
}
},
/**
* Get the height of the viewport (the visible part of the page)
*/
getViewHeight : function() {
if (window.innerHeight) {
return window.innerHeight;
} else if (document.documentElement && document.documentElement.clientHeight) {
return document.documentElement.clientHeight;
} else if (document.body) {
return document.body.clientHeight;
}
},
/**
* Get the width of the viewport (the visible part of the page)
*/
getViewWidth : function() {
if (document.documentElement && document.documentElement.clientWidth) {
return document.documentElement.clientWidth;
} else if (document.body) {
return document.body.clientWidth;
}
return window.innerWidth;
}
};
/////////////////////////// Class handling //////////////////////
/** @namespace */
hui.cls = {
/**
* Check if an element has a class
* @param {Element} element The element
* @param {String} className The class
* @returns {boolean} true if the element has the class
*/
has : function(element, className) {
element = hui.get(element);
if (!element || !element.className) {
return false;
}
if (element.hasClassName) {
return element.hasClassName(className);
}
if (element.className==className) {
return true;
}
if (element.className.animVal!==undefined) {
return false; // TODO (handle SVG stuff)
}
var a = element.className.split(/\s+/);
for (var i = 0; i < a.length; i++) {
if (a[i] == className) {
return true;
}
}
return false;
},
/**
* Add a class to an element
* @param {Element} element The element to add the class to
* @param {String} className The class
*/
add : function(element, className) {
element = hui.get(element);
if (!element) {
return;
}
if (element.classList && className.indexOf(' ') === -1) {
element.classList.add(className);
} else if (element.addClassName) {
element.addClassName(className);
} else {
hui.cls.remove(element, className);
element.className += ' ' + className;
}
},
/**
* Remove a class from an element
* @param {Element} element The element to remove the class from
* @param {String} className The class
*/
remove : function(element, className) {
element = hui.get(element);
if (!element || !element.className) {return;}
if (element.removeClassName) {
element.removeClassName(className);
}
if (element.className==className) {
element.className='';
return;
}
var newClassName = '';
var a = element.className.split(/\s+/);
for (var i = 0; i < a.length; i++) {
if (a[i] != className) {
if (i > 0) {
newClassName += ' ';
}
newClassName += a[i];
}
}
element.className = newClassName;
},
/**
* Add or remove a class from an element
* @param {Element} element The element
* @param {String} className The class
*/
toggle : function(element,className) {
if (hui.cls.has(element,className)) {
hui.cls.remove(element,className);
} else {
hui.cls.add(element,className);
}
},
/**
* Add or remove a class from an element
* @param {Element} element The element
* @param {String} className The class
* @param {boolean} add If the class should be added or removed
*/
set : function(element,className,add) {
if (add) {
hui.cls.add(element,className);
} else {
hui.cls.remove(element,className);
}
}
};
///////////////////// Events //////////////////
hui.on = function(node,event,func,bind) {
if (arguments.length == 1 && typeof(node) == 'function') {
return hui.onReady(node);
}
if (arguments.length == 2 && typeof(event) == 'function' && hui.isArray(node)) {
return hui._runOrPostpone.apply(hui, arguments);
}
if (event=='tap') {
if (bind) {
func = func.bind(bind);
}
var moved = false;
var touched = false;
hui.listen(node,'touchstart',function() {
touched = true;
moved = false;
},bind);
hui.listen(node,'touchmove',function() {
moved = true;
},bind);
/*hui.listen(node,'touchcancel',function() {
hui.log('cancel')
},bind);*/
hui.listen(node,'touchend',function(ev) {
touched = false;
if (!moved) {
touched = true;
hui._callListener(func, ev);
}
},bind);
hui.listen(node,'click',function(ev) {
if (!moved && !touched) {
hui._callListener(func, ev);
}
},bind);
} else {
hui.listen(node,event,func,bind);
}
};
hui._callListener = function(listener, ev) {
if (typeof(listener)=='function') {
listener(ev);
} else {
for (var key in listener) {
if (listener.hasOwnProperty(key)) {
var found = hui.closest(key, ev.target);
if (found) {
listener[key](found, ev);
return;
}
}
}
}
};
/**
* Add an event listener to an element
* @param {Element} element The element to listen on
* @param {String} type The event to listen for
* @param {Function} listener The function to be called
* @param {object} ?bindTo Bind the listener to it
*/
hui.listen = function(element, type, listener, bindTo) {
element = hui.get(element);
if (!element) {
return;
}
var l = listener;
if (typeof(listener)!=='function') {
l = function(e) {
hui._callListener(listener, e);
}
}
else if (bindTo) {
l = listener.bind(bindTo);
}
if(document.addEventListener) {
element.addEventListener(type, l);
} else {
element.attachEvent('on'+type, l);
}
};
/**
* Add an event listener to an element, it will only fire once
* @param {Element} element The element to listen on
* @param {String} type The event to listen for
* @param {Function} listener The function to be called
*/
hui.listenOnce = function(element,type,listener) {
var func = null;
func = function(e) {
hui.unListen(element,type,func);
listener(e);
};
hui.listen(element,type,func);
};
/**
* Remove an event listener from an element
* @param {Element} element The element to remove listener from
* @param {String} type The event to remove
* @param {Function} listener The function to remove
* @param {boolean} useCapture If the listener should "capture"
*/
hui.unListen = function(el,type,listener,useCapture) {
el = hui.get(el);
if(document.removeEventListener) {
el.removeEventListener(type,listener,useCapture ? true : false);
} else {
el.detachEvent('on'+type, listener);
}
};
/** Creates an event wrapper for an event
* @param event The DOM event
* @returns {hui.Event} An event wrapper
*/
hui.event = function(event) {
if (event!==undefined && event.huiEvent===true) {
return event;
}
return new hui.Event(event);
};
/**
* Wrapper for events
* @class
* @param event {Event} The DOM event
*/
hui.Event = function(event) {
this.huiEvent = true;
/** The event */
this.event = event = event || window.event;
if (!event) {
hui.log('No event');
}
/** The target element */
this.element = event.target ? event.target : event.srcElement;
/** If the shift key was pressed */
this.shiftKey = event.shiftKey;
/** If the alt key was pressed */
this.altKey = event.altKey;
/** If the command key was pressed */
this.metaKey = event.metaKey;
/** If the return key was pressed */
this.returnKey = event.keyCode==13;
/** If the escape key was pressed */
this.escapeKey = event.keyCode==27;
/** If the space key was pressed */
this.spaceKey = event.keyCode==32;
/** If the backspace was pressed */
this.backspaceKey = event.keyCode==8;
/** If the up key was pressed */
this.upKey = event.keyCode==38;
/** If the down key was pressed */
this.downKey = event.keyCode==40;
/** If the left key was pressed */
this.leftKey = event.keyCode==37;
/** If the right key was pressed */
this.rightKey = event.keyCode==39;
/** The key code */
this.keyCode = event.keyCode;
};
hui.Event.prototype = {
/**
* Get the left coordinate
* @returns {Number} The left coordinate
* @type {Number}
*/
getLeft : function() {
var left = 0;
if (this.event) {
if (this.event.pageX) {
left = this.event.pageX;
} else if (this.event.clientX) {
left = this.event.clientX + hui.window.getScrollLeft();
}
}
return left;
},
/**
* Get the top coordinate
* @returns {Number} The top coordinate
*/
getTop : function() {
var top = 0;
if (this.event) {
if (this.event.pageY) {
top = this.event.pageY;
} else if (this.event.clientY) {
top = this.event.clientY + hui.window.getScrollTop();
}
}
return top;
},
/** Get the node the event originates from
* @returns {ELement} The originating element
*/
getElement : function() {
return this.element;
},
/** Finds the nearest ancester with a certain class name
* @param cls The css class name
* @returns {Element} The found element or null
*/
findByClass : function(cls) {
return hui.closest('.' + cls, this.element);
},
/** Finds the nearest ancester with a certain tag name
* @param tag The tag name
* @returns {Element} The found element or null
*/
findByTag : function(tag) {
return hui.closest(tag, this.element);
},
/* @Deprecated */
find : function(func) {
return this.closest(func);
},
closest : function(func) {
if (hui.isString(func)) {
return hui.closest(func, this.element);
}
var parent = this.element;
while (parent) {
if (func(parent)) {
return parent;
}
parent = parent.parentNode;
}
return null;
},
isDescendantOf : function(node) {
var parent = this.element;
while (parent) {
if (parent===node) {
return true;
}
parent = parent.parentNode;
}
return false;
},
/** Stops the event from propagating */
stop : function() {
hui.stop(this.event);
},
prevent : function() {
if (this.event.preventDefault) {
this.event.preventDefault();
}
}
};
/**
* Stops an event from propagating
* @param event A standard DOM event, NOT an hui.Event
*/
hui.stop = function(event) {
if (!event) {event = window.event;}
if (event.stopPropagation) {event.stopPropagation();}
if (event.preventDefault) {event.preventDefault();}
event.cancelBubble = true;
event.stopped = true;
};
hui._ = hui._ || [];
hui._ready = document.readyState == 'complete';// || document.readyState;
// TODO Maybe interactive is too soon???
hui.onReady = function() {
if (hui._ready) {
hui._runOrPostpone.apply(hui, arguments);
} else {
hui._.push(arguments);
}
};
hui.onDraw = function(func) {
window.setTimeout(func,13);
};
hui.onDraw = (function(vendors,window) {
var found = window.requestAnimationFrame;
for(var x = 0; x < vendors.length && !found; ++x) {
found = window[vendors[x]+'RequestAnimationFrame'];
}
return found ? found.bind(window) : hui.onDraw;
})(['ms', 'moz', 'webkit', 'o'],window);
/**
* Execute a function when the DOM is ready
* @param delegate The function to execute
*/
hui._onReady = function(delegate) {
if (document.readyState == 'interactive') {
window.setTimeout(delegate);
}
else if (window.addEventListener) {
window.addEventListener('DOMContentLoaded',delegate,false);
}
else if (window.attachEvent) {
document.attachEvent("onreadystatechange", function(){
if(document.readyState === "complete"){
document.detachEvent("onreadystatechange", arguments.callee);
delegate();
}
});
}
};
///////////////////////// Request /////////////////////////
/**
* Send a HTTP request
* <pre><strong>options:</strong> {
* method : «'<strong>POST</strong>' | 'get' | 'rEmOVe'»,
* async : <strong>true</strong>,
* headers : {<strong>Ajax : true</strong>, header : 'value'},
* file : «HTML5-file»,
* files : «HTML5-files»,
* parameters : {key : 'value'},
*
* $success : function(transport) {
* // when status is 200
* },
* $forbidden : function(transport) {
* // when status is 403
* },
* $abort : function(transport) {
* // when request is aborted
* },
* $failure : function(transport) {
* // when status is not 200 (if status is 403 and $forbidden is set then $failure will not be called)
* },
* $exception : function(exception,transport) {
* // When an exception has occurred while calling on«Something», If not set the exception will be thrown
* },
* $progress : function(current,total) {
* // Progress for file uploads (maybe also other requests?)
* },
* $load : functon() {
* // When file upload is transfered?
* }
*}
* </pre>
*
* @param options The options
* @returns {XMLHttpRequest} The transport
*/
hui.request = function(options) {
options = hui.override({
method : 'POST',
async : true,
headers : {Accept : 'application/json'}
},options);
var transport = new XMLHttpRequest();
if (options.credentials) {
transport.withCredentials = true;
}
if (!transport) {return;}
transport.onreadystatechange = function() {
if (transport.readyState == 4) {
if (transport.status == 200 && options.$success) {
options.$success(transport);
} else if (transport.status == 403 && options.$forbidden) {
options.$forbidden(transport);
} else if (transport.status !== 0 && options.$failure) {
options.$failure(transport);
} else if (transport.status === 0 && options.$abort) {
options.$abort(transport);
}
if (options.$finally) {
options.$finally();
}
}
};
var method = options.method.toUpperCase();
transport.open(method, options.url, options.async);
var body = null;
if (method=='POST' && options.file) {
if (false) {
body = options.file;
transport.setRequestHeader("Content-type", options.file.type);
transport.setRequestHeader("X_FILE_NAME", options.file.name);
} else {
body = new FormData();
body.append('file', options.file);
if (options.parameters) {
for (var param in options.parameters) {
var value = options.parameters[param];
if (hui.isArray(value)) {
for (var i = 0; i < value.length; i++) {
body.append(param, value[i]);
}
} else {
body.append(param, value);
}
}
}
}
if (options.$progress) {
transport.upload.addEventListener("progress", function(e) {
options.$progress(e.loaded,e.total);
}, false);
}
if (options.$load) {
transport.upload.addEventListener("load", function(e) {
options.$load();
}, false);
}
} else if (method=='POST' && options.files) {
body = new FormData();
//form.append('path', '/');
for (var j = 0; j < options.files.length; j++) {
body.append('file'+j, options.files[j]);
}
} else if (options.parameters) {
body = hui.request._buildPostBody(options.parameters);
transport.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=utf-8");
} else {
body = '';
}
if (options.headers) {
for (var name in options.headers) {
transport.setRequestHeader(name, options.headers[name]);
}
}
transport.send(body);
//hui.request._transports.push(transport);
//hui.log('Add: '+hui.request._transports.length);
return transport;
};
/*
hui.request._transports = [];
hui.request._forget = function(t) {
hui.log('Forget: '+hui.request._transports.length);
hui.array.remove(hui.request._transports, t);
}
hui.request.abort = function() {
window.stop();
for (var i = hui.request._transports.length - 1; i >= 0; i--){
hui.log('aborting: '+hui.request._transports[i].readyState)
hui.request._transports[i].abort();
};
}*/
/**
* Check if a http request has a valid XML response
* @param {XMLHttpRequest} t The request
* @return true if a valid XML request exists
*/
hui.request.isXMLResponse = function(t) {
return t.responseXML && t.responseXML.documentElement && t.responseXML.documentElement.nodeName!='parsererror';
};
hui.request._buildPostBody = function(parameters) {
if (!parameters) return null;
var output = '',
param, i;
if (hui.isArray(parameters)) {
for (i = 0; i < parameters.length; i++) {
param = parameters[i];
if (i > 0) {output += '&';}
output+=encodeURIComponent(param.name)+'=';
if (param.value!==undefined && param.value!==null) {
output+=encodeURIComponent(param.value);
}
}
} else {
for (param in parameters) {
var value = parameters[param];
if (hui.isArray(value)) {
for (i = 0; i < value.length; i++) {
if (output.length > 0) {output += '&';}
output += encodeURIComponent(param)+'=';
if (value[i]!==undefined && value[i]!==null) {
output += encodeURIComponent(value[i]);
}
}
} else {
if (output.length > 0) {output += '&';}
output += encodeURIComponent(param)+'=';
if (value!==undefined && value!==null) {
output += encodeURIComponent(value);
}
}
}
}
return output;
};
///////////////////// Style ///////////////////
/** @namespace */
hui.style = {
/**
* Copy the style from one element to another
* @param source The element to copy from
* @param target The element to copy to
* @param styles An array of properties to copy
*/
copy : function(source,target,styles) {
for (var i=0; i < styles.length; i++) {
var property = styles[i];
var value = hui.style.get(source,property);
if (value) {
target.style[hui.string.camelize(property)] = value;
}
}
},
set : function(element,styles) {
for (var style in styles) {
if (style==='transform') {
element.style.webkitTransform = styles[style];
} else if (style==='opacity') {
hui.style.setOpacity(element,styles[style]);
} else {
element.style[style] = styles[style];
}
}
},
/**
* Get the computed style of an element
* @param {Element} element The element
* @param {String} style The CSS property in the form font-size NOT fontSize;
*/
get : function(element, style) {
element = hui.get(element);
var cameled = hui.string.camelize(style);
var value = element.style[cameled];
if (!value) {
if (document.defaultView && document.defaultView.getComputedStyle) {
var css = document.defaultView.getComputedStyle(element, null);
value = css ? css.getPropertyValue(style) : null;
} else if (element.currentStyle) {
value = element.currentStyle[cameled];
}
}
return value == 'auto' ? '' : value;
},
/** Cross browser way of setting opacity */
setOpacity : function(element,opacity) {
if (!hui.browser.opacity) {
if (opacity==1) {
element.style.filter = null;
} else {
element.style.filter = 'alpha(opacity='+(opacity*100)+')';
}
} else {
element.style.opacity = opacity;
}
},
length : function(value) {
if (typeof(value) === 'number') {
return value + 'px';
}
return value;
}
};
//////////////////// Frames ////////////////////
/** @namespace */
hui.frame = {
/**
* Get the document object of a frame
* @param frame The frame to get the document from
*/
getDocument : function(frame) {
if (frame.contentDocument) {
return frame.contentDocument;
} else if (frame.contentWindow) {
return frame.contentWindow.document;
} else if (frame.document) {
return frame.document;
}
},
/**
* Get the window object of a frame
* @param frame The frame to get the window from
*/
getWindow : function(frame) {
if (frame.defaultView) {
return frame.defaultView;
} else if (frame.contentWindow) {
return frame.contentWindow;
}
}
};
/////////////////// Selection /////////////////////
/** @namespace */
hui.selection = {
/** Clear the text selection */
clear : function() {
var sel ;
if(document.selection && document.selection.empty ){
document.selection.empty() ;
} else if(window.getSelection) {
sel=window.getSelection();
if(sel && sel.removeAllRanges) {
sel.removeAllRanges() ;
}
}
},
/** Get the selected text
* @param doc The document, defaults to current document
*/
getText : function(doc) {
doc = doc || document;
if (doc.getSelection) {
return doc.getSelection()+'';
} else if (doc.selection) {
return doc.selection.createRange().text;
}
return '';
},
getNode : function(doc) {
doc = doc || document;
if (doc.getSelection) {
var range = doc.getSelection().getRangeAt(0);
if (typeof(range.commonAncestorContainer) == 'function') {
return range.commonAncestorContainer(); // TODO Not sure why?
}
return range.commonAncestorContainer;
}
return null;
},
get : function(doc) {
return {
node : hui.selection.getNode(doc),
text : hui.selection.getText(doc)
};
},
enable : function(on) {
document.onselectstart = on ? null : function () { return false; };
document.body.style.webkitUserSelect = on ? null : 'none';
}
};
/////////////////// Effects //////////////////////
/** @namespace */
hui.effect = {
makeFlippable : function(options) {
if (hui.browser.webkit) {
hui.cls.add(options.container,'hui_flip_container');
hui.cls.add(options.front,'hui_flip_front');
hui.cls.add(options.back,'hui_flip_back');
} else {
hui.cls.add(options.front,'hui_flip_front_legacy');
hui.cls.add(options.back,'hui_flip_back_legacy');
}
},
flip : function(options) {
if (!hui.browser.webkit) {
hui.cls.toggle(options.element,'hui_flip_flipped_legacy');
} else {
var element = hui.get(options.element);
var duration = options.duration || '1s';
var front = hui.get.firstByClass(element,'hui_flip_front');
var back = hui.get.firstByClass(element,'hui_flip_back');
front.style.webkitTransitionDuration=duration;
back.style.webkitTransitionDuration=duration;
hui.cls.toggle(options.element,'hui_flip_flipped');
}
},
/**
* Reveal an element using a bounce/zoom effect
* @param {Object} options {element:«Element»}
*/
bounceIn : function(options) {
var node = options.element;
if (hui.browser.msie) {
hui.style.set(node,{'display':'block',visibility:'visible'});
} else {
hui.style.set(node,{'display':'block','opacity':0,visibility:'visible'});
hui.animate(node,'transform','scale(0.1)',0);// rotate(10deg)
window.setTimeout(function() {
hui.animate(node,'opacity',1,300);
hui.animate(node,'transform','scale(1)',400,{ease:hui.ease.backOut}); // rotate(0deg)
});
}
},
/**
* Fade an element in - making it visible
* @param {Object} options {element : «Element», duration : «milliseconds», delay : «milliseconds», $complete : «Function» }
*/
fadeIn : function(options) {
var node = options.element;
if (hui.style.get(node,'display')=='none') {
hui.style.set(node,{opacity : 0,display : 'inherit'});
}
hui.animate({
node : node,
css : { opacity : 1 },
delay : options.delay || null,
duration : options.duration || 500,
$complete : options.onComplete || options.$complete
});
},
/**
* Fade an element out - making it invisible
* @param {Object} options {element : «Element», duration : «milliseconds», delay : «milliseconds», $complete : «Function» }
*/
fadeOut : function(options) {
hui.animate({
node : options.element,
css : { opacity : 0 },
delay : options.delay || null,
duration : options.duration || 500,
hideOnComplete : true,
complete : options.onComplete || options.$complete
});
},
/**
* Make an element wiggle
* @param {Object} options {element : «Element», duration : «milliseconds» }
*/
wiggle : function(options) {
var e = hui.ui.getElement(options.element);
hui.cls.add(options.element,'hui_effect_wiggle');
window.setTimeout(function() {
hui.cls.remove(options.element,'hui_effect_wiggle');
},options.duration || 1000);
},
/**
* Make an element shake
* @param {Object} options {element : «Element», duration : «milliseconds» }
*/
shake : function(options) {
this._do(options.element,'hui_effect_shake',options.duration || 1000);
},
/**
* Make an element shake
* @param {Object} options {element : «Element», duration : «milliseconds» }
*/
tada : function(options) {
this._do(options.element,'hui_effect_tada',1000);
},
_do : function(e,cls,time) {
e = hui.ui.getElement(e);
hui.cls.add(e,cls);
window.setTimeout(function() {
hui.cls.remove(e,cls);
},time);
}
};
/////////////////////////////// Drag ///////////////////////////
/** @namespace */
hui.drag = {
/** Register dragging on an element
* <pre><strong>options:</strong> {
* element : «Element»
* <em>see hui.drag.start for more options</em>
* }
* @param {Object} options The options
* @param {Element} options.element The element to attach to
*/
attach : function(options) {
var touch = options.touch && hui.browser.touch;
hui.listen(options.element,touch ? 'touchstart' : 'mousedown',function(e) {
e = hui.event(e);
// TODO This shuould be a hui.Event
if (options.$check && options.$check(e)===false) {
return;
}
e.stop();
hui.drag.start(options,e);
});
},
register : function(options) {
this.attach(options);
},
/** Start dragging
* <pre><strong>options:</strong> {
* onBeforeMove ($firstMove) : function(event), // Called when the cursor moves for the first time
* onMove ($move): function(event), // Called when the cursor moves
* onAfterMove ($didMove): function(event), // Called if the cursor has moved
* onNotMoved ($notMoved): function(event), // Called if the cursor has not moved
* onEnd ($finally) : function(event), // Called when the mouse is released, even if the cursor has not moved
* }
* @param {Object} options The options
* @param {function} options.$before When the user starts interacting (mousedown/touchstart)
* @param {function} options.$startMove When the user starts moving (before first move - called once)
* @param {function} options.$move When the user moves
* @param {function} options.$endMove After a move has finished
* @param {function} options.$notMoved If the user started interacting but did not move (maybe treat as click/tap)
* @param {function} options.$finally After everything - moved or not
*/
start : function(options,e) {
var win = options.window || window;
var root = window.document.body.parentNode;
var target = hui.browser.msie ? win.document : win;
var touch = options.touch && hui.browser.touch;
if (options.$before) options.$before();
if (options.onStart) options.onStart();
var latest = {
x: e.getLeft(),
y: e.getTop(),
time: Date.now()
};
var initial = latest;
var mover,
upper,
moved = false;
mover = function(e) {
e = hui.event(e);
e.stop(e);
if (!moved) {
if (options.onBeforeMove) options.onBeforeMove(e); // TODO: deprecated
if (options.$startMove) options.$startMove(e);
}
moved = true;
if (options.onMove) options.onMove(e);
if (options.$move) options.$move(e);
}.bind(this);
hui.listen(root,touch ? 'touchmove' : 'mousemove',mover);
upper = function(e) {
hui.unListen(root,touch ? 'touchmove' : 'mousemove',mover);
hui.unListen(target,touch ? 'touchend' : 'mouseup',upper);
if (options.onEnd) options.onEnd(); // TODO: deprecated
if (moved) {
if (options.onAfterMove) options.onAfterMove(e); // TODO: deprecated
if (options.$endMove) options.$endMove(e);
} else {
if (options.onNotMoved) options.onNotMoved(e); // TODO: deprecated
if (options.$notMoved) options.$notMoved(e);
}
if (options.$finally) options.$finally();
hui.selection.enable(true);
}.bind(this);
hui.listen(target,touch ? 'touchend' : 'mouseup',upper);
hui.selection.enable(false);
},
_nativeListeners : [],
_activeDrop : null,
/** Listen for native drops
* <pre><strong>options:</strong> {
* elements : «Element»,
* hoverClass : «String»,
* $drop : function(event),
* $dropFiles : function(fileArray),
* $dropURL : function(url),
* $dropText : function(url)
* }
* @param {Object} options The options
*/
listen : function(options) {
if (hui.browser.msie) {
return;
}
hui.drag._nativeListeners.push(options);
if (hui.drag._nativeListeners.length>1) {
return;
}
hui.listen(document.body,'dragenter',function(e) {
var l = hui.drag._nativeListeners;
var found = null;
for (var i=0; i < l.length; i++) {
var lmnt = l[i].element;
if (hui.dom.isDescendantOrSelf(e.target,lmnt)) {
found = l[i];
if (hui.drag._activeDrop === null || hui.drag._activeDrop != found) {
hui.cls.add(lmnt,found.hoverClass);
}
break;
}
}
if (hui.drag._activeDrop) {
//var foundElement = found ? found.element : null;
if (hui.drag._activeDrop!=found) {
hui.cls.remove(hui.drag._activeDrop.element,hui.drag._activeDrop.hoverClass);
if (hui.drag._activeDrop.$leave) {
hui.drag._activeDrop.$leave(e);
}
} else if (hui.drag._activeDrop.$hover) {
hui.drag._activeDrop.$hover(e);
}
}
hui.drag._activeDrop = found;
});
hui.listen(document.body,'dragover',function(e) {
hui.stop(e);
if (hui.drag._activeDrop) {
if (hui.drag._activeDrop.$hover) {
hui.drag._activeDrop.$hover(e);
}
}
});
hui.listen(document.body,'dragend',function(e) {
hui.log('drag end');
});
hui.listen(document.body,'dragstart',function(e) {
hui.log('drag start');
});
hui.listen(document.body,'drop',function(e) {
var event = hui.event(e);
event.stop();
var options = hui.drag._activeDrop;
hui.drag._activeDrop = null;
if (options) {
hui.cls.remove(options.element,options.hoverClass);
if (options.$drop) {
options.$drop(e,{event:event});
}
if (e.dataTransfer) {
hui.log(e.dataTransfer.types);
if (options.$dropFiles && e.dataTransfer.files && e.dataTransfer.files.length>0) {
options.$dropFiles(e.dataTransfer.files,{event:event});
} else if (options.$dropURL && e.dataTransfer.types !== null && (hui.array.contains(e.dataTransfer.types,'public.url') || hui.array.contains(e.dataTransfer.types,'text/uri-list'))) {
var url = e.dataTransfer.getData('public.url');
var uriList = e.dataTransfer.getData('text/uri-list');
if (url && !hui.string.startsWith(url,'data:')) {
options.$dropURL(url,{event:event});
} else if (uriList && !hui.string.startsWith(url,'data:')) {
options.$dropURL(uriList,{event:event});
}
} else if (options.$dropText && e.dataTransfer.types !== null && hui.array.contains(e.dataTransfer.types,'text/plain')) {
options.$dropText(e.dataTransfer.getData('text/plain'),{event:event});
}
}
}
});
}
};
///////////////////////// Location /////////////////////
/** @namespace */
hui.location = {
/** Get an URL parameter */
getParameter : function(name) {
var parms = hui.location.getParameters();
for (var i=0; i < parms.length; i++) {
if (parms[i].name==name) {
return parms[i].value;
}
}
return null;
},
/** Set an URL parameter - initiates a new request */
setParameter : function(name,value) {
var parms = hui.location.getParameters();
var found = false;
for (var i=0; i < parms.length; i++) {
if (parms[i].name==name) {
parms[i].value=value;
found=true;
break;
}
}
if (!found) {
parms.push({name:name,value:value});
}
hui.location.setParameters(parms);
},
/** Checks if the URL has a certain hash */
hasHash : function(name) {
var h = document.location.hash;
if (h!=='') {
return h=='#'+name;
}
return false;
},
getHash : function() {
var h = document.location.hash;
if (h!=='') {
return h.substring(1);
}
return null;
},
/** Gets a hash parameter (#name=value&other=text) */
getHashParameter : function(name) {
var h = document.location.hash;
if (h!=='') {
var i = h.indexOf(name+'=');
if (i!==-1) {
var remaining = h.substring(i+name.length+1);
if (remaining.indexOf('&')!==-1) {
return remaining.substring(0,remaining.indexOf('&'));
}
return remaining;
}
}
return null;
},
/** Clears the URL hash */
clearHash : function() {
document.location.hash='#';
},
/** Sets a number of parameters
* @param params {Array} Parameters [{name:'hep',value:'hey'}]
*/
setParameters : function(parms) {
var query = '';
for (var i=0; i < parms.length; i++) {
query+= i === 0 ? '?' : '&';
query+=parms[i].name+'='+parms[i].value;
}
document.location.search=query;
},
/** Checks if a parameter exists with the value 'true' or 1 */
getBoolean : function(name) {
var value = hui.location.getParameter(name);
return (value=='true' || value=='1');
},
/** Checks if a parameter exists with the value 'true' or 1 */
getInt : function(name) {
var value = parseInt(hui.location.getParameter(name));
if (!isNaN(value)) {
return value;
}
return null;
},
/** Gets all parameters as an array like : [{name:'hep',value:'hey'}] */
getParameters : function() {
var items = document.location.search.substring(1).split('&');
var parsed = [];
for( var i = 0; i < items.length; i++) {
var item = items[i].split('=');
var name = unescape(item[0]).replace(/^\s*|\s*$/g,"");
var value = unescape(item[1]).replace(/^\s*|\s*$/g,"");
if (name) {
parsed.push({name:name,value:value});
}
}
return parsed;
}
};
/**
* Run through all postponed actions in "_" and remove it afterwards
*/
hui._onReady(function() {
hui._ready = true;
for (var i = 0; i < hui._.length; i++) {
var item = hui._[i];
if (typeof(item) === 'function') {
item = [item];
}
hui._runOrPostpone.apply(hui, item);
}
delete hui._;
});