Androtafl/assets/www/js/jq.mobi.js
Wanja ff1ffc407d * Got rid of jo framework
* Using jqMobi as a javascript framework
* Using jqMobi-Ui as ui framework
* Implemented basic minimax AI
* Implemented minimalistic player pairing server for node.js
2012-04-06 01:54:53 +02:00

1895 lines
No EOL
71 KiB
JavaScript

/**
* jqMobi is a query selector class for HTML5 mobile apps on a WebkitBrowser.
* Since most mobile devices (Android, iOS, webOS) use a WebKit browser, you only need to target one browser.
* We are able to increase the speed greatly by removing support for legacy desktop browsers and taking advantage of browser features, like native JSON parsing and querySelectorAll
* MIT License
* @author AppMobi
* @api private
*/
if (!window.jq || typeof (jq) !== "function") {
/**
* This is our master jq object that everything is built upon.
* $ is a pointer to this object
* @title jqMobi
* @api private
*/
var jq = (function(window) {
"use strict";
var undefined, document = window.document,
emptyArray = [],
slice = emptyArray.slice,
classCache = [],
eventHandlers = [],
_eventID = 1,
jsonPHandlers = [],
_jsonPID = 1,
fragementRE=/^\s*<(\w+)[^>]*>/,
_attrCache={};
/**
* Internal function to test if a class name fits in a regular expression
* @param {String} name to search against
* @return {Boolean}
* @api private
*/
function classRE(name) {
return name in classCache ? classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)'));
}
/**
* Internal function that returns a array of unique elements
* @param {Array} array to compare against
* @return {Array} array of unique elements
* @api private
*/
function unique(arr) {
for (var i = 0; i < arr.length; i++) {
if (arr.indexOf(arr[i]) != i) {
arr.splice(i, 1);
i--;
}
}
return arr;
}
/**
* Given a set of nodes, it returns them as an array. Used to find
* siblings of an element
* @param {Nodelist} Node list to search
* @param {Object} [element] to find siblings off of
* @return {Array} array of sibblings
* @api private
*/
function siblings(nodes, element) {
var elems = [];
if (nodes == undefined)
return elems;
for (; nodes; nodes = nodes.nextSibling) {
if (nodes.nodeType == 1 && nodes !== element) {
elems.push(nodes);
}
}
return elems;
}
/**
* This is the internal jqMobi object that gets extended and added on to it
* This is also the start of our query selector engine
* @param {String|Element|Object|Array} selector
* @param {String|Element|Object} [context]
*/
var $jqm = function(toSelect, what) {
this.length = 0;
if (!toSelect) {
return this;
} else if (toSelect instanceof $jqm && what == undefined) {
return toSelect;
} else if ($.isFunction(toSelect)) {
return $(document).ready(toSelect);
} else if ($.isArray(toSelect) && toSelect.length != undefined) { //Passing in an array or object
for (var i = 0; i < toSelect.length; i++)
this[this.length++] = toSelect[i];
return this;
} else if ($.isObject(toSelect) && $.isObject(what)) { //var tmp=$("span"); $("p").find(tmp);
if (toSelect.length == undefined) {
if (toSelect.parentNode == what)
this[this.length++] = toSelect;
} else {
for (var i = 0; i < toSelect.length; i++)
if (toSelect[i].parentNode == what)
this[this.length++] = toSelect[i];
}
return this;
} else if ($.isObject(toSelect) && what == undefined) { //Single object
this[this.length++] = toSelect;
return this;
} else if (what !== undefined) {
if (what instanceof $jqm) {
return what.find(toSelect);
}
} else {
what = document;
}
var dom = this.selector(toSelect, what);
if (!dom) {
return this;
}
//reverse the query selector all storage
else if ($.isArray(dom)) {
for (var j = 0; j < dom.length; j++) {
this[this.length++] = dom[j];
}
} else {
this[this.length++] = dom;
return this;
}
return this;
};
/**
* This calls the $jqm function
* @param {String|Element|Object|Array} selector
* @param {String|Element|Object} [context]
*/
var $ = function(selector, what) {
return new $jqm(selector, what);
};
/**
* this is the query selector library for elements
* @param {String} selector
* @param {String|Element|Object} [context]
* @api private
*/
function _selector(selector, what) {
var dom;
try {
selector=selector.trim();
if (selector[0] === "#" && selector.indexOf(" ") === -1 && selector.indexOf(">") === -1) {
if (what == document)
dom = what.getElementById(selector.replace("#", ""));
else
dom = [].slice.call(what.querySelectorAll(selector));
} else if (selector[0] === "<" && selector[selector.length - 1] === ">") //html
{
var tmp = document.createElement("div");
tmp.innerHTML = selector.trim();
dom = [].slice.call(tmp.childNodes);
} else {
dom = [].slice.call(what.querySelectorAll(selector));
}
} catch (e) {
}
return dom;
}
/**
* Map takes in elements and executes a callback function on each and returns a collection
```
$.map([1,2],function(ind){return ind+1});
```
* @param {Array|Object} elements
* @param {Function} callback
* @return {Object} jqMobi object with elements in it
* @title $.map(elements,callback)
*/
$.map = function(elements, callback) {
var value, values = [],
i, key;
if ($.isArray(elements))
for (i = 0; i < elements.length; i++) {
value = callback(elements[i], i);
if (value !== undefined)
values.push(value);
}
else if ($.isObject(elements))
for (key in elements) {
if (!elements.hasOwnProperty(key))
continue;
value = callback(elements[key], key);
if (value !== undefined)
values.push(value);
}
return $([values]);
};
/**
* Iterates through elements and executes a callback. Returns if false
```
$.each([1,2],function(ind){console.log(ind);});
```
* @param {Array|Object} elements
* @param {Function} callback
* @return {Array} elements
* @title $.each(elements,callback)
*/
$.each = function(elements, callback) {
var i, key;
if ($.isArray(elements))
for (i = 0; i < elements.length; i++) {
if (callback(i, elements[i]) === false)
return elements;
}
else if ($.isObject(elements))
for (key in elements) {
if (!elements.hasOwnProperty(key))
continue;
if (callback(key, elements[key]) === false)
return elements;
}
return elements;
};
/**
* Extends an object with additional arguments
```
$.extend({foo:'bar'});
$.extend(element,{foo:'bar'});
```
* @param {Object} [target] element
* @param any number of additional arguments
* @return {Object} [target]
* @title $.extend(target,{params})
*/
$.extend = function(target) {
if (target == undefined)
target = this;
if (arguments.length === 1) {
for (var key in target)
this[key] = target[key];
return this;
} else {
slice.call(arguments, 1).forEach(function(source) {
for (var key in source)
target[key] = source[key];
});
}
return target;
};
/**
* Checks to see if the parameter is an array
```
var arr=[];
$.isArray(arr);
```
* @param {Object} element
* @return {Boolean}
* @example $.isArray([1]);
* @title $.isArray(param)
*/
$.isArray = function(obj) {
return obj instanceof Array && obj['push'] != undefined; //ios 3.1.3 doesn't have Array.isArray
};
/**
* Checks to see if the parameter is a function
```
var func=function(){};
$.isFunction(func);
```
* @param {Object} element
* @return {Boolean}
* @title $.isFunction(param)
*/
$.isFunction = function(obj) {
return typeof obj === "function";
};
/**
* Checks to see if the parameter is a object
```
var foo={bar:'bar'};
$.isObject(foo);
```
* @param {Object} element
* @return {Boolean}
* @title $.isObject(param)
*/
$.isObject = function(obj) {
return typeof obj === "object";
}
/**
* Prototype for jqm object. Also extens $.fn
*/
$.fn = $jqm.prototype = {
constructor: $jqm,
forEach: emptyArray.forEach,
reduce: emptyArray.reduce,
push: emptyArray.push,
indexOf: emptyArray.indexOf,
concat: emptyArray.concat,
selector: _selector,
oldElement: undefined,
slice: emptyArray.slice,
/**
* This is a utility function for .end()
* @param {Object} params
* @return {Object} a jqMobi with params.oldElement set to this
* @api private
*/
setupOld: function(params) {
if (params == undefined)
return $();
params.oldElement = this;
return params;
},
/**
* This is a wrapper to $.map on the selected elements
```
$().map(function(){this.value+=ind});
```
* @param {Function} callback
* @return {Object} a jqMobi object
* @title $().map(function)
*/
map: function(fn) {
return $.map(this, function(el, i) {
return fn.call(el, i, el);
});
},
/**
* Iterates through all elements and applys a callback function
```
$().each(function(){console.log(this.value)});
```
* @param {Function} callback
* @return {Object} a jqMobi object
* @title $().each(function)
*/
each: function(callback) {
this.forEach(function(el, idx) {
callback.call(el, idx, el);
});
return this;
},
/**
* This is executed when DOMContentLoaded happens, or after if you've registered for it.
```
$(document).ready(function(){console.log('I'm ready');});
```
* @param {Function} callback
* @return {Object} a jqMobi object
* @title $().ready(function)
*/
ready: function(callback) {
if (document.readyState === "complete" || document.readyState === "loaded")
callback();
document.addEventListener("DOMContentLoaded", callback, false);
return this;
},
/**
* Searches through the collection and reduces them to elements that match the selector
```
$("#foo").find('.bar');
$("#foo").find($('.bar'));
$("#foo").find($('.bar').get());
```
* @param {String|Object|Array} selector
* @return {Object} a jqMobi object filtered
* @title $().find(selector)
*/
find: function(sel) {
if (this.length === 0)
return undefined;
var elems = [];
var tmpElems;
for (var i = 0; i < this.length; i++) {
tmpElems = ($(sel, this[i]));
for (var j = 0; j < tmpElems.length; j++) {
elems.push(tmpElems[j]);
}
}
return $(unique(elems));
},
/**
* Gets or sets the innerHTML for the collection.
* If used as a get, the first elements innerHTML is returned
```
$("#foo").html(); //gets the first elements html
$("#foo").html('new html');//sets the html
```
* @param {String} html to set
* @return {Object} a jqMobi object
* @title $().html([html])
*/
html: function(html) {
if (this.length === 0)
return undefined;
if (html === undefined)
return this[0].innerHTML;
for (var i = 0; i < this.length; i++) {
this[i].innerHTML = html;
}
return this;
},
/**
* Gets or sets the innerText for the collection.
* If used as a get, the first elements innerText is returned
```
$("#foo").text(); //gets the first elements text;
$("#foo").text('new text'); //sets the text
```
* @param {String} text to set
* @return {Object} a jqMobi object
* @title $().text([text])
*/
text: function(text) {
if (this.length === 0)
return undefined;
if (text === undefined)
return this[0].textContent;
for (var i = 0; i < this.length; i++) {
this[i].textContent = text;
}
return this;
},
/**
* Gets or sets a css property for the collection
* If used as a get, the first elements css property is returned
```
$().css("background"); // Gets the first elements background
$().css("background","red") //Sets the elements background to red
```
* @param {String} attribute to get
* @param {String} value to set as
* @return {Object} a jqMobi object
* @title $().css(attribute,[value])
*/
css: function(attribute, value, obj) {
var toAct = obj != undefined ? obj : this[0];
if (this.length === 0)
return undefined;
if (value == undefined && typeof (attribute) === "string") {
var styles = window.getComputedStyle(toAct);
return toAct.style[attribute] ? toAct.style[attribute]: window.getComputedStyle(toAct)[attribute] ;
}
for (var i = 0; i < this.length; i++) {
if ($.isObject(attribute)) {
for (var j in attribute) {
this[i].style[j] = attribute[j];
}
} else {
this[i].style[attribute] = value;
}
}
return this;
},
/**
* Sets the innerHTML of all elements to an empty string
```
$().empty();
```
* @return {Object} a jqMobi object
* @title $().empty()
*/
empty: function() {
for (var i = 0; i < this.length; i++) {
this[i].innerHTML = '';
}
return this;
},
/**
* Sets the elements display property to "none".
* This will also store the old property into an attribute for hide
```
$().hide();
```
* @return {Object} a jqMobi object
* @title $().hide()
*/
hide: function() {
if (this.length === 0)
return this;
for (var i = 0; i < this.length; i++) {
if (this.css("display", null, this[i]) != "none") {
this[i].setAttribute("jqmOldStyle", this.css("display", null, this[i]));
this[i].style.display = "none";
}
}
return this;
},
/**
* Shows all the elements by setting the css display property
* We look to see if we were retaining an old style (like table-cell) and restore that, otherwise we set it to block
```
$().show();
```
* @return {Object} a jqMobi object
* @title $().show()
*/
show: function() {
if (this.length === 0)
return this;
for (var i = 0; i < this.length; i++) {
if (this.css("display", null, this[i]) == "none") {
this[i].style.display = this[i].getAttribute("jqmOldStyle") ? this[i].getAttribute("jqmOldStyle") : 'block';
this[i].removeAttribute("jqmOldStyle");
}
}
return this;
},
/**
* Toggle the visibility of a div
```
$().toggle();
$().toggle(true); //force showing
```
* @param {Boolean} [show] -force the hiding or showing of the element
* @return {Object} a jqMobi object
* @title $().toggle([show])
*/
toggle: function(show) {
var show2 = show === true ? true : false;
for (var i = 0; i < this.length; i++) {
if (window.getComputedStyle(this[i])['display'] !== "none" || (show !== undefined && show2 === false)) {
this[i].setAttribute("jqmOldStyle", this[i].style.display)
this[i].style.display = "none";
} else {
this[i].style.display = this[i].getAttribute("jqmOldStyle") != undefined ? this[i].getAttribute("jqmOldStyle") : 'block';
this[i].removeAttribute("jqmOldStyle");
}
}
return this;
},
/**
* Gets or sets an elements value
* If used as a getter, we return the first elements value. If nothing is in the collection, we return undefined
```
$().value; //Gets the first elements value;
$().value="bar"; //Sets all elements value to bar
```
* @param {String} [value] to set
* @return {String|Object} A string as a getter, jqMobi object as a setter
* @title $().val([value])
*/
val: function(value) {
if (this.length === 0)
return undefined;
if (value == undefined)
return this[0].value;
for (var i = 0; i < this.length; i++) {
this[i].value = value;
}
return this;
},
/**
* Gets or sets an attribute on an element
* If used as a getter, we return the first elements value. If nothing is in the collection, we return undefined
```
$().attr("foo"); //Gets the first elements 'foo' attribute
$().attr("foo","bar");//Sets the elements 'foo' attribute to 'bar'
$().attr("foo",{bar:'bar'}) //Adds the object to an internal cache
```
* @param {String|Object} attribute to act upon. If it's an object (hashmap), it will set the attributes based off the kvp.
* @param {String|Array|Object|function} [value] to set
* @return {String|Object|Array|Function} If used as a getter, return the attribute value. If a setter, return a jqMobi object
* @title $().attr(attribute,[value])
*/
attr: function(attr, value) {
if (this.length === 0)
return undefined;
if (value === undefined && !$.isObject(attr)) {
var val = (this[0].jqmCacheId&&_attrCache[this[0].jqmCacheId][attr])?(this[0].jqmCacheId&&_attrCache[this[0].jqmCacheId][attr]):this[0].getAttribute(attr);
return val;
}
for (var i = 0; i < this.length; i++) {
if ($.isObject(attr)) {
for (var key in attr) {
$(this[i]).attr(key,attr[key]);
}
}
else if($.isArray(value)||$.isObject(value)||$.isFunction(value))
{
if(!this[i].jqmCacheId)
this[i].jqmCacheId=$.uuid();
if(!_attrCache[this[i].jqmCacheId])
_attrCache[this[i].jqmCacheId]={}
_attrCache[this[i].jqmCacheId][attr]=value;
}
else if (value == null && value !== undefined)
{
this[i].removeAttribute(attr);
if(this[i].jqmCacheId&&_attrCache[this[i].jqmCacheId][attr])
delete _attrCache[this[i].jqmCacheId][attr];
}
else{
this[i].setAttribute(attr, value);
}
}
return this;
},
/**
* Removes an attribute on the elements
```
$().removeAttr("foo");
```
* @param {String} attributes that can be space delimited
* @return {Object} jqMobi object
* @title $().removeAttr(attribute)
*/
removeAttr: function(attr) {
var that = this;
for (var i = 0; i < this.length; i++) {
attr.split(/\s+/g).forEach(function(param) {
that[i].removeAttribute(param);
if(that[i].jqmCacheId&&_attrCache[that[i].jqmCacheId][attr])
delete _attrCache[that[i].jqmCacheId][attr];
});
}
return this;
},
/**
* Removes elements based off a selector
```
$().remove(".foo");//Remove off a string selector
var element=$("#foo").get();
$().remove(element); //Remove by an element
$().remove($(".foo")); //Remove by a collection
```
* @param {String|Object|Array} selector to filter against
* @return {Object} jqMobi object
* @title $().remove(selector)
*/
remove: function(selector) {
var elems = $(this).filter(selector);
if (elems == undefined)
return this;
for (var i = 0; i < elems.length; i++) {
elems[i].parentNode.removeChild(elems[i]);
}
return this;
},
/**
* Adds a css class to elements.
```
$().addClass("selected");
```
* @param {String} classes that are space delimited
* @return {Object} jqMobi object
* @title $().addClass(name)
*/
addClass: function(name) {
for (var i = 0; i < this.length; i++) {
var cls = this[i].className;
var classList = [];
var that = this;
name.split(/\s+/g).forEach(function(cname) {
if (!that.hasClass(cname, that[i]))
classList.push(cname);
});
this[i].className += (cls ? " " : "") + classList.join(" ");
this[i].className = this[i].className.trim();
}
return this;
},
/**
* Removes a css class from elements.
```
$().removeClass("foo"); //single class
$().removeClass("foo selected");//remove multiple classess
```
* @param {String} classes that are space delimited
* @return {Object} jqMobi object
* @title $().removeClass(name)
*/
removeClass: function(name) {
for (var i = 0; i < this.length; i++) {
if (name == undefined) {
this[i].className = '';
return this;
}
var classList = this[i].className;
name.split(/\s+/g).forEach(function(cname) {
classList = classList.replace(classRE(cname), " ");
});
if (classList.length > 0)
this[i].className = classList.trim();
else
this[i].className = "";
}
return this;
},
/**
* Checks to see if an element has a class.
```
$().hasClass('foo');
$().hasClass('foo',element);
```
* @param {String} class name to check against
* @param {Object} [element] to check against
* @return {Boolean}
* @title $().hasClass(name,[element])
*/
hasClass: function(name, element) {
if (this.length === 0)
return false;
if (!element)
element = this[0];
return classRE(name).test(element.className);
},
/**
* Appends to the elements
* We boil everything down to a jqMobi object and then loop through that.
* If it's HTML, we create a dom element so we do not break event bindings.
* if it's a script tag, we evaluate it.
```
$().append("<div></div>"); //Creates the object from the string and appends it
$().append($("#foo")); //Append an object;
```
* @param {String|Object} Element/string to add
* @param {Boolean} [insert] insert or append
* @return {Object} jqMobi object
* @title $().append(element,[insert])
*/
append: function(element, insert) {
if (element && element.length != undefined && element.length === 0)
return this;
if ($.isArray(element) || $.isObject(element))
element = $(element);
var i;
for (i = 0; i < this.length; i++) {
if (element.length && typeof element != "string") {
element = $(element);
for (var j = 0; j < element.length; j++)
insert != undefined ? this[i].insertBefore(element[j], this[i].firstChild) : this[i].appendChild(element[j]);
} else {
var obj =fragementRE.test(element)?$(element):undefined;
if (obj == undefined || obj.length == 0) {
obj = document.createTextNode(element);
}
if (obj.nodeName != undefined && obj.nodeName.toLowerCase() == "script" && (!obj.type || obj.type.toLowerCase() === 'text/javascript')) {
window.eval(obj.innerHTML);
} else if(obj instanceof $jqm) {
for(var k=0;k<obj.length;k++)
{
insert != undefined ? this[i].insertBefore(obj[k], this[i].firstChild) : this[i].appendChild(obj[k]);
}
}
else {
insert != undefined ? this[i].insertBefore(obj, this[i].firstChild) : this[i].appendChild(obj);
}
}
}
return this;
},
/**
* Prepends to the elements
* This simply calls append and sets insert to true
```
$().prepend("<div></div>");//Creates the object from the string and appends it
$().prepend($("#foo")); //Prepends an object
```
* @param {String|Object} Element/string to add
* @return {Object} jqMobi object
* @title $().prepend(element)
*/
prepend: function(element) {
return this.append(element, 1);
},
/**
* Inserts collection before the target (adjacent)
```
$().insertBefore(jq("#target"));
```
* @param {String|Object} Target
* @title $().insertBefore(target);
*/
insertBefore: function(target, after) {
if (this.length == 0)
return this;
target = $(target).get(0);
if (!target || target.length == 0)
return this;
for (var i = 0; i < this.length; i++)
{
after ? target.parentNode.insertBefore(this[i], target.nextSibling) : target.parentNode.insertBefore(this[i], target);
}
return this;
},
/**
* Inserts collection after the target (adjacent)
```
$().insertAfter(jq("#target"));
```
* @param {String|Object} target
* @title $().insertAfter(target);
*/
insertAfter: function(target) {
this.insertBefore(target, true);
},
/**
* Returns the raw DOM element.
```
$().get(); //returns the first element
$().get(2);// returns the third element
```
* @param {Int} [index]
* @return {Object} raw DOM element
* @title $().get([index])
*/
get: function(index) {
index = index == undefined ? 0 : index;
if (index < 0)
index += this.length;
return (this[index]) ? this[index] : undefined;
},
/**
* Returns the offset of the element, including traversing up the tree
```
$().offset();
```
* @return {Object} with left, top, width and height properties
* @title $().offset()
*/
offset: function() {
if (this.length === 0)
return undefined;
var obj = this[0].getBoundingClientRect();
return {
left: obj.left + window.pageXOffset,
top: obj.top + window.pageYOffset,
width: parseInt(this[0].style.width),
height: parseInt(this[0].style.height)
};
},
/**
* Returns the parent nodes of the elements based off the selector
```
$("#foo").parent('.bar');
$("#foo").parent($('.bar'));
$("#foo").parent($('.bar').get());
```
* @param {String|Array|Object} [selector]
* @return {Object} jqMobi object with unique parents
* @title $().parent(selector)
*/
parent: function(selector) {
if (this.length == 0)
return undefined;
var elems = [];
for (var i = 0; i < this.length; i++) {
if (this[i].parentNode)
elems.push(this[i].parentNode);
}
return this.setupOld($(unique(elems)).filter(selector));
},
/**
* Returns the child nodes of the elements based off the selector
```
$("#foo").children('.bar'); //Selector
$("#foo").children($('.bar')); //Objects
$("#foo").children($('.bar').get()); //Single element
```
* @param {String|Array|Object} [selector]
* @return {Object} jqMobi object with unique children
* @title $().children(selector)
*/
children: function(selector) {
if (this.length == 0)
return undefined;
var elems = [];
for (var i = 0; i < this.length; i++) {
elems = elems.concat(siblings(this[i].firstChild));
}
return this.setupOld($((elems)).filter(selector));
},
/**
* Returns the siblings of the element based off the selector
```
$("#foo").siblings('.bar'); //Selector
$("#foo").siblings($('.bar')); //Objects
$("#foo").siblings($('.bar').get()); //Single element
```
* @param {String|Array|Object} [selector]
* @return {Object} jqMobi object with unique siblings
* @title $().siblings(selector)
*/
siblings: function(selector) {
if (this.length == 0)
return undefined;
var elems = [];
for (var i = 0; i < this.length; i++) {
if (this[i].parentNode)
elems = elems.concat(siblings(this[i].parentNode.firstChild, this[i]));
}
return this.setupOld($(elems).filter(selector));
},
/**
* Returns the closest element based off the selector and optional context
```
$("#foo").closest('.bar'); //Selector
$("#foo").closest($('.bar')); //Objects
$("#foo").closest($('.bar').get()); //Single element
```
* @param {String|Array|Object} selector
* @param {Object} [context]
* @return {Object} Returns a jqMobi object with the closest element based off the selector
* @title $().closest(selector,[context]);
*/
closest: function(selector, context) {
if (this.length == 0)
return undefined;
var elems = [],
cur = this[0];
var start = $(selector, context);
if (start.length == 0)
return $();
while (cur && start.indexOf(cur) == -1) {
cur = cur !== context && cur !== document && cur.parentNode;
}
return $(cur);
},
/**
* Filters elements based off the selector
```
$("#foo").filter('.bar'); //Selector
$("#foo").filter($('.bar')); //Objects
$("#foo").filter($('.bar').get()); //Single element
```
* @param {String|Array|Object} selector
* @return {Object} Returns a jqMobi object after the filter was run
* @title $().filter(selector);
*/
filter: function(selector) {
if (this.length == 0)
return undefined;
if (selector == undefined)
return this;
var elems = [];
for (var i = 0; i < this.length; i++) {
var val = this[i];
if (val.parentNode && $(selector, val.parentNode).indexOf(val) >= 0)
elems.push(val);
}
return this.setupOld($(unique(elems)));
},
/**
* Basically the reverse of filter. Return all elements that do NOT match the selector
```
$("#foo").not('.bar'); //Selector
$("#foo").not($('.bar')); //Objects
$("#foo").not($('.bar').get()); //Single element
```
* @param {String|Array|Object} selector
* @return {Object} Returns a jqMobi object after the filter was run
* @title $().not(selector);
*/
not: function(selector) {
if (this.length == 0)
return undefined;
var elems = [];
for (var i = 0; i < this.length; i++) {
var val = this[i];
if (val.parentNode && $(selector, val.parentNode).indexOf(val) == -1)
elems.push(val);
}
return this.setupOld($(unique(elems)));
},
/**
* Gets or set data-* attribute parameters on elements
* When used as a getter, it's only the first element
```
$().data("foo"); //Gets the data-foo attribute for the first element
$().data("foo","bar"); //Sets the data-foo attribute for all elements
$().data("foo",{bar:'bar'});//object as the data
```
* @param {String} key
* @param {String|Array|Object} value
* @return {String|Object} returns the value or jqMobi object
* @title $().data(key,[value]);
*/
data: function(key, value) {
return this.attr('data-' + key, value);
},
/**
* Rolls back the jqMobi elements when filters were applied
* This can be used after .not(), .filter(), .children(), .parent()
```
$().filter(".panel").end(); //This will return the collection BEFORE filter is applied
```
* @return {Object} returns the previous jqMobi object before filter was applied
* @title $().end();
*/
end: function() {
return this.oldElement != undefined ? this.oldElement : $();
},
/**
* Clones the nodes in the collection.
```
$().clone();// Deep clone of all elements
$().clone(false); //Shallow clone
```
* @param {Boolean} [deep] - do a deep copy or not
* @return {Object} jqMobi object of cloned nodes
* @title $().clone();
*/
clone: function(deep) {
deep = deep === false ? false : true;
if (this.length == 0)
return undefined;
var elems = [];
for (var i = 0; i < this.length; i++) {
elems.push(this[i].cloneNode(deep));
}
return $(elems);
},
/**
* Returns the number of elements in the collection
```
$().size();
```
* @return {Int}
* @title $().size();
*/
size: function() {
return this.length;
},
/**
* Serailizes a form into a query string
```
$().serialize(grouping);
```
* @param {String} [grouping] - optional grouping to the fields -e.g users[name]
* @return {String}
* @title $().serialize(grouping)
*/
serialize: function(grouping) {
if (this.length == 0)
return "";
var params = {};
for (var i = 0; i < this.length; i++)
{
this.slice.call(this[i].elements).forEach(function(elem) {
var type = elem.getAttribute("type");
if (elem.nodeName.toLowerCase() != "fieldset" && !elem.disabled && type != "submit"
&& type != "reset" && type != "button" && ((type != "radio" && type != "checkbox") || elem.checked))
params[elem.getAttribute("name")] = elem.value;
});
}
return $.param(params,grouping);
}
};
/* AJAX functions */
function empty() {
}
var ajaxSettings = {
type: 'GET',
beforeSend: empty,
success: empty,
error: empty,
complete: empty,
context: undefined,
timeout: 0,
crossDomain:false
};
/**
* Execute a jsonP call, allowing cross domain scripting
* options.url - URL to call
* options.success - Success function to call
* options.error - Error function to call
```
$.jsonP({url:'mysite.php?callback=?&foo=bar',success:function(){},error:function(){}});
```
* @param {Object} options
* @title $.jsonP(options)
*/
$.jsonP = function(options) {
var callbackName = 'jsonp_callback' + (++_jsonPID);
var abortTimeout = "",
context;
var script = document.createElement("script");
var abort = function() {
$(script).remove();
if (window[callbackName])
window[callbackName] = empty;
};
window[callbackName] = function(data) {
clearTimeout(abortTimeout);
$(script).remove();
delete window[callbackName];
options.success.call(context, data);
};
script.src = options.url.replace(/=\?/, '=' + callbackName);
if(options.error)
{
script.onerror=function(){
clearTimeout(abortTimeout);
options.error.call(context, "", 'error');
}
}
$('head').append(script);
if (options.timeout > 0)
abortTimeout = setTimeout(function() {
options.error.call(context, "", 'timeout');
}, options.timeout);
return {};
};
/**
* Execute an Ajax call with the given options
* options.type - Type of request
* options.beforeSend - function to execute before sending the request
* options.success - success callback
* options.error - error callback
* options.complete - complete callback - callled with a success or error
* options.timeout - timeout to wait for the request
* options.url - URL to make request against
* options.contentType - HTTP Request Content Type
* options.headers - Object of headers to set
* options.dataType - Data type of request
* options.data - data to pass into request. $.param is called on objects
```
var opts={
type:"GET",
success:function(data){},
url:"mypage.php",
data:{bar:'bar'},
}
$.ajax(opts);
```
* @param {Object} options
* @title $.ajax(options)
*/
$.ajax = function(opts) {
var xhr;
try {
xhr = new window.XMLHttpRequest();
var settings = opts || {};
for (var key in ajaxSettings) {
if (!settings[key])
settings[key] = ajaxSettings[key];
}
if (!settings.url)
settings.url = window.location;
if (!settings.contentType)
settings.contentType = "application/x-www-form-urlencoded";
if (!settings.headers)
settings.headers = {};
if (!settings.dataType)
settings.dataType = "text/html";
else {
switch (settings.dataType) {
case "script":
settings.dataType = 'text/javascript, application/javascript';
break;
case "json":
settings.dataType = 'application/json';
break;
case "xml":
settings.dataType = 'application/xml, text/xml';
break;
case "html":
settings.dataType = 'text/html';
break;
case "text":
settings.dataType = 'text/plain';
break;
default:
settings.dataType = "text/html";
break;
case "jsonp":
return $.jsonP(opts);
break;
}
}
if ($.isObject(settings.data))
settings.data = $.param(settings.data);
if (settings.type.toLowerCase() === "get" && settings.data) {
if (settings.url.indexOf("?") === -1)
settings.url += "?" + settings.data;
else
settings.url += "&" + settings.data;
}
if (/=\?/.test(settings.url)) {
return $.jsonP(settings);
}
if (!settings.crossDomain) settings.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(settings.url) &&
RegExp.$2 != window.location.host;
if(!settings.crossDomain)
settings.headers = $.extend({'X-Requested-With': 'XMLHttpRequest'}, settings.headers);
var abortTimeout;
var context = settings.context;
var protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol;
xhr.onreadystatechange = function() {
var mime = settings.dataType;
if (xhr.readyState === 4) {
clearTimeout(abortTimeout);
var result, error = false;
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 0&&protocol=='file:') {
if (mime === 'application/json' && !(/^\s*$/.test(xhr.responseText))) {
try {
result = JSON.parse(xhr.responseText);
} catch (e) {
error = e;
}
} else
result = xhr.responseText;
//If we're looking at a local file, we assume that no response sent back means there was an error
if(xhr.status===0&&result.length===0)
error=true;
if (error)
settings.error.call(context, xhr, 'parsererror', error);
else {
settings.success.call(context, result, 'success', xhr);
}
} else {
error = true;
settings.error.call(context, xhr, 'error');
}
settings.complete.call(context, xhr, error ? 'error' : 'success');
}
};
xhr.open(settings.type, settings.url, true);
if (settings.contentType)
settings.headers['Content-Type'] = settings.contentType;
for (var name in settings.headers)
xhr.setRequestHeader(name, settings.headers[name]);
if (settings.beforeSend.call(context, xhr, settings) === false) {
xhr.abort();
return false;
}
if (settings.timeout > 0)
abortTimeout = setTimeout(function() {
xhr.onreadystatechange = empty;
xhr.abort();
settings.error.call(context, xhr, 'timeout');
}, settings.timeout);
xhr.send(settings.data);
} catch (e) {
console.log(e);
}
return xhr;
};
/**
* Shorthand call to an Ajax GET request
```
$.get("mypage.php?foo=bar",function(data){});
```
* @param {String} url to hit
* @param {Function} success
* @title $.get(url,success)
*/
$.get = function(url, success) {
return this.ajax({
url: url,
success: success
});
};
/**
* Shorthand call to an Ajax POST request
```
$.post("mypage.php",{bar:'bar'},function(data){});
```
* @param {String} url to hit
* @param {Object} [data] to pass in
* @param {Function} success
* @param {String} [dataType]
* @title $.post(url,[data],success,[dataType])
*/
$.post = function(url, data, success, dataType) {
if (typeof (data) === "function") {
success = data;
data = {};
}
if (dataType === undefined)
dataType = "html";
return this.ajax({
url: url,
type: "POST",
data: data,
dataType: dataType,
success: success
});
};
/**
* Shorthand call to an Ajax request that expects a JSON response
```
$.getJSON("mypage.php",{bar:'bar'},function(data){});
```
* @param {String} url to hit
* @param {Object} [data]
* @param {Function} [success]
* @title $.getJSON(url,data,success)
*/
$.getJSON = function(url, data, success) {
if (typeof (data) === "function") {
success = data;
data = {};
}
return this.ajax({
url: url,
data: data,
success: success,
dataType: "json"
});
};
/**
* Converts an object into a key/value par with an optional prefix. Used for converting objects to a query string
```
var obj={
foo:'foo',
bar:'bar'
}
var kvp=$.param(obj,'data');
```
* @param {Object} object
* @param {String} [prefix]
* @return {String} Key/value pair representation
* @title $.param(object,[prefix];
*/
$.param = function(obj, prefix) {
var str = [];
if (obj instanceof $jqm) {
obj.each(function() {
var k = prefix ? prefix + "[]" : this.id,
v = this.value;
str.push((k) + "=" + encodeURIComponent(v));
});
} else {
for (var p in obj) {
var k = prefix ? prefix + "[" + p + "]" : p,
v = obj[p];
str.push($.isObject(v) ? $.param(v, k) : (k) + "=" + encodeURIComponent(v));
}
}
return str.join("&");
};
/**
* Used for backwards compatibility. Uses native JSON.parse function
```
var obj=$.parseJSON("{\"bar\":\"bar\"}");
```
* @params {String} string
* @return {Object}
* @title $.parseJSON(string)
*/
$.parseJSON = function(string) {
return JSON.parse(string);
};
/**
* Helper function to convert XML into the DOM node representation
```
var xmlDoc=$.parseXML("<xml><foo>bar</foo></xml>");
```
* @param {String} string
* @return {Object} DOM nodes
* @title $.parseXML(string)
*/
$.parseXML = function(string) {
return (new DOMParser).parseFromString(string, "text/xml");
};
/**
* Helper function to parse the user agent. Sets the following
* .os.webkit
* .os.android
* .os.ipad
* .os.iphone
* .os.webos
* .os.touchpad
* .os.blackberry
* .os.opera
* .os.fennec
* @api private
*/
function detectUA($, userAgent) {
$.os = {};
$.os.webkit = userAgent.match(/WebKit\/([\d.]+)/) ? true : false;
$.os.android = userAgent.match(/(Android)\s+([\d.]+)/) || userAgent.match(/Silk-Accelerated/) ? true : false;
$.os.ipad = userAgent.match(/(iPad).*OS\s([\d_]+)/) ? true : false;
$.os.iphone = !$.os.ipad && userAgent.match(/(iPhone\sOS)\s([\d_]+)/) ? true : false;
$.os.webos = userAgent.match(/(webOS|hpwOS)[\s\/]([\d.]+)/) ? true : false;
$.os.touchpad = $.os.webos && userAgent.match(/TouchPad/) ? true : false;
$.os.ios = $.os.ipad || $.os.iphone;
$.os.blackberry = userAgent.match(/BlackBerry/) || userAgent.match(/PlayBook/) ? true : false;
$.os.opera = userAgent.match(/Opera Mobi/) ? true : false;
$.os.fennec = userAgent.match(/fennec/i) ? true : false;
$.os.desktop = !($.os.ios || $.os.android || $.os.blackberry || $.os.opera || $.os.fennec);
}
detectUA($, navigator.userAgent);
$.__detectUA = detectUA; //needed for unit tests
if (typeof String.prototype.trim !== 'function') {
/**
* Helper function for iOS 3.1.3
*/
String.prototype.trim = function() {
this.replace(/(\r\n|\n|\r)/gm, "").replace(/^\s+|\s+$/, '');
return this
};
}
/**
* Utility function to create a psuedo GUID
```
var id= $.uuid();
```
* @title $.uuid
*/
$.uuid = function () {
var S4 = function () {
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
}
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
};
/**
Zepto.js events
@api private
*/
//The following is modified from Zepto.js / events.js
//We've removed depricated jQuery events like .live and allow anonymous functions to be removed
var $$ = $.qsa,
handlers = {},
_jqmid = 1,
specialEvents = {};
/**
* Gets or sets the expando property on a javascript element
* Also increments the internal counter for elements;
* @param {Object} element
* @return {Int} jqmid
* @api private
*/
function jqmid(element) {
return element._jqmid || (element._jqmid = _jqmid++);
}
/**
* Searches through a local array that keeps track of event handlers for proxying.
* Since we listen for multiple events, we match up the event, function and selector.
* This is used to find, execute, remove proxied event functions
* @param {Object} element
* @param {String} [event]
* @param {Function} [function]
* @param {String|Object|Array} [selector]
* @return {Function|null} handler function or false if not found
* @api private
*/
function findHandlers(element, event, fn, selector) {
event = parse(event);
if (event.ns)
var matcher = matcherFor(event.ns);
return (handlers[jqmid(element)] || []).filter(function(handler) {
return handler && (!event.e || handler.e == event.e) && (!event.ns || matcher.test(handler.ns)) && (!fn || handler.fn == fn || (typeof handler.fn === 'function' && typeof fn === 'function' && "" + handler.fn === "" + fn)) && (!selector || handler.sel == selector);
});
}
/**
* Splits an event name by "." to look for namespaces (e.g touch.click)
* @param {String} event
* @return {Object} an object with the event name and namespace
* @api private
*/
function parse(event) {
var parts = ('' + event).split('.');
return {
e: parts[0],
ns: parts.slice(1).sort().join(' ')
};
}
/**
* Regular expression checker for event namespace checking
* @param {String} namespace
* @return {Regex} regular expression
* @api private
*/
function matcherFor(ns) {
return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)');
}
/**
* Utility function that will loop through events that can be a hash or space delimited and executes the function
* @param {String|Object} events
* @param {Function} fn
* @param {Iterator} [iterator]
* @api private
*/
function eachEvent(events, fn, iterator) {
if ($.isObject(events))
$.each(events, iterator);
else
events.split(/\s/).forEach(function(type) {
iterator(type, fn)
});
}
/**
* Helper function for adding an event and creating the proxy handler function.
* All event handlers call this to wire event listeners up. We create proxy handlers so they can be removed then.
* This is needed for delegate/on
* @param {Object} element
* @param {String|Object} events
* @param {Function} function that will be executed when event triggers
* @param {String|Array|Object} [selector]
* @param {Boolean} [getDelegate]
* @api private
*/
function add(element, events, fn, selector, getDelegate) {
var id = jqmid(element),
set = (handlers[id] || (handlers[id] = []));
eachEvent(events, fn, function(event, fn) {
var delegate = getDelegate && getDelegate(fn, event),
callback = delegate || fn;
var proxyfn = function(event) {
var result = callback.apply(element, [event].concat(event.data));
if (result === false)
event.preventDefault();
return result;
};
var handler = $.extend(parse(event), {
fn: fn,
proxy: proxyfn,
sel: selector,
del: delegate,
i: set.length
});
set.push(handler);
element.addEventListener(handler.e, proxyfn, false);
});
}
/**
* Helper function to remove event listeners. We look through each event and then the proxy handler array to see if it exists
* If found, we remove the listener and the entry from the proxy array. If no function is specified, we remove all listeners that match
* @param {Object} element
* @param {String|Object} events
* @param {Function} [fn]
* @param {String|Array|Object} [selector]
* @api private
*/
function remove(element, events, fn, selector) {
var id = jqmid(element);
eachEvent(events || '', fn, function(event, fn) {
findHandlers(element, event, fn, selector).forEach(function(handler) {
delete handlers[id][handler.i];
element.removeEventListener(handler.e, handler.proxy, false);
});
});
}
$.event = {
add: add,
remove: remove
}
/**
* Binds an event to each element in the collection and executes the callback
```
$().bind('click',function(){console.log('I clicked '+this.id);});
```
* @param {String|Object} event
* @param {Function} callback
* @return {Object} jqMobi object
* @title $().bind(event,callback)
*/
$.fn.bind = function(event, callback) {
for (var i = 0; i < this.length; i++) {
add(this[i], event, callback);
}
return this;
};
/**
* Unbinds an event to each element in the collection. If a callback is passed in, we remove just that one, otherwise we remove all callbacks for those events
```
$().unbind('click'); //Unbinds all click events
$().unbind('click',myFunc); //Unbinds myFunc
```
* @param {String|Object} event
* @param {Function} [callback]
* @return {Object} jqMobi object
* @title $().unbind(event,[callback]);
*/
$.fn.unbind = function(event, callback) {
for (var i = 0; i < this.length; i++) {
remove(this[i], event, callback);
}
return this;
};
/**
* Binds an event to each element in the collection that will only execute once. When it executes, we remove the event listener then right away so it no longer happens
```
$().one('click',function(){console.log('I was clicked once');});
```
* @param {String|Object} event
* @param {Function} [callback]
* @return jqMobi object
* @title $().one(event,callback);
*/
$.fn.one = function(event, callback) {
return this.each(function(i, element) {
add(this, event, callback, null, function(fn, type) {
return function() {
var result = fn.apply(element, arguments);
remove(element, type, fn);
return result;
}
});
});
};
/**
* internal variables
* @api private
*/
var returnTrue = function() {
return true
},
returnFalse = function() {
return false
},
eventMethods = {
preventDefault: 'isDefaultPrevented',
stopImmediatePropagation: 'isImmediatePropagationStopped',
stopPropagation: 'isPropagationStopped'
};
/**
* Creates a proxy function for event handlers
* @param {String} event
* @return {Function} proxy
* @api private
*/
function createProxy(event) {
var proxy = $.extend({
originalEvent: event
}, event);
$.each(eventMethods, function(name, predicate) {
proxy[name] = function() {
this[predicate] = returnTrue;
return event[name].apply(event, arguments);
};
proxy[predicate] = returnFalse;
})
return proxy;
}
/**
* Delegate an event based off the selector. The event will be registered at the parent level, but executes on the selector.
```
$("#div").delegate("p",'click',callback);
```
* @param {String|Array|Object} selector
* @param {String|Object} event
* @param {Function} callback
* @return {Object} jqMobi object
* @title $().delegate(selector,event,callback)
*/
$.fn.delegate = function(selector, event, callback) {
for (var i = 0; i < this.length; i++) {
var element = this[i];
add(element, event, callback, selector, function(fn) {
return function(e) {
var evt, match = $(e.target).closest(selector, element).get(0);
if (match) {
evt = $.extend(createProxy(e), {
currentTarget: match,
liveFired: element
});
return fn.apply(match, [evt].concat([].slice.call(arguments, 1)));
}
}
});
}
return this;
};
/**
* Unbinds events that were registered through delegate. It acts upon the selector and event. If a callback is specified, it will remove that one, otherwise it removes all of them.
```
$("#div").undelegate("p",'click',callback);//Undelegates callback for the click event
$("#div").undelegate("p",'click');//Undelegates all click events
```
* @param {String|Array|Object} selector
* @param {String|Object} event
* @param {Function} callback
* @return {Object} jqMobi object
* @title $().undelegate(selector,event,[callback]);
*/
$.fn.undelegate = function(selector, event, callback) {
for (var i = 0; i < this.length; i++) {
remove(this[i], event, callback, selector);
}
return this;
}
/**
* Similar to delegate, but the function parameter order is easier to understand.
* If selector is undefined or a function, we just call .bind, otherwise we use .delegate
```
$("#div").on("click","p",callback);
```
* @param {String|Array|Object} selector
* @param {String|Object} event
* @param {Function} callback
* @return {Object} jqMobi object
* @title $().on(event,selector,callback);
*/
$.fn.on = function(event, selector, callback) {
return selector === undefined || $.isFunction(selector) ? this.bind(event, selector) : this.delegate(selector, event, callback);
};
/**
* Removes event listeners for .on()
* If selector is undefined or a function, we call unbind, otherwise it's undelegate
```
$().off("click","p",callback); //Remove callback function for click events
$().off("click","p") //Remove all click events
```
* @param {String|Object} event
* @param {String|Array|Object} selector
* @param {Sunction} callback
* @return {Object} jqMobi object
* @title $().off(event,selector,[callback])
*/
$.fn.off = function(event, selector, callback) {
return selector === undefined || $.isFunction(selector) ? this.unbind(event, selector) : this.undelegate(selector, event, callback);
};
/**
This triggers an event to be dispatched. Usefull for emulating events, etc.
```
$().trigger("click",{foo:'bar'});//Trigger the click event and pass in data
```
* @param {String|Object} event
* @param {Object} [data]
* @return {Object} jqMobi object
* @title $().trigger(event,data);
*/
$.fn.trigger = function(event, data) {
if (typeof event == 'string')
event = $.Event(event);
event.data = data;
for (var i = 0; i < this.length; i++) {
this[i].dispatchEvent(event)
}
return this;
};
/**
* Creates a custom event to be used internally.
* @param {String} type
* @param {Object} [properties]
* @return {event} a custom event that can then be dispatched
* @title $.Event(type,props);
*/
$.Event = function(type, props) {
var event = document.createEvent(specialEvents[type] || 'Events'),
bubbles = true;
if (props)
for (var name in props)
(name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name]);
event.initEvent(type, bubbles, true, null, null, null, null, null, null, null, null, null, null, null, null);
return event;
};
/**
* Creates a proxy function so you can change the 'this' context in the function
```
var newObj={foo:bar}
$("#main").bind("click",$.proxy(function(evt){console.log(this)},newObj);
```
* @param {Function} Callback
* @param {Object} Context
* @title $.proxy(callback,context);
*/
$.proxy=function(fnc,ctx){
var tmp=function(evt){
return fnc.call(ctx,evt);
}
return tmp;
}
/**
* End of APIS
* @api private
*/
//End zepto/events.js
return $;
})(window);
'$' in window || (window.$ = jq);
//Helper function used in jq.mobi.plugins.
if (!window.numOnly) {
window.numOnly = function numOnly(val) {
if (isNaN(parseFloat(val)))
val = val.replace(/[^0-9.-]/, "");
return parseFloat(val);
}
}
}