diff --git a/.gitignore b/.gitignore
index 3a8d5388..db3cf947 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,4 @@
.tmp*
-dist
*.swp
.DS_store
*node_modules*
diff --git a/bower.json b/bower.json
new file mode 100644
index 00000000..391a6cff
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,7 @@
+{
+ "name" : "jquerypp",
+ "version": "1.0.1",
+ "main": [
+ "./dist/jquerypp.js"
+ ]
+}
\ No newline at end of file
diff --git a/dist/dom/animate.js b/dist/dom/animate.js
new file mode 100644
index 00000000..0f4e9f97
--- /dev/null
+++ b/dist/dom/animate.js
@@ -0,0 +1,325 @@
+(function ($) {
+
+ // Overwrites `jQuery.fn.animate` to use CSS 3 animations if possible
+
+ var
+ // The global animation counter
+ animationNum = 0,
+ // The stylesheet for our animations
+ styleSheet = null,
+ // The animation cache
+ cache = [],
+ // Stores the browser properties like transition end event name and prefix
+ browser = null,
+ // Store the original $.fn.animate
+ oldanimate = $.fn.animate,
+
+ // Return the stylesheet, create it if it doesn't exists
+ getStyleSheet = function () {
+ if(!styleSheet) {
+ var style = document.createElement('style');
+ style.setAttribute("type", "text/css");
+ style.setAttribute("media", "screen");
+
+ document.getElementsByTagName('head')[0].appendChild(style);
+ if (!window.createPopup) { /* For Safari */
+ style.appendChild(document.createTextNode(''));
+ }
+
+ styleSheet = style.sheet;
+ }
+
+ return styleSheet;
+ },
+
+ //removes an animation rule from a sheet
+ removeAnimation = function (sheet, name) {
+ for (var j = sheet.cssRules.length - 1; j >= 0; j--) {
+ var rule = sheet.cssRules[j];
+ // 7 means the keyframe rule
+ if (rule.type === 7 && rule.name == name) {
+ sheet.deleteRule(j)
+ return;
+ }
+ }
+ },
+
+ // Returns whether the animation should be passed to the original $.fn.animate.
+ passThrough = function (props, ops) {
+ var nonElement = !(this[0] && this[0].nodeType),
+ isInline = !nonElement && $(this).css("display") === "inline" && $(this).css("float") === "none";
+
+ for (var name in props) {
+ // jQuery does something with these values
+ if (props[name] == 'show' || props[name] == 'hide' || props[name] == 'toggle'
+ // Arrays for individual easing
+ || $.isArray(props[name])
+ // Negative values not handled the same
+ || props[name] < 0
+ // unit-less value
+ || name == 'zIndex' || name == 'z-index' || name == 'scrollTop' || name == 'scrollLeft'
+ ) {
+ return true;
+ }
+ }
+
+ return props.jquery === true || getBrowser() === null ||
+ // Animating empty properties
+ $.isEmptyObject(props) ||
+ // We can't do custom easing
+ (ops && ops.length == 4) || (ops && typeof ops[2] == 'string') ||
+ // Second parameter is an object - we can only handle primitives
+ $.isPlainObject(ops) ||
+ // Inline and non elements
+ isInline || nonElement;
+ },
+
+ // Gets a CSS number (with px added as the default unit if the value is a number)
+ cssValue = function(origName, value) {
+ if (typeof value === "number" && !$.cssNumber[ origName ]) {
+ return value += "px";
+ }
+ return value;
+ },
+
+ // Feature detection borrowed by http://modernizr.com/
+ getBrowser = function(){
+ if(!browser) {
+ var t,
+ el = document.createElement('fakeelement'),
+ transitions = {
+ 'transition': {
+ transitionEnd : 'transitionEnd',
+ prefix : ''
+ },
+// 'OTransition': {
+// transitionEnd : 'oTransitionEnd',
+// prefix : '-o-'
+// },
+// 'MSTransition': {
+// transitionEnd : 'msTransitionEnd',
+// prefix : '-ms-'
+// },
+ 'MozTransition': {
+ transitionEnd : 'animationend',
+ prefix : '-moz-'
+ },
+ 'WebkitTransition': {
+ transitionEnd : 'webkitAnimationEnd',
+ prefix : '-webkit-'
+ }
+ }
+
+ for(t in transitions){
+ if( el.style[t] !== undefined ){
+ browser = transitions[t];
+ }
+ }
+ }
+ return browser;
+ },
+
+ // Properties that Firefox can't animate if set to 'auto':
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=571344
+ // Provides a converter that returns the actual value
+ ffProps = {
+ top : function(el) {
+ return el.position().top;
+ },
+ left : function(el) {
+ return el.position().left;
+ },
+ width : function(el) {
+ return el.width();
+ },
+ height : function(el) {
+ return el.height();
+ },
+ fontSize : function(el) {
+ return '1em';
+ }
+ },
+
+ // Add browser specific prefix
+ addPrefix = function(properties) {
+ var result = {};
+ $.each(properties, function(name, value) {
+ result[getBrowser().prefix + name] = value;
+ });
+ return result;
+ },
+
+ // Returns the animation name for a given style. It either uses a cached
+ // version or adds it to the stylesheet, removing the oldest style if the
+ // cache has reached a certain size.
+ getAnimation = function(style) {
+ var sheet, name, last;
+
+ // Look up the cached style, set it to that name and reset age if found
+ // increment the age for any other animation
+ $.each(cache, function(i, animation) {
+ if(style === animation.style) {
+ name = animation.name;
+ animation.age = 0;
+ } else {
+ animation.age += 1;
+ }
+ });
+
+ if(!name) { // Add a new style
+ sheet = getStyleSheet();
+ name = "jquerypp_animation_" + (animationNum++);
+ // get the last sheet and insert this rule into it
+ sheet.insertRule("@" + getBrowser().prefix + "keyframes " + name + ' ' + style,
+ (sheet.cssRules && sheet.cssRules.length) || 0);
+ cache.push({
+ name : name,
+ style : style,
+ age : 0
+ });
+
+ // Sort the cache by age
+ cache.sort(function(first, second) {
+ return first.age - second.age;
+ });
+
+ // Remove the last (oldest) item from the cache if it has more than 20 items
+ if(cache.length > 20) {
+ last = cache.pop();
+ removeAnimation(sheet, last.name);
+ }
+ }
+
+ return name;
+ };
+
+ /**
+ * @function $.fn.animate
+ * @parent $.animate
+ *
+ * Animate CSS properties using native CSS animations, if possible.
+ * Uses the original [$.fn.animate()](http://api.$.com/animate/) otherwise.
+ *
+ * @param {Object} props The CSS properties to animate
+ * @param {Integer|String|Object} [speed=400] The animation duration in ms.
+ * Will use $.fn.animate if a string or object is passed
+ * @param {Function} [callback] A callback to execute once the animation is complete
+ * @return {jQuery} The jQuery element
+ */
+ $.fn.animate = function (props, speed, easing, callback) {
+ //default to normal animations if browser doesn't support them
+ if (passThrough.apply(this, arguments)) {
+ return oldanimate.apply(this, arguments);
+ }
+
+ var optall = $.speed(speed, easing, callback);
+
+ // Add everything to the animation queue
+ this.queue(optall.queue, function(done) {
+ var
+ //current CSS values
+ current,
+ // The list of properties passed
+ properties = [],
+ to = "",
+ prop,
+ self = $(this),
+ duration = optall.duration,
+ //the animation keyframe name
+ animationName,
+ // The key used to store the animation hook
+ dataKey,
+ //the text for the keyframe
+ style = "{ from {",
+ // The animation end event handler.
+ // Will be called both on animation end and after calling .stop()
+ animationEnd = function (currentCSS, exec) {
+ self.css(currentCSS);
+
+ self.css(addPrefix({
+ "animation-duration" : "",
+ "animation-name" : "",
+ "animation-fill-mode" : "",
+ "animation-play-state" : ""
+ }));
+
+ // Call the original callback
+ if ($.isFunction(optall.old) && exec) {
+ // Call success, pass the DOM element as the this reference
+ optall.old.call(self[0], true)
+ }
+
+ $.removeData(self, dataKey, true);
+ },
+ finishAnimation = function() {
+ // Call animationEnd using the passed properties
+ animationEnd(props, true);
+ done();
+ };
+
+ for(prop in props) {
+ properties.push(prop);
+ }
+
+ if(getBrowser().prefix === '-moz-') {
+ // Normalize 'auto' properties in FF
+ $.each(properties, function(i, prop) {
+ var converter = ffProps[$.camelCase(prop)];
+ if(converter && self.css(prop) == 'auto') {
+ self.css(prop, converter(self));
+ }
+ });
+ }
+
+ // Use $.styles
+ current = self.styles.apply(self, properties);
+ $.each(properties, function(i, cur) {
+ // Convert a camelcased property name
+ var name = cur.replace(/([A-Z]|^ms)/g, "-$1" ).toLowerCase();
+ style += name + " : " + cssValue(cur, current[cur]) + "; ";
+ to += name + " : " + cssValue(cur, props[cur]) + "; ";
+ });
+
+ style += "} to {" + to + " }}";
+
+ animationName = getAnimation(style);
+ dataKey = animationName + '.run';
+
+ // Add a hook which will be called when the animation stops
+ $._data(this, dataKey, {
+ stop : function(gotoEnd) {
+ // Pause the animation
+ self.css(addPrefix({
+ 'animation-play-state' : 'paused'
+ }));
+ // Unbind the animation end handler
+ self.off(getBrowser().transitionEnd, finishAnimation);
+ if(!gotoEnd) {
+ // We were told not to finish the animation
+ // Call animationEnd but set the CSS to the current computed style
+ animationEnd(self.styles.apply(self, properties), false);
+ } else {
+ // Finish animaion
+ animationEnd(props, true);
+ }
+ }
+ });
+
+ // set this element to point to that animation
+ self.css(addPrefix({
+ "animation-duration" : duration + "ms",
+ "animation-name" : animationName,
+ "animation-fill-mode": "forwards"
+ }));
+
+ // Attach the transition end event handler to run only once
+ self.one(getBrowser().transitionEnd, finishAnimation);
+
+ });
+
+ return this;
+ };
+
+ return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/dom/compare.js b/dist/dom/compare.js
new file mode 100644
index 00000000..03107e51
--- /dev/null
+++ b/dist/dom/compare.js
@@ -0,0 +1,76 @@
+(function($) {
+
+/**
+ * @function jQuery.fn.compare
+ * @parent jQuery.compare
+ *
+ * Compare two elements and return a bitmask as a number representing the following conditions:
+ *
+ * - `000000` -> __0__: Elements are identical
+ * - `000001` -> __1__: The nodes are in different documents (or one is outside of a document)
+ * - `000010` -> __2__: #bar precedes #foo
+ * - `000100` -> __4__: #foo precedes #bar
+ * - `001000` -> __8__: #bar contains #foo
+ * - `010000` -> __16__: #foo contains #bar
+ *
+ * You can check for any of these conditions using a bitwise AND:
+ *
+ * if( $('#foo').compare($('#bar')) & 2 ) {
+ * console.log("#bar precedes #foo")
+ * }
+ *
+ * @param {HTMLElement|jQuery} element an element or jQuery collection to compare against.
+ * @return {Number} A number representing a bitmask deatiling how the elements are positioned from each other.
+ */
+
+// See http://ejohn.org/blog/comparing-document-position/
+$.fn.compare = function(element){ //usually
+ try{
+ // Firefox 3 throws an error with XUL - we can't use compare then
+ element = element.jquery ? element[0] : element;
+ }catch(e){
+ return null;
+ }
+
+ // make sure we aren't coming from XUL element
+ if (window.HTMLElement) {
+ var s = HTMLElement.prototype.toString.call(element)
+ if (s == '[xpconnect wrapped native prototype]' || s == '[object XULElement]' || s === '[object Window]') {
+ return null;
+ }
+ }
+
+ if(this[0].compareDocumentPosition){
+ // For browsers that support it, use compareDocumentPosition
+ // https://developer.mozilla.org/en/DOM/Node.compareDocumentPosition
+ return this[0].compareDocumentPosition(element);
+ }
+
+ // this[0] contains element
+ if(this[0] == document && element != document) return 8;
+
+ var number =
+ // this[0] contains element
+ (this[0] !== element && this[0].contains(element) && 16) +
+ // element contains this[0]
+ (this[0] != element && element.contains(this[0]) && 8),
+ docEl = document.documentElement;
+
+ // Use the sourceIndex
+ if(this[0].sourceIndex){
+ // this[0] precedes element
+ number += (this[0].sourceIndex < element.sourceIndex && 4)
+ // element precedes foo[0]
+ number += (this[0].sourceIndex > element.sourceIndex && 2)
+ // The nodes are in different documents
+ number += (this[0].ownerDocument !== element.ownerDocument ||
+ (this[0] != docEl && this[0].sourceIndex <= 0 ) ||
+ (element != docEl && element.sourceIndex <= 0 )) && 1
+ }
+
+ return number;
+}
+
+return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/dom/cookie.js b/dist/dom/cookie.js
new file mode 100644
index 00000000..0e0541b1
--- /dev/null
+++ b/dist/dom/cookie.js
@@ -0,0 +1,115 @@
+(function($) {
+ /**
+ * @function jQuery.cookie
+ * @parent jquerypp
+ * @plugin jquery/dom/cookie
+ * @author Klaus Hartl/klaus.hartl@stilbuero.de
+ *
+ * `jQuery.cookie(name, [value], [options])` lets you create, read and remove cookies. It is the
+ * [jQuery cookie plugin](https://github.com/carhartl/jquery-cookie) written by [Klaus Hartl](stilbuero.de)
+ * and dual licensed under the [MIT](http://www.opensource.org/licenses/mit-license.php)
+ * and [GPL](http://www.gnu.org/licenses/gpl.html) licenses.
+ *
+ * ## Examples
+ *
+ * Set the value of a cookie.
+ *
+ * $.cookie('the_cookie', 'the_value');
+ *
+ * Create a cookie with all available options.
+ *
+ * $.cookie('the_cookie', 'the_value', {
+ * expires: 7,
+ * path: '/',
+ * domain: 'jquery.com',
+ * secure: true
+ * });
+ *
+ * Create a session cookie.
+ *
+ * $.cookie('the_cookie', 'the_value');
+ *
+ * Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
+ * used when the cookie was set.
+ *
+ * $.cookie('the_cookie', null);
+ *
+ * Get the value of a cookie.
+ *
+ * $.cookie('the_cookie');
+ *
+ * @param {String} [name] The name of the cookie.
+ * @param {String} [value] The value of the cookie.
+ * @param {Object} [options] An object literal containing key/value pairs to provide optional cookie attributes. Values can be:
+ *
+ * - `expires` - Either an integer specifying the expiration date from now on in days or a Date object. If a negative value is specified (e.g. a date in the past), the cookie will be deleted. If set to null or omitted, the cookie will be a session cookie and will not be retained when the the browser exits.
+ * - `domain` - The domain name
+ * - `path` - The value of the path atribute of the cookie (default: path of page that created the cookie).
+ * - `secure` - If true, the secure attribute of the cookie will be set and the cookie transmission will require a secure protocol (like HTTPS).
+ *
+ * @return {String} the value of the cookie or {undefined} when setting the cookie.
+ */
+ $.cookie = function(name, value, options) {
+ if (typeof value != 'undefined') {
+ // name and value given, set cookie
+ options = options ||
+ {};
+ if (value === null) {
+ value = '';
+ options.expires = -1;
+ }
+ // convert value to JSON string
+ if (typeof value == 'object' && $.toJSON) {
+ value = $.toJSON(value);
+ }
+ var expires = '';
+ // Set expiry
+ if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
+ var date;
+ if (typeof options.expires == 'number') {
+ date = new Date();
+ date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
+ }
+ else {
+ date = options.expires;
+ }
+ expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
+ }
+ // CAUTION: Needed to parenthesize options.path and options.domain
+ // in the following expressions, otherwise they evaluate to undefined
+ // in the packed version for some reason...
+ var path = options.path ? '; path=' + (options.path) : '';
+ var domain = options.domain ? '; domain=' + (options.domain) : '';
+ var secure = options.secure ? '; secure' : '';
+ // Set the cookie name=value;expires=;path=;domain=;secure-
+ document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
+ }
+ else { // only name given, get cookie
+ var cookieValue = null;
+ if (document.cookie && document.cookie != '') {
+ var cookies = document.cookie.split(';');
+ for (var i = 0; i < cookies.length; i++) {
+ var cookie = $.trim(cookies[i]);
+ // Does this cookie string begin with the name we want?
+ if (cookie.substring(0, name.length + 1) == (name + '=')) {
+ // Get the cookie value
+ cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
+ break;
+ }
+ }
+ }
+ // Parse JSON from the cookie into an object
+ if ($.evalJSON && cookieValue && cookieValue.match(/^\s*\{/)) {
+ try {
+ cookieValue = $.evalJSON(cookieValue);
+ }
+ catch (e) {
+ }
+ }
+ return cookieValue;
+ }
+ };
+
+ return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/dom/dimensions.js b/dist/dom/dimensions.js
new file mode 100644
index 00000000..9eeb99c7
--- /dev/null
+++ b/dist/dom/dimensions.js
@@ -0,0 +1,192 @@
+(function($) {
+
+var
+ //margin is inside border
+ weird = /button|select/i,
+ getBoxes = {},
+ checks = {
+ width: ["Left", "Right"],
+ height: ['Top', 'Bottom'],
+ oldOuterHeight: $.fn.outerHeight,
+ oldOuterWidth: $.fn.outerWidth,
+ oldInnerWidth: $.fn.innerWidth,
+ oldInnerHeight: $.fn.innerHeight
+ },
+ supportsSetter = $.fn.jquery >= '1.8.0';
+
+$.each({
+
+/**
+ * @function jQuery.fn.outerWidth
+ * @parent jQuery.dimensions
+ *
+ * `jQuery.fn.outerWidth([value], [includeMargins])` lets you set
+ * the outer width of an object where:
+ *
+ * outerWidth = width + padding + border + (margin)
+ *
+ * And can be used like:
+ *
+ * $("#foo").outerWidth(100); //sets outer width
+ * $("#foo").outerWidth(100, true); // uses margins
+ * $("#foo").outerWidth(); //returns outer width
+ * $("#foo").outerWidth(true); //returns outer width + margins
+ *
+ * When setting the outerWidth, it adjusts the width of the element.
+ * If *includeMargin* is set to `true` margins will also be included.
+ * It is also possible to animate the outer width:
+ *
+ * $('#foo').animate({ outerWidth: 200 });
+ *
+ * @param {Number} [width] The width to set
+ * @param {Boolean} [includeMargin=false] Makes setting the outerWidth adjust
+ * for margins.
+ * @return {jQuery|Number} Returns the outer width or the jQuery wrapped elements
+ * if you are setting the outer width.
+ */
+width:
+/**
+ * @function jQuery.fn.innerWidth
+ * @parent jQuery.dimensions
+ *
+ * `jQuery.fn.innerWidth([value])` lets you set the inner width of an element where
+ *
+ * innerWidth = width + padding
+ *
+ * Use it like:
+ *
+ * $("#foo").innerWidth(100); //sets inner width
+ * $("#foo").outerWidth(); // returns inner width
+ *
+ * Or in an animation like:
+ *
+ * $('#foo').animate({ innerWidth : 200 });
+ *
+ * Setting inner width adjusts the width of the element.
+ *
+ * @param {Number} [width] The inner width to set
+ * @return {jQuery|Number} Returns the inner width or the jQuery wrapped elements
+ * if you are setting the inner width.
+ */
+"Width",
+/**
+ * @function jQuery.fn.outerHeight
+ * @parent jQuery.dimensions
+ *
+ * `jQuery.fn.outerHeight([value], [includeMargins])` lets
+ * you set the outer height of an object where:
+ *
+ * outerHeight = height + padding + border + (margin)
+ *
+ * And can be used like:
+ *
+ * $("#foo").outerHeight(100); //sets outer height
+ * $("#foo").outerHeight(100, true); // uses margins
+ * $("#foo").outerHeight(); //returns outer height
+ * $("#foo").outerHeight(true); //returns outer height + margins
+ *
+ * When setting the outerHeight, it adjusts the height of the element.
+ * If *includeMargin* is set to `true` margins will also be included.
+ * It is also possible to animate the outer heihgt:
+ *
+ * $('#foo').animate({ outerHeight : 200 });
+ *
+ * @param {Number} [height] The height to set
+ * @param {Boolean} [includeMargin=false] Makes setting the outerHeight adjust
+ * for margins.
+ * @return {jQuery|Number} Returns the outer height or the jQuery wrapped elements
+ * if you are setting the outer height.
+ */
+height:
+/**
+ * @function jQuery.fn.innerHeight
+ * @parent jQuery.dimensions
+ *
+ * `jQuery.fn.innerHeight([value])` lets you set the inner height of an element where
+ *
+ * innerHeight = height + padding
+ *
+ * Use it like:
+ *
+ * $("#foo").innerHeight(100); //sets inner height
+ * $("#foo").outerHeight(); // returns inner height
+ *
+ * Or in an animation like:
+ *
+ * $('#foo').animate({ innerHeight : 200 });
+ *
+ * Setting inner height adjusts the height of the element.
+ *
+ * @param {Number} [height] The inner height to set
+ * @return {jQuery|Number} Returns the inner height or the jQuery wrapped elements
+ * if you are setting the inner height.
+ */
+// for each 'height' and 'width'
+"Height" }, function(lower, Upper) {
+
+ //used to get the padding and border for an element in a given direction
+ getBoxes[lower] = function(el, boxes) {
+ var val = 0;
+ if (!weird.test(el.nodeName)) {
+ //make what to check for ....
+ var myChecks = [];
+ $.each(checks[lower], function() {
+ var direction = this;
+ $.each(boxes, function(name, val) {
+ if (val)
+ myChecks.push(name + direction+ (name == 'border' ? "Width" : "") );
+ })
+ })
+ $.each($.styles(el, myChecks), function(name, value) {
+ val += (parseFloat(value) || 0);
+ })
+ }
+ return val;
+ }
+
+ //getter / setter
+ if(!supportsSetter) {
+ $.fn["outer" + Upper] = function(v, margin) {
+ var first = this[0];
+ if (typeof v == 'number') {
+ // Setting the value
+ first && this[lower](v - getBoxes[lower](first, {padding: true, border: true, margin: margin}))
+ return this;
+ } else {
+ // Return the old value
+ return first ? checks["oldOuter" + Upper].apply(this, arguments) : null;
+ }
+ }
+ $.fn["inner" + Upper] = function(v) {
+ var first = this[0];
+ if (typeof v == 'number') {
+ // Setting the value
+ first&& this[lower](v - getBoxes[lower](first, { padding: true }))
+ return this;
+ } else {
+ // Return the old value
+ return first ? checks["oldInner" + Upper].apply(this, arguments) : null;
+ }
+ }
+ }
+
+ //provides animations
+ var animate = function(boxes){
+ // Return the animation function
+ return function(fx){
+ if (fx[supportsSetter ? 'pos' : 'state'] == 0) {
+ fx.start = $(fx.elem)[lower]();
+ fx.end = fx.end - getBoxes[lower](fx.elem,boxes);
+ }
+ fx.elem.style[lower] = (fx.pos * (fx.end - fx.start) + fx.start) + "px"
+ }
+ }
+ $.fx.step["outer" + Upper] = animate({padding: true, border: true})
+ $.fx.step["outer" + Upper+"Margin"] = animate({padding: true, border: true, margin: true})
+ $.fx.step["inner" + Upper] = animate({padding: true})
+
+})
+
+return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/dom/form_params.js b/dist/dom/form_params.js
new file mode 100644
index 00000000..0006c258
--- /dev/null
+++ b/dist/dom/form_params.js
@@ -0,0 +1,186 @@
+(function( $ ) {
+ var
+ converters = {},
+ // use to parse bracket notation like my[name][attribute]
+ keyBreaker = /[^\[\]]+/g,
+ // converts values that look like numbers and booleans and removes empty strings
+ convertValue = function( value ) {
+ if ( $.isNumeric( value )) {
+ return parseFloat( value );
+ } else if ( value === 'true') {
+ return true;
+ } else if ( value === 'false' ) {
+ return false;
+ } else if ( value === '' || value === null ) {
+ return undefined;
+ }
+ return value;
+ },
+ // Access nested data
+ nestData = function( elem, type, data, parts, value, seen, fullName ) {
+ var name = parts.shift();
+ // Keep track of the dot separated fullname. Used to uniquely track seen values
+ // and if they should be converted to an array or not
+ fullName = fullName ? fullName + '.' + name : name;
+
+ if (parts.length ) {
+ if ( ! data[ name ] ) {
+ data[ name ] = {};
+ }
+
+ // Recursive call
+ nestData( elem, type, data[ name ], parts, value, seen, fullName);
+ } else {
+
+ // Handle same name case, as well as "last checkbox checked"
+ // case
+ if ( fullName in seen && type != "radio" && ! $.isArray( data[ name ] )) {
+ if ( name in data ) {
+ data[ name ] = [ data[name] ];
+ } else {
+ data[ name ] = [];
+ }
+ } else {
+ seen[ fullName ] = true;
+ }
+
+ // Finally, assign data
+ if ( ( type == "radio" || type == "checkbox" ) && ! elem.is(":checked") ) {
+ return
+ }
+
+ if ( ! data[ name ] ) {
+ data[ name ] = value;
+ } else {
+ data[ name ].push( value );
+ }
+
+
+ }
+
+ };
+
+ /**
+ * @function jQuery.fn.formParams
+ * @parent jQuery.formParams
+ * @plugin jquery/dom/form_params
+ * @test jquery/dom/form_params/qunit.html
+ *
+ * Returns a JavaScript object for values in a form.
+ * It creates nested objects by using bracket notation in the form element name.
+ *
+ * @param {Object} [params] If an object is passed, the form will be repopulated
+ * with the values of the object based on the name of the inputs within
+ * the form
+ * @param {Boolean} [convert=false] True if strings that look like numbers
+ * and booleans should be converted and if empty string should not be added
+ * to the result.
+ * @return {Object} An object of name-value pairs.
+ */
+ $.fn.extend({
+ formParams: function( params ) {
+
+ var convert;
+
+ // Quick way to determine if something is a boolean
+ if ( !! params === params ) {
+ convert = params;
+ params = null;
+ }
+
+ if ( params ) {
+ return this.setParams( params );
+ } else {
+ return this.getParams( convert );
+ }
+ },
+ setParams: function( params ) {
+
+ // Find all the inputs
+ this.find("[name]").each(function() {
+ var $this = $(this),
+ value = params[ $this.attr("name") ],
+ converterKey;
+
+ // Don't do all this work if there's no value
+ if ( value !== undefined ) {
+
+ converterKey = $this.data('convert');
+ if (converters[converterKey] !== undefined) {
+ value = converters[converterKey]['deserializer'](value);
+ }
+
+ // Nested these if statements for performance
+ if ( $this.is(":radio") ) {
+ if ( $this.val() == value ) {
+ $this.prop("checked", true).trigger('change');
+ }
+ } else if ( $this.is(":checkbox") ) {
+ // Convert single value to an array to reduce
+ // complexity
+ value = $.isArray( value ) ? value : [value];
+ if ( $.inArray( $this.val(), value ) > -1) {
+ $this.prop("checked", true).trigger('change');
+ }
+ } else {
+ $this.val( value );
+ }
+ }
+ });
+ },
+ getParams: function( convert ) {
+ var data = {},
+ // This is used to keep track of the checkbox names that we've
+ // already seen, so we know that we should return an array if
+ // we see it multiple times. Fixes last checkbox checked bug.
+ seen = {},
+ current;
+
+ this.find("[name]:not(:disabled)").each(function() {
+ var $this = $(this),
+ type = $this.attr("type"),
+ name = $this.attr("name"),
+ value = $this.val(),
+ parts,
+ convertKey;
+
+ // Don't accumulate submit buttons and nameless elements
+ if ( type == "submit" || ! name ) {
+ return;
+ }
+
+ // Figure out name parts
+ parts = name.match( keyBreaker );
+ if ( ! parts.length ) {
+ parts = [name];
+ }
+
+ // Convert the value
+ if (convert) {
+ converterKey = $this.data('convert');
+ if (converters[converterKey] !== undefined) {
+ value = converters[converterKey]['serializer'](value);
+ } else {
+ value = convertValue(value);
+ }
+ }
+
+ // Assign data recursively
+ nestData( $this, type, data, parts, value, seen );
+
+ });
+
+ return data;
+ }
+ });
+
+ $.registerParamConverter = function (options) {
+ converters[options.key] = {
+ 'serializer': options.serializer, // To JSON
+ 'deserializer': options.deserializer // To form data
+ };
+ };
+
+ return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/dom/range.js b/dist/dom/range.js
new file mode 100644
index 00000000..a01f9ee7
--- /dev/null
+++ b/dist/dom/range.js
@@ -0,0 +1,868 @@
+(function ($) {
+
+ $.fn.range =
+ /**
+ * @function jQuery.fn.range
+ * @parent jQuery.Range
+ *
+ * `$.fn.range` returns a new [jQuery.Range] instance for the first selected element.
+ *
+ * $('#content').range() //-> range
+ *
+ * @return {$.Range} A $.Range instance for the selected element
+ */
+ function () {
+ return $.Range(this[0])
+ }
+
+ var convertType = function (type) {
+ return type.replace(/([a-z])([a-z]+)/gi,function (all, first, next) {
+ return first + next.toLowerCase()
+ }).replace(/_/g, "");
+ },
+// reverses things like START_TO_END into END_TO_START
+ reverse = function (type) {
+ return type.replace(/^([a-z]+)_TO_([a-z]+)/i, function (all, first, last) {
+ return last + "_TO_" + first;
+ });
+ },
+ getWindow = function (element) {
+ return element ? element.ownerDocument.defaultView || element.ownerDocument.parentWindow : window
+ },
+ bisect = function (el, start, end) {
+ //split the start and end ... figure out who is touching ...
+ if (end - start == 1) {
+ return
+ }
+ },
+ support = {};
+ /**
+ * @Class jQuery.Range
+ * @parent jQuery.Range
+ *
+ * Depending on the object passed, the selected text will be different.
+ *
+ * @param {TextRange|HTMLElement|Point} [range] An object specifiying a
+ * range. Depending on the object, the selected text will be different. $.Range supports the
+ * following types
+ *
+ * - __undefined or null__ - returns a range with nothing selected
+ * - __HTMLElement__ - returns a range with the node's text selected
+ * - __Point__ - returns a range at the point on the screen. The point can be specified like:
+ *
+ * //client coordinates
+ * {clientX: 200, clientY: 300}
+ *
+ * //page coordinates
+ * {pageX: 200, pageY: 300}
+ * {top: 200, left: 300}
+ *
+ * - __TextRange__ a raw text range object.
+ */
+ $.Range = function (range) {
+ // If it's called w/o new, call it with new!
+ if (this.constructor !== $.Range) {
+ return new $.Range(range);
+ }
+ // If we are passed a jQuery-wrapped element, get the raw element
+ if (range && range.jquery) {
+ range = range[0];
+ }
+ // If we have an element, or nothing
+ if (!range || range.nodeType) {
+ // create a range
+ this.win = getWindow(range)
+ if (this.win.document.createRange) {
+ this.range = this.win.document.createRange()
+ } else if(this.win && this.win.document.body && this.win.document.body.createTextRange) {
+ this.range = this.win.document.body.createTextRange()
+ }
+ // if we have an element, make the range select it
+ if (range) {
+ this.select(range)
+ }
+ }
+ // if we are given a point
+ else if (range.clientX != null || range.pageX != null || range.left != null) {
+ this.moveToPoint(range);
+ }
+ // if we are given a touch event
+ else if (range.originalEvent && range.originalEvent.touches && range.originalEvent.touches.length) {
+ this.moveToPoint(range.originalEvent.touches[0])
+
+ }
+ // if we are a normal event
+ else if (range.originalEvent && range.originalEvent.changedTouches && range.originalEvent.changedTouches.length) {
+ this.moveToPoint(range.originalEvent.changedTouches[0])
+ }
+ // given a TextRange or something else?
+ else {
+ this.range = range;
+ }
+ };
+ /**
+ * @static
+ */
+ $.Range.
+ /**
+ * `$.Range.current([element])` returns the currently selected range
+ * (using [window.getSelection](https://developer.mozilla.org/en/nsISelection)).
+ *
+ * var range = $.Range.current()
+ * range.start().offset // -> selection start offset
+ * range.end().offset // -> selection end offset
+ *
+ * @param {HTMLElement} [el] an optional element used to get selection for a given window.
+ * @return {jQuery.Range} The range instance.
+ */
+ current = function (el) {
+ var win = getWindow(el),
+ selection;
+ if (win.getSelection) {
+ // If we can get the selection
+ selection = win.getSelection()
+ return new $.Range(selection.rangeCount ? selection.getRangeAt(0) : win.document.createRange())
+ } else {
+ // Otherwise use document.selection
+ return new $.Range(win.document.selection.createRange());
+ }
+ };
+
+
+ $.extend($.Range.prototype,
+ /** @prototype **/
+ {
+ /**
+ * `range.moveToPoint(point)` moves the range end and start position to a specific point.
+ * A point can be specified like:
+ *
+ * //client coordinates
+ * {clientX: 200, clientY: 300}
+ *
+ * //page coordinates
+ * {pageX: 200, pageY: 300}
+ * {top: 200, left: 300}
+ *
+ * @param point The point to move the range to
+ * @return {$.Range}
+ */
+ moveToPoint : function (point) {
+ var clientX = point.clientX, clientY = point.clientY
+ if (!clientX) {
+ var off = scrollOffset();
+ clientX = (point.pageX || point.left || 0 ) - off.left;
+ clientY = (point.pageY || point.top || 0 ) - off.top;
+ }
+ if (support.moveToPoint) {
+ this.range = $.Range().range
+ this.range.moveToPoint(clientX, clientY);
+ return this;
+ }
+
+
+ // it's some text node in this range ...
+ var parent = document.elementFromPoint(clientX, clientY);
+
+ //typically it will be 'on' text
+ for (var n = 0; n < parent.childNodes.length; n++) {
+ var node = parent.childNodes[n];
+ if (node.nodeType === 3 || node.nodeType === 4) {
+ var range = $.Range(node),
+ length = range.toString().length;
+
+
+ // now lets start moving the end until the boundingRect is within our range
+ for (var i = 1; i < length + 1; i++) {
+ var rect = range.end(i).rect();
+ if (rect.left <= clientX && rect.left + rect.width >= clientX &&
+ rect.top <= clientY && rect.top + rect.height >= clientY) {
+ range.start(i - 1);
+ this.range = range.range;
+ return this;
+ }
+ }
+ }
+ }
+
+ // if not 'on' text, recursively go through and find out when we shift to next
+ // 'line'
+ var previous;
+ iterate(parent.childNodes, function (textNode) {
+ var range = $.Range(textNode);
+ if (range.rect().top > point.clientY) {
+ return false;
+ } else {
+ previous = range;
+ }
+ });
+
+ if (previous) {
+ previous.start(previous.toString().length);
+ this.range = previous.range;
+ } else {
+ this.range = $.Range(parent).range
+ }
+ },
+
+ window : function () {
+ return this.win || window;
+ },
+ /**
+ * `range.overlaps([elRange])` returns `true` if any portion of these two ranges overlap.
+ *
+ * var foo = document.getElementById('foo');
+ *
+ * $.Range(foo.childNodes[0]).overlaps(foo.childNodes[1]) //-> false
+ *
+ * @param {jQuery.Range} elRange The range to compare
+ * @return {Boolean} true if part of the ranges overlap, false if otherwise.
+ */
+ overlaps : function (elRange) {
+ if (elRange.nodeType) {
+ elRange = $.Range(elRange).select(elRange);
+ }
+ //if the start is within the element ...
+ var startToStart = this.compare("START_TO_START", elRange),
+ endToEnd = this.compare("END_TO_END", elRange)
+
+ // if we wrap elRange
+ if (startToStart <= 0 && endToEnd >= 0) {
+ return true;
+ }
+ // if our start is inside of it
+ if (startToStart >= 0 &&
+ this.compare("START_TO_END", elRange) <= 0) {
+ return true;
+ }
+ // if our end is inside of elRange
+ if (this.compare("END_TO_START", elRange) >= 0 &&
+ endToEnd <= 0) {
+ return true;
+ }
+ return false;
+ },
+ /**
+ * `range.collapse([toStart])` collapses a range to one of its boundary points.
+ * See [range.collapse](https://developer.mozilla.org/en/DOM/range.collapse).
+ *
+ * $('#foo').range().collapse()
+ *
+ * @param {Boolean} [toStart] true if to the start of the range, false if to the
+ * end. Defaults to false.
+ * @return {jQuery.Range} returns the range for chaining.
+ */
+ collapse : function (toStart) {
+ this.range.collapse(toStart === undefined ? true : toStart);
+ return this;
+ },
+ /**
+ * `range.toString()` returns the text of the range.
+ *
+ * currentText = $.Range.current().toString()
+ *
+ * @return {String} The text content of this range
+ */
+ toString : function () {
+ return typeof this.range.text == "string" ? this.range.text : this.range.toString();
+ },
+ /**
+ * `range.start([start])` gets or sets the start of the range.
+ *
+ * If a value is not provided, start returns the range's starting container and offset like:
+ *
+ * $('#foo').range().start()
+ * //-> {container: fooElement, offset: 0 }
+ *
+ * If a set value is provided, it can set the range. The start of the range is set differently
+ * depending on the type of set value:
+ *
+ * - __Object__ - an object with the new starting container and offset like
+ *
+ * $.Range().start({container: $('#foo')[0], offset: 20})
+ *
+ * - __Number__ - the new offset value. The container is kept the same.
+ *
+ * - __String__ - adjusts the offset by converting the string offset to a number and adding it to the current
+ * offset. For example, the following moves the offset forward four characters:
+ *
+ * $('#foo').range().start("+4")
+ *
+ * Note that `start` can return a text node. To get the containing element use this:
+ *
+ * var startNode = range.start().container;
+ * if( startNode.nodeType === Node.TEXT_NODE ||
+ * startNode.nodeType === Node.CDATA_SECTION_NODE ) {
+ * startNode = startNode.parentNode;
+ * }
+ * $(startNode).addClass('highlight');
+ *
+ * @param {Object|String|Number} [set] a set value if setting the start of the range or nothing if reading it.
+ * @return {jQuery.Range|Object} if setting the start, the range is returned for chaining, otherwise, the
+ * start offset and container are returned.
+ */
+ start : function (set) {
+ // return start
+ if (set === undefined) {
+ if (this.range.startContainer) {
+ return {
+ container : this.range.startContainer,
+ offset : this.range.startOffset
+ }
+ } else {
+ // Get the start parent element
+ var start = this.clone().collapse().parent();
+ // used to get the start element offset
+ var startRange = $.Range(start).select(start).collapse();
+ startRange.move("END_TO_START", this);
+ return {
+ container : start,
+ offset : startRange.toString().length
+ }
+ }
+ } else {
+ if (this.range.setStart) {
+ // supports setStart
+ if (typeof set == 'number') {
+ this.range.setStart(this.range.startContainer, set)
+ } else if (typeof set == 'string') {
+ var res = callMove(this.range.startContainer, this.range.startOffset, parseInt(set, 10))
+ this.range.setStart(res.node, res.offset);
+ } else {
+ this.range.setStart(set.container, set.offset)
+ }
+ } else {
+ if (typeof set == "string") {
+ this.range.moveStart('character', parseInt(set, 10))
+ } else {
+ // get the current end container
+ var container = this.start().container,
+ offset
+ if (typeof set == "number") {
+ offset = set
+ } else {
+ container = set.container
+ offset = set.offset
+ }
+ var newPoint = $.Range(container).collapse();
+ //move it over offset characters
+ newPoint.range.move(offset);
+ this.move("START_TO_START", newPoint);
+ }
+ }
+ return this;
+ }
+
+
+ },
+ /**
+ * `range.end([end])` gets or sets the end of the range.
+ * It takes similar options as [jQuery.Range::start start]:
+ *
+ * - __Object__ - an object with the new end container and offset like
+ *
+ * $.Range().end({container: $('#foo')[0], offset: 20})
+ *
+ * - __Number__ - the new offset value. The container is kept the same.
+ *
+ * - __String__ - adjusts the offset by converting the string offset to a number and adding it to the current
+ * offset. For example, the following moves the offset forward four characters:
+ *
+ * $('#foo').range().end("+4")
+ *
+ * Note that `end` can return a text node. To get the containing element use this:
+ *
+ * var startNode = range.end().container;
+ * if( startNode.nodeType === Node.TEXT_NODE ||
+ * startNode.nodeType === Node.CDATA_SECTION_NODE ) {
+ * startNode = startNode.parentNode;
+ * }
+ * $(startNode).addClass('highlight');
+ *
+ * @param {Object|String|Number} [set] a set value if setting the end of the range or nothing if reading it.
+ */
+ end : function (set) {
+ // read end
+ if (set === undefined) {
+ if (this.range.startContainer) {
+ return {
+ container : this.range.endContainer,
+ offset : this.range.endOffset
+ }
+ }
+ else {
+ var
+ // Get the end parent element
+ end = this.clone().collapse(false).parent(),
+ // used to get the end elements offset
+ endRange = $.Range(end).select(end).collapse();
+ endRange.move("END_TO_END", this);
+ return {
+ container : end,
+ offset : endRange.toString().length
+ }
+ }
+ } else {
+ if (this.range.setEnd) {
+ if (typeof set == 'number') {
+ this.range.setEnd(this.range.endContainer, set)
+ } else if (typeof set == 'string') {
+ var res = callMove(this.range.endContainer, this.range.endOffset, parseInt(set, 10))
+ this.range.setEnd(res.node, res.offset);
+ } else {
+ this.range.setEnd(set.container, set.offset)
+ }
+ } else {
+ if (typeof set == "string") {
+ this.range.moveEnd('character', parseInt(set, 10));
+ } else {
+ // get the current end container
+ var container = this.end().container,
+ offset
+ if (typeof set == "number") {
+ offset = set
+ } else {
+ container = set.container
+ offset = set.offset
+ }
+ var newPoint = $.Range(container).collapse();
+ //move it over offset characters
+ newPoint.range.move(offset);
+ this.move("END_TO_START", newPoint);
+ }
+ }
+ return this;
+ }
+ },
+ /**
+ * `range.parent()` returns the most common ancestor element of
+ * the endpoints in the range. This will return a text element if the range is
+ * within a text element. In this case, to get the containing element use this:
+ *
+ * var parent = range.parent();
+ * if( parent.nodeType === Node.TEXT_NODE ||
+ * parent.nodeType === Node.CDATA_SECTION_NODE ) {
+ * parent = startNode.parentNode;
+ * }
+ * $(parent).addClass('highlight');
+ *
+ * @return {HTMLNode} the TextNode or HTMLElement
+ * that fully contains the range
+ */
+ parent : function () {
+ if (this.range.commonAncestorContainer) {
+ return this.range.commonAncestorContainer;
+ } else {
+
+ var parentElement = this.range.parentElement(),
+ range = this.range;
+
+ // IE's parentElement will always give an element, we want text ranges
+ iterate(parentElement.childNodes, function (txtNode) {
+ if ($.Range(txtNode).range.inRange(range)) {
+ // swap out the parentElement
+ parentElement = txtNode;
+ return false;
+ }
+ });
+
+ return parentElement;
+ }
+ },
+ /**
+ * `range.rect([from])` returns the bounding rectangle of this range.
+ *
+ * @param {String} [from] - where the coordinates should be
+ * positioned from. By default, coordinates are given from the client viewport.
+ * But if 'page' is given, they are provided relative to the page.
+ *
+ * @return {TextRectangle} - The client rects.
+ */
+ rect : function (from) {
+ var rect = this.range.getBoundingClientRect();
+ // for some reason in webkit this gets a better value
+ if (!rect.height && !rect.width) {
+ rect = this.range.getClientRects()[0]
+ }
+ if (from === 'page') {
+ // Add the scroll offset
+ var off = scrollOffset();
+ rect = $.extend({}, rect);
+ rect.top += off.top;
+ rect.left += off.left;
+ }
+ return rect;
+ },
+ /**
+ * `range.rects(from)` returns the client rects.
+ *
+ * @param {String} [from] how the rects coordinates should be given (viewport or page). Provide 'page' for
+ * rect coordinates from the page.
+ * @return {Array} The client rects
+ */
+ rects : function (from) {
+ // order rects by size
+ var rects = $.map($.makeArray(this.range.getClientRects()).sort(function (rect1, rect2) {
+ return rect2.width * rect2.height - rect1.width * rect1.height;
+ }), function (rect) {
+ return $.extend({}, rect)
+ }),
+ i = 0, j,
+ len = rects.length;
+
+ // safari returns overlapping client rects
+ //
+ // - big rects can contain 2 smaller rects
+ // - some rects can contain 0 - width rects
+ // - we don't want these 0 width rects
+ while (i < rects.length) {
+ var cur = rects[i],
+ found = false;
+
+ j = i + 1;
+ while (j < rects.length) {
+ if (withinRect(cur, rects[j])) {
+ if (!rects[j].width) {
+ rects.splice(j, 1)
+ } else {
+ found = rects[j];
+ break;
+ }
+ } else {
+ j++;
+ }
+ }
+
+
+ if (found) {
+ rects.splice(i, 1)
+ } else {
+ i++;
+ }
+
+ }
+ // safari will be return overlapping ranges ...
+ if (from == 'page') {
+ var off = scrollOffset();
+ return $.each(rects, function (ith, item) {
+ item.top += off.top;
+ item.left += off.left;
+ })
+ }
+
+
+ return rects;
+ }
+
+ });
+ (function () {
+ //method branching ....
+ var fn = $.Range.prototype,
+ range = $.Range().range;
+
+ /**
+ * @function compare
+ *
+ * `range.compare(type, compareRange)` compares one range to another range.
+ *
+ * ## Example
+ *
+ * // compare the highlight element's start position
+ * // to the start of the current range
+ * $('#highlight')
+ * .range()
+ * .compare('START_TO_START', $.Range.current())
+ *
+ *
+ *
+ * @param {String} type Specifies the boundary of the
+ * range and the compareRange to compare.
+ *
+ * - `"START_TO_START"` - the start of the range and the start of compareRange
+ * - `"START_TO_END"` - the start of the range and the end of compareRange
+ * - `"END_TO_END"` - the end of the range and the end of compareRange
+ * - `"END_TO_START"` - the end of the range and the start of compareRange
+ *
+ * @param {$.Range} compareRange The other range
+ * to compare against.
+ * @return {Number} a number indicating if the range
+ * boundary is before,
+ * after, or equal to compareRange's
+ * boundary where:
+ *
+ * - -1 - the range boundary comes before the compareRange boundary
+ * - 0 - the boundaries are equal
+ * - 1 - the range boundary comes after the compareRange boundary
+ */
+ fn.compare = range.compareBoundaryPoints ?
+ function (type, range) {
+ return this.range.compareBoundaryPoints(this.window().Range[reverse(type)], range.range)
+ } :
+ function (type, range) {
+ return this.range.compareEndPoints(convertType(type), range.range)
+ }
+
+ /**
+ * @function move
+ *
+ * `range.move([referenceRange])` moves the endpoints of a range relative to another range.
+ *
+ * // Move the current selection's end to the
+ * // end of the #highlight element
+ * $.Range.current().move('END_TO_END',
+ * $('#highlight').range() )
+ *
+ *
+ * @param {String} type a string indicating the ranges boundary point
+ * to move to which referenceRange boundary point where:
+ *
+ * - `"START_TO_START"` - the start of the range moves to the start of referenceRange
+ * - `"START\_TO\_END"` - the start of the range move to the end of referenceRange
+ * - `"END_TO_END"` - the end of the range moves to the end of referenceRange
+ * - `"END_TO_START"` - the end of the range moves to the start of referenceRange
+ *
+ * @param {jQuery.Range} referenceRange
+ * @return {jQuery.Range} the original range for chaining
+ */
+ fn.move = range.setStart ?
+ function (type, range) {
+
+ var rangesRange = range.range;
+ switch (type) {
+ case "START_TO_END" :
+ this.range.setStart(rangesRange.endContainer, rangesRange.endOffset)
+ break;
+ case "START_TO_START" :
+ this.range.setStart(rangesRange.startContainer, rangesRange.startOffset)
+ break;
+ case "END_TO_END" :
+ this.range.setEnd(rangesRange.endContainer, rangesRange.endOffset)
+ break;
+ case "END_TO_START" :
+ this.range.setEnd(rangesRange.startContainer, rangesRange.startOffset)
+ break;
+ }
+
+ return this;
+ } :
+ function (type, range) {
+ this.range.setEndPoint(convertType(type), range.range)
+ return this;
+ };
+ var cloneFunc = range.cloneRange ? "cloneRange" : "duplicate",
+ selectFunc = range.selectNodeContents ? "selectNodeContents" : "moveToElementText";
+
+ fn.
+ /**
+ * `range.clone()` clones the range and returns a new $.Range
+ * object:
+ *
+ * var range = new $.Range(document.getElementById('text'));
+ * var newRange = range.clone();
+ * range.start('+2');
+ * range.select();
+ *
+ * @return {jQuery.Range} returns the range as a $.Range.
+ */
+ clone = function () {
+ return $.Range(this.range[cloneFunc]());
+ };
+
+ fn.
+ /**
+ * @function
+ *
+ * `range.select([el])` selects an element with this range. If nothing
+ * is provided, makes the current range appear as if the user has selected it.
+ *
+ * This works with text nodes. For example with:
+ *
+ *
This is a text
+ *
+ * $.Range can select `is a` like this:
+ *
+ * var range = new $.Range(document.getElementById('text'));
+ * range.start('+5');
+ * range.end('-5');
+ * range.select();
+ *
+ * @param {HTMLElement} [el] The element in which this range should be selected
+ * @return {jQuery.Range} the range for chaining.
+ */
+ select = range.selectNodeContents ? function (el) {
+ if (!el) {
+ var selection = this.window().getSelection();
+ selection.removeAllRanges();
+ selection.addRange(this.range);
+ } else {
+ this.range.selectNodeContents(el);
+ }
+ return this;
+ } : function (el) {
+ if (!el) {
+ this.range.select()
+ } else if (el.nodeType === 3) {
+ //select this node in the element ...
+ var parent = el.parentNode,
+ start = 0,
+ end;
+ iterate(parent.childNodes, function (txtNode) {
+ if (txtNode === el) {
+ end = start + txtNode.nodeValue.length;
+ return false;
+ } else {
+ start = start + txtNode.nodeValue.length
+ }
+ })
+ this.range.moveToElementText(parent);
+
+ this.range.moveEnd('character', end - this.range.text.length)
+ this.range.moveStart('character', start);
+ } else {
+ this.range.moveToElementText(el);
+ }
+ return this;
+ };
+
+ })();
+
+
+// helpers -----------------
+
+// iterates through a list of elements, calls cb on every text node
+// if cb returns false, exits the iteration
+ var iterate = function (elems, cb) {
+ var elem, start;
+ for (var i = 0; elems[i]; i++) {
+ elem = elems[i];
+ // Get the text from text nodes and CDATA nodes
+ if (elem.nodeType === 3 || elem.nodeType === 4) {
+ if (cb(elem) === false) {
+ return false;
+ }
+ // Traverse everything else, except comment nodes
+ }
+ else if (elem.nodeType !== 8) {
+ if (iterate(elem.childNodes, cb) === false) {
+ return false;
+ }
+ }
+ }
+
+ },
+ isText = function (node) {
+ return node.nodeType === 3 || node.nodeType === 4
+ },
+ iteratorMaker = function (toChildren, toNext) {
+ return function (node, mustMoveRight) {
+ // first try down
+ if (node[toChildren] && !mustMoveRight) {
+ return isText(node[toChildren]) ?
+ node[toChildren] :
+ arguments.callee(node[toChildren])
+ } else if (node[toNext]) {
+ return isText(node[toNext]) ?
+ node[toNext] :
+ arguments.callee(node[toNext])
+ } else if (node.parentNode) {
+ return arguments.callee(node.parentNode, true)
+ }
+ }
+ },
+ getNextTextNode = iteratorMaker("firstChild", "nextSibling"),
+ getPrevTextNode = iteratorMaker("lastChild", "previousSibling"),
+ callMove = function (container, offset, howMany) {
+ var mover = howMany < 0 ?
+ getPrevTextNode : getNextTextNode;
+
+ // find the text element
+ if( !isText(container) ){
+ // sometimes offset isn't actually an element
+ container = container.childNodes[offset] ?
+ container.childNodes[offset] :
+ // if this happens, use the last child
+ container.lastChild;
+
+ if( !isText(container) ) {
+ container = mover(container)
+ }
+ return move(container, howMany)
+ } else {
+ if(offset+howMany < 0){
+ return move(mover(container), offset + howMany)
+ } else {
+ return move(container, offset + howMany)
+ }
+
+ }
+ },
+ // Moves howMany characters from the start of
+ // from
+ move = function (from, howMany) {
+ var mover = howMany < 0 ?
+ getPrevTextNode : getNextTextNode;
+
+ howMany = Math.abs(howMany);
+
+ while (from && howMany >= from.nodeValue.length) {
+ howMany = howMany - from.nodeValue.length;
+ from = mover(from)
+ }
+ return {
+ node : from,
+ offset : mover === getNextTextNode ?
+ howMany :
+ from.nodeValue.length - howMany
+ }
+ },
+ supportWhitespace,
+ isWhitespace = function (el) {
+ if (supportWhitespace == null) {
+ supportWhitespace = 'isElementContentWhitespace' in el;
+ }
+ return (supportWhitespace ? el.isElementContentWhitespace :
+ (el.nodeType === 3 && '' == el.data.trim()));
+
+ },
+// if a point is within a rectangle
+ within = function (rect, point) {
+
+ return rect.left <= point.clientX && rect.left + rect.width >= point.clientX &&
+ rect.top <= point.clientY &&
+ rect.top + rect.height >= point.clientY
+ },
+// if a rectangle is within another rectangle
+ withinRect = function (outer, inner) {
+ return within(outer, {
+ clientX : inner.left,
+ clientY : inner.top
+ }) && //top left
+ within(outer, {
+ clientX : inner.left + inner.width,
+ clientY : inner.top
+ }) && //top right
+ within(outer, {
+ clientX : inner.left,
+ clientY : inner.top + inner.height
+ }) && //bottom left
+ within(outer, {
+ clientX : inner.left + inner.width,
+ clientY : inner.top + inner.height
+ }) //bottom right
+ },
+// gets the scroll offset from a window
+ scrollOffset = function (win) {
+ var win = win || window;
+ doc = win.document.documentElement, body = win.document.body;
+
+ return {
+ left : (doc && doc.scrollLeft || body && body.scrollLeft || 0) + (doc.clientLeft || 0),
+ top : (doc && doc.scrollTop || body && body.scrollTop || 0) + (doc.clientTop || 0)
+ };
+ };
+
+
+ support.moveToPoint = !!$.Range().range.moveToPoint
+
+ return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/dom/selection.js b/dist/dom/selection.js
new file mode 100644
index 00000000..adfb085a
--- /dev/null
+++ b/dist/dom/selection.js
@@ -0,0 +1,228 @@
+(function($){
+
+var getWindow = function( element ) {
+ return element ? element.ownerDocument.defaultView || element.ownerDocument.parentWindow : window
+},
+// A helper that uses range to abstract out getting the current start and endPos.
+getElementsSelection = function(el, win){
+ // get a copy of the current range and a range that spans the element
+ var current = $.Range.current(el).clone(),
+ entireElement = $.Range(el).select(el);
+ // if there is no overlap, there is nothing selected
+ if(!current.overlaps(entireElement)){
+ return null;
+ }
+ // if the current range starts before our element
+ if(current.compare("START_TO_START", entireElement) < 1){
+ // the selection within the element begins at 0
+ startPos = 0;
+ // move the current range to start at our element
+ current.move("START_TO_START",entireElement);
+ }else{
+ // Make a copy of the element's range.
+ // Move it's end to the start of the selected range
+ // The length of the copy is the start of the selected
+ // range.
+ fromElementToCurrent =entireElement.clone();
+ fromElementToCurrent.move("END_TO_START", current);
+ startPos = fromElementToCurrent.toString().length
+ }
+
+ // If the current range ends after our element
+ if(current.compare("END_TO_END", entireElement) >= 0){
+ // the end position is the last character
+ endPos = entireElement.toString().length
+ }else{
+ // otherwise, it's the start position plus the current range
+ // TODO: this doesn't seem like it works if current
+ // extends to the left of the element.
+ endPos = startPos+current.toString().length
+ }
+ return {
+ start: startPos,
+ end : endPos,
+ width : endPos - startPos
+ };
+},
+// Text selection works differently for selection in an input vs
+// normal html elements like divs, spans, and ps.
+// This function branches between the various methods of getting the selection.
+getSelection = function(el){
+ var win = getWindow(el);
+
+ // `selectionStart` means this is an input element in a standards browser.
+ if (el.selectionStart !== undefined) {
+
+ if(document.activeElement
+ && document.activeElement != el
+ && el.selectionStart == el.selectionEnd
+ && el.selectionStart == 0){
+ return {start: el.value.length, end: el.value.length, width: 0};
+ }
+ return {start: el.selectionStart, end: el.selectionEnd, width: el.selectionEnd - el.selectionStart};
+ }
+ // getSelection means a 'normal' element in a standards browser.
+ else if(win.getSelection){
+ return getElementsSelection(el, win)
+ } else{
+ // IE will freak out, where there is no way to detect it, so we provide a callback if it does.
+ try {
+ // The following typically works for input elements in IE:
+ if (el.nodeName.toLowerCase() == 'input') {
+ var real = getWindow(el).document.selection.createRange(),
+ r = el.createTextRange();
+ r.setEndPoint("EndToStart", real);
+
+ var start = r.text.length
+ return {
+ start: start,
+ end: start + real.text.length,
+ width: real.text.length
+ }
+ }
+ // This works on textareas and other elements
+ else {
+ var res = getElementsSelection(el,win)
+ if(!res){
+ return res;
+ }
+ // we have to clean up for ie's textareas which don't count for
+ // newlines correctly
+ var current = $.Range.current().clone(),
+ r2 = current.clone().collapse().range,
+ r3 = current.clone().collapse(false).range;
+
+ r2.moveStart('character', -1)
+ r3.moveStart('character', -1)
+ // if we aren't at the start, but previous is empty, we are at start of newline
+ if (res.startPos != 0 && r2.text == "") {
+ res.startPos += 2;
+ }
+ // do a similar thing for the end of the textarea
+ if (res.endPos != 0 && r3.text == "") {
+ res.endPos += 2;
+ }
+
+ return res
+ }
+ }catch(e){
+ return {start: el.value.length, end: el.value.length, width: 0};
+ }
+ }
+},
+// Selects text within an element. Depending if it's a form element or
+// not, or a standards based browser or not, we do different things.
+select = function( el, start, end ) {
+ var win = getWindow(el);
+ // IE behaves bad even if it sorta supports
+ // getSelection so we have to try the IE methods first. barf.
+ if(el.setSelectionRange){
+ if(end === undefined){
+ el.focus();
+ el.setSelectionRange(start, start);
+ } else {
+ el.select();
+ el.selectionStart = start;
+ el.selectionEnd = end;
+ }
+ } else if (el.createTextRange) {
+ var r = el.createTextRange();
+ r.moveStart('character', start);
+ end = end || start;
+ r.moveEnd('character', end - el.value.length);
+
+ r.select();
+ } else if(win.getSelection){
+ var doc = win.document,
+ sel = win.getSelection(),
+ range = doc.createRange(),
+ ranges = [start, end !== undefined ? end : start];
+ getCharElement([el],ranges);
+ range.setStart(ranges[0].el, ranges[0].count);
+ range.setEnd(ranges[1].el, ranges[1].count);
+
+ // removeAllRanges is necessary for webkit
+ sel.removeAllRanges();
+ sel.addRange(range);
+
+ } else if(win.document.body.createTextRange){ //IE's weirdness
+ var range = document.body.createTextRange();
+ range.moveToElementText(el);
+ range.collapse()
+ range.moveStart('character', start)
+ range.moveEnd('character', end !== undefined ? end : start)
+ range.select();
+ }
+
+},
+// If one of the range values is within start and len, replace the range
+// value with the element and its offset.
+replaceWithLess = function(start, len, range, el){
+ if(typeof range[0] === 'number' && range[0] < len){
+ range[0] = {
+ el: el,
+ count: range[0] - start
+ };
+ }
+ if(typeof range[1] === 'number' && range[1] <= len){
+ range[1] = {
+ el: el,
+ count: range[1] - start
+ };;
+ }
+},
+getCharElement = function( elems , range, len ) {
+ var elem,
+ start;
+
+ len = len || 0;
+ for ( var i = 0; elems[i]; i++ ) {
+ elem = elems[i];
+ // Get the text from text nodes and CDATA nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
+ start = len
+ len += elem.nodeValue.length;
+ //check if len is now greater than what's in counts
+ replaceWithLess(start, len, range, elem )
+ // Traverse everything else, except comment nodes
+ } else if ( elem.nodeType !== 8 ) {
+ len = getCharElement( elem.childNodes, range, len );
+ }
+ }
+ return len;
+};
+/**
+ * @parent jQuery.selection
+ * @function jQuery.fn.selection
+ *
+ * Set or retrieve the currently selected text range. It works on all elements:
+ *
+ * $('#text').selection(8, 12)
+ * $('#text').selection() // -> { start : 8, end : 12, width: 4 }
+ *
+ * @param {Number} [start] Start position of the selection range
+ * @param {Number} [end] End position of the selection range
+ * @return {Object|jQuery} Returns either the jQuery object when setting the selection or
+ * an object containing
+ *
+ * - __start__ - The number of characters from the start of the element to the start of the selection.
+ * - __end__ - The number of characters from the start of the element to the end of the selection.
+ * - __width__ - The width of the selection range.
+ *
+ * when no arguments are passed.
+ */
+$.fn.selection = function(start, end){
+ if(start !== undefined){
+ return this.each(function(){
+ select(this, start, end)
+ })
+ }else{
+ return getSelection(this[0])
+ }
+};
+// for testing
+$.fn.selection.getCharElement = getCharElement;
+
+return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/dom/styles.js b/dist/dom/styles.js
new file mode 100644
index 00000000..ca6da98b
--- /dev/null
+++ b/dist/dom/styles.js
@@ -0,0 +1,104 @@
+(function( $ ) {
+ var getComputedStyle = document.defaultView && document.defaultView.getComputedStyle,
+ // The following variables are used to convert camelcased attribute names
+ // into dashed names, e.g. borderWidth to border-width
+ rupper = /([A-Z])/g,
+ rdashAlpha = /-([a-z])/ig,
+ fcamelCase = function( all, letter ) {
+ return letter.toUpperCase();
+ },
+ // Returns the computed style for an elementn
+ getStyle = function( elem ) {
+ if ( getComputedStyle ) {
+ return getComputedStyle(elem, null);
+ }
+ else if ( elem.currentStyle ) {
+ return elem.currentStyle;
+ }
+ },
+ // Checks for float px and numeric values
+ rfloat = /float/i,
+ rnumpx = /^-?\d+(?:px)?$/i,
+ rnum = /^-?\d/;
+
+ // Returns a list of styles for a given element
+ $.styles = function( el, styles ) {
+ if (!el ) {
+ return null;
+ }
+ var currentS = getStyle(el),
+ oldName, val, style = el.style,
+ results = {},
+ i = 0,
+ left, rsLeft, camelCase, name;
+
+ // Go through each style
+ for (; i < styles.length; i++ ) {
+ name = styles[i];
+ oldName = name.replace(rdashAlpha, fcamelCase);
+
+ if ( rfloat.test(name) ) {
+ name = $.support.cssFloat ? "float" : "styleFloat";
+ oldName = "cssFloat";
+ }
+
+ // If we have getComputedStyle available
+ if ( getComputedStyle ) {
+ // convert camelcased property names to dashed name
+ name = name.replace(rupper, "-$1").toLowerCase();
+ // use getPropertyValue of the current style object
+ val = currentS.getPropertyValue(name);
+ // default opacity is 1
+ if ( name === "opacity" && val === "" ) {
+ val = "1";
+ }
+ results[oldName] = val;
+ } else {
+ // Without getComputedStyles
+ camelCase = name.replace(rdashAlpha, fcamelCase);
+ results[oldName] = currentS[name] || currentS[camelCase];
+
+ // convert to px
+ if (!rnumpx.test(results[oldName]) && rnum.test(results[oldName]) ) {
+ // Remember the original values
+ left = style.left;
+ rsLeft = el.runtimeStyle.left;
+
+ // Put in the new values to get a computed value out
+ el.runtimeStyle.left = el.currentStyle.left;
+ style.left = camelCase === "fontSize" ? "1em" : (results[oldName] || 0);
+ results[oldName] = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ el.runtimeStyle.left = rsLeft;
+ }
+
+ }
+ }
+
+ return results;
+ };
+
+ /**
+ * @function jQuery.fn.styles
+ * @parent jQuery.styles
+ * @plugin jQuery.styles
+ *
+ * Returns a set of computed styles. Pass the names of the styles you want to
+ * retrieve as arguments:
+ *
+ * $("div").styles('float','display')
+ * // -> { cssFloat: "left", display: "block" }
+ *
+ * @param {String} style pass the names of the styles to retrieve as the argument list
+ * @return {Object} an object of `style` : `value` pairs
+ */
+ $.fn.styles = function() {
+ // Pass the arguments as an array to $.styles
+ return $.styles(this[0], $.makeArray(arguments));
+ };
+
+ return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/dom/within.js b/dist/dom/within.js
new file mode 100644
index 00000000..a8780ac9
--- /dev/null
+++ b/dist/dom/within.js
@@ -0,0 +1,95 @@
+(function($) {
+ // Checks if x and y coordinates are within a box with left, top, width and height
+ var withinBox = function(x, y, left, top, width, height ){
+ return (y >= top &&
+ y < top + height &&
+ x >= left &&
+ x < left + width);
+ }
+/**
+ * @function jQuery.fn.within
+ * @parent jQuery.within
+ * @plugin jquery/dom/within
+ *
+ * Returns all elements matching the selector that touch a given point:
+ *
+ * // get all elements that touch 200x200.
+ * $('*').within(200, 200);
+ *
+ * @param {Number} left the position from the left of the page
+ * @param {Number} top the position from the top of the page
+ * @param {Boolean} [useOffsetCache=false] cache the dimensions and offset of the elements.
+ * @return {jQuery} a jQuery collection of elements whos area
+ * overlaps the element position.
+ */
+$.fn.within= function(left, top, useOffsetCache) {
+ var ret = []
+ this.each(function(){
+ var q = jQuery(this);
+
+ if (this == document.documentElement) {
+ return ret.push(this);
+ }
+
+ // uses either the cached offset or .offset()
+ var offset = useOffsetCache ?
+ $.data(this,"offsetCache") || $.data(this,"offsetCache", q.offset()) :
+ q.offset();
+
+ // Check if the given coordinates are within the area of the current element
+ var res = withinBox(left, top, offset.left, offset.top,
+ this.offsetWidth, this.offsetHeight );
+
+ if (res) {
+ // Add it to the results
+ ret.push(this);
+ }
+ });
+
+ return this.pushStack( $.unique( ret ), "within", left+","+top );
+}
+
+
+/**
+ * @function jQuery.fn.withinBox
+ * @parent jQuery.within
+ *
+ * Returns all elements matching the selector that have a given area in common:
+ *
+ * $('*').withinBox(200, 200, 100, 100)
+ *
+ * @param {Number} left the position from the left of the page
+ * @param {Number} top the position from the top of the page
+ * @param {Number} width the width of the area
+ * @param {Number} height the height of the area
+ * @param {Boolean} [useOffsetCache=false] cache the dimensions and offset of the elements.
+ * @return {jQuery} a jQuery collection of elements whos area
+ * overlaps the element position.
+ */
+$.fn.withinBox = function(left, top, width, height, useOffsetCache){
+ var ret = []
+ this.each(function(){
+ var q = jQuery(this);
+
+ if(this == document.documentElement) return ret.push(this);
+
+ // use cached offset or .offset()
+ var offset = useOffsetCache ?
+ $.data(this,"offset") ||
+ $.data(this,"offset", q.offset()) :
+ q.offset();
+
+
+ var ew = q.width(), eh = q.height(),
+ // Checks if the element offset is within the given box
+ res = !( (offset.top > top+height) || (offset.top +eh < top) || (offset.left > left+width ) || (offset.left+ew < left));
+
+ if(res)
+ ret.push(this);
+ });
+ return this.pushStack( $.unique( ret ), "withinBox", $.makeArray(arguments).join(",") );
+}
+
+return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/event/default.js b/dist/event/default.js
new file mode 100644
index 00000000..af901541
--- /dev/null
+++ b/dist/event/default.js
@@ -0,0 +1,120 @@
+(function($){
+/**
+ * @function jQuery.fn.triggerAsync
+ * @parent jQuery.event.pause
+ * @plugin jquery/event/default
+ *
+ * `jQuery.fn.triggerAsync(type, [data], [success], [prevented]` triggers an event and calls success
+ * when the event has finished propagating through the DOM and no other handler
+ * called `event.preventDefault()` or returned `false`.
+ *
+ * $('#panel').triggerAsync('show', function() {
+ * $('#panel').show();
+ * });
+ *
+ * You can also provide a callback that gets called if `event.preventDefault()` was called on the event:
+ *
+ * $('panel').triggerAsync('show', function(){
+ * $('#panel').show();
+ * },function(){
+ * $('#other').addClass('error');
+ * });
+ *
+ * @param {String} type The type of event
+ * @param {Object} data The data for the event
+ * @param {Function} success a callback function which occurs upon success
+ * @param {Function} prevented a callback function which occurs if preventDefault was called
+ */
+$.fn.triggerAsync = function(type, data, success, prevented){
+ if(typeof data == 'function'){
+ prevented=success;
+ success = data;
+ data = undefined;
+ }
+
+ if ( this.length ) {
+ var el=this;
+ // Trigger the event with the success callback as the success handler
+ // when triggerAsync called within another triggerAsync,it's the same tick time so we should use timeout
+ // http://javascriptweblog.wordpress.com/2010/06/28/understanding-javascript-timers/
+ setTimeout(function(){
+ el.trigger( {type: type, _success: success,_prevented:prevented}, data);
+ },0);
+
+ } else{
+ // If we have no elements call the success callback right away
+ if(success)
+ success.call(this);
+ }
+ return this;
+}
+
+
+
+/**
+ * @add jQuery.event.special
+ */
+//cache default types for performance
+var types = {}, rnamespaces= /\.(.*)$/, $event = $.event;
+/**
+ * @attribute default
+ * @parent specialevents
+ * @plugin jquery/event/default
+ * @download http://jmvcsite.heroku.com/pluginify?plugins[]=jquery/event/default/default.js
+ * @test jquery/event/default/qunit.html
+ *
+ */
+$event.special["default"] = {
+ add: function( handleObj ) {
+ //save the type
+ types[handleObj.namespace.replace(rnamespaces,"")] = true;
+ },
+ setup: function() {return true}
+}
+
+// overwrite trigger to allow default types
+var oldTrigger = $event.trigger;
+
+$event.trigger = function defaultTriggerer( event, data, elem, onlyHandlers){
+
+ // Event object or event type
+ var type = event.type || event,
+ // Caller can pass in an Event, Object, or just an event type string
+ event = typeof event === "object" ?
+ // jQuery.Event object
+ event[ $.expando ] ? event :
+ // Object literal
+ new $.Event( type, event ) :
+ // Just the event type (string)
+ new $.Event( type),
+ res=oldTrigger.call($.event,event, data, elem, onlyHandlers),
+ paused=event.isPaused && event.isPaused();
+
+ if(!onlyHandlers && !event.isDefaultPrevented() && event.type.indexOf("default") !== 0) {
+ // Trigger the default. event
+ oldTrigger("default."+event.type, data, elem)
+ if(event._success){
+ event._success(event)
+ }
+ }
+
+ if(!onlyHandlers && event.isDefaultPrevented() && event.type.indexOf("default") !== 0 && !paused ){
+ if(event._prevented){
+ event._prevented(event);
+ }
+ }
+
+ // code for paused
+ if( paused ){
+ // set back original stuff
+ event.isDefaultPrevented =
+ event.pausedState.isDefaultPrevented;
+ event.isPropagationStopped =
+ event.pausedState.isPropagationStopped;
+ }
+ return res;
+};
+
+return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/event/destroyed.js b/dist/event/destroyed.js
new file mode 100644
index 00000000..69a6ce07
--- /dev/null
+++ b/dist/event/destroyed.js
@@ -0,0 +1,23 @@
+(function( $ ) {
+ /**
+ * @attribute destroyed
+ * @download http://jmvcsite.heroku.com/pluginify?plugins[]=jquery/dom/destroyed/destroyed.js
+ * @test jquery/event/destroyed/qunit.html
+ */
+
+ // Store the old jQuery.cleanData
+ var oldClean = $.cleanData;
+
+ // Overwrites cleanData which is called by jQuery on manipulation methods
+ $.cleanData = function( elems ) {
+ for ( var i = 0, elem;
+ (elem = elems[i]) !== undefined; i++ ) {
+ // Trigger the destroyed event
+ $(elem).triggerHandler("destroyed");
+ }
+ // Call the old jQuery.cleanData
+ oldClean(elems);
+ };
+return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/event/drag.js b/dist/event/drag.js
new file mode 100644
index 00000000..ccdcf7a1
--- /dev/null
+++ b/dist/event/drag.js
@@ -0,0 +1,4 @@
+(function( $ ) {
+ return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/event/drag/core.js b/dist/event/drag/core.js
new file mode 100644
index 00000000..36fc3ef1
--- /dev/null
+++ b/dist/event/drag/core.js
@@ -0,0 +1,748 @@
+(function( $ ) {
+
+ if(!$.event.special.move) {
+ $.event.reverse('move');
+ }
+
+ //modify live
+ //steal the live handler ....
+ var bind = function( object, method ) {
+ var args = Array.prototype.slice.call(arguments, 2);
+ return function() {
+ var args2 = [this].concat(args, $.makeArray(arguments));
+ return method.apply(object, args2);
+ };
+ },
+ event = $.event,
+ // function to clear the window selection if there is one
+ clearSelection = window.getSelection ? function(){
+ window.getSelection().removeAllRanges()
+ } : function(){},
+
+ supportTouch = "ontouchend" in document,
+ // Use touch events or map it to mouse events
+ startEvent = supportTouch ? "touchstart" : "mousedown",
+ stopEvent = supportTouch ? "touchend" : "mouseup",
+ moveEvent = supportTouch ? "touchmove" : "mousemove",
+ // On touchmove events the default (scrolling) event has to be prevented
+ preventTouchScroll = function(ev) {
+ ev.preventDefault();
+ };
+
+ /**
+ * @class jQuery.Drag
+ * @parent jQuery.event.drag
+ * @plugin jquery/event/drag
+ * @download http://jmvcsite.heroku.com/pluginify?plugins[]=jquery/event/drag/drag.js
+ * @test jquery/event/drag/qunit.html
+ *
+ * The `$.Drag` constructor is never called directly but an instance of `$.Drag` is passed as the second argument
+ * to the `dragdown`, `draginit`, `dragmove`, `dragend`, `dragover` and `dragout` event handlers:
+ *
+ * $('#dragger').on('draginit', function(el, drag) {
+ * // drag -> $.Drag
+ * });
+ */
+ $.Drag = function() {};
+
+ /**
+ * @Static
+ */
+ $.extend($.Drag, {
+ lowerName: "drag",
+ current: null,
+ distance: 0,
+ /**
+ * Called when someone mouses down on a draggable object.
+ * Gathers all callback functions and creates a new Draggable.
+ * @hide
+ */
+ mousedown: function( ev, element ) {
+ var isLeftButton = ev.button === 0 || ev.button == 1,
+ doEvent = isLeftButton || supportTouch;
+
+ if (!doEvent || this.current ) {
+ return;
+ }
+
+ //create Drag
+ var drag = new $.Drag(),
+ delegate = ev.delegateTarget || element,
+ selector = ev.handleObj.selector,
+ self = this;
+ this.current = drag;
+
+ drag.setup({
+ element: element,
+ delegate: ev.delegateTarget || element,
+ selector: ev.handleObj.selector,
+ moved: false,
+ _distance: this.distance,
+ callbacks: {
+ dragdown: event.find(delegate, ["dragdown"], selector),
+ draginit: event.find(delegate, ["draginit"], selector),
+ dragover: event.find(delegate, ["dragover"], selector),
+ dragmove: event.find(delegate, ["dragmove"], selector),
+ dragout: event.find(delegate, ["dragout"], selector),
+ dragend: event.find(delegate, ["dragend"], selector),
+ dragcleanup: event.find(delegate, ["dragcleanup"], selector)
+ },
+ destroyed: function() {
+ self.current = null;
+ }
+ }, ev);
+ }
+ });
+
+ /**
+ * @Prototype
+ */
+ $.extend($.Drag.prototype, {
+ setup: function( options, ev ) {
+ $.extend(this, options);
+
+ this.element = $(this.element);
+ this.event = ev;
+ this.moved = false;
+ this.allowOtherDrags = false;
+ var mousemove = bind(this, this.mousemove),
+ mouseup = bind(this, this.mouseup);
+ this._mousemove = mousemove;
+ this._mouseup = mouseup;
+ this._distance = options.distance ? options.distance : 0;
+
+ //where the mouse is located
+ this.mouseStartPosition = ev.vector();
+
+ $(document).bind(moveEvent, mousemove);
+ $(document).bind(stopEvent, mouseup);
+ if(supportTouch) {
+ // On touch devices we want to disable scrolling
+ $(document).bind(moveEvent, preventTouchScroll);
+ }
+
+ if (!this.callEvents('down', this.element, ev) ) {
+ this.noSelection(this.delegate);
+ //this is for firefox
+ clearSelection();
+ }
+ },
+ /**
+ * @attribute element
+ * A reference to the element that is being dragged. For example:
+ *
+ * $('.draggable').on('draginit', function(ev, drag) {
+ * drag.element.html('I am the drag element');
+ * });
+ */
+
+ /**
+ * Unbinds listeners and allows other drags ...
+ * @hide
+ */
+ destroy: function() {
+ // Unbind the mouse handlers attached for dragging
+ $(document).unbind(moveEvent, this._mousemove);
+ $(document).unbind(stopEvent, this._mouseup);
+ if(supportTouch) {
+ // Enable scrolling again for touch devices when the drag is done
+ $(document).unbind(moveEvent, preventTouchScroll);
+ }
+
+ if (!this.moved ) {
+ this.event = this.element = null;
+ }
+
+ if(!supportTouch) {
+ this.selection(this.delegate);
+ }
+ this.destroyed();
+ },
+ mousemove: function( docEl, ev ) {
+ if (!this.moved ) {
+ var dist = Math.sqrt( Math.pow( ev.pageX - this.event.pageX, 2 ) + Math.pow( ev.pageY - this.event.pageY, 2 ));
+ // Don't initialize the drag if it hasn't been moved the minimum distance
+ if(dist < this._distance){
+ return false;
+ }
+ // Otherwise call init and indicate that the drag has moved
+ this.init(this.element, ev);
+ this.moved = true;
+ }
+
+ this.element.trigger('move', this);
+ var pointer = ev.vector();
+ if ( this._start_position && this._start_position.equals(pointer) ) {
+ return;
+ }
+ this.draw(pointer, ev);
+ },
+
+ mouseup: function( docEl, event ) {
+ //if there is a current, we should call its dragstop
+ if ( this.moved ) {
+ this.end(event);
+ }
+ this.destroy();
+ },
+
+ /**
+ * The `drag.noSelection(element)` method turns off text selection during a drag event.
+ * This method is called by default unless a event is listening to the 'dragdown' event.
+ *
+ * ## Example
+ *
+ * $('div.drag').bind('dragdown', function(elm,event,drag){
+ * drag.noSelection();
+ * });
+ *
+ * @param [elm] an element to prevent selection on. Defaults to the dragable element.
+ */
+ noSelection: function(elm) {
+ elm = elm || this.delegate
+ document.documentElement.onselectstart = function() {
+ // Disables selection
+ return false;
+ };
+ document.documentElement.unselectable = "on";
+ this.selectionDisabled = (this.selectionDisabled ? this.selectionDisabled.add(elm) : $(elm));
+ this.selectionDisabled.css('-moz-user-select', '-moz-none');
+ },
+
+ /**
+ * @hide
+ * `drag.selection()` method turns on text selection that was previously turned off during the drag event.
+ * This method is always called.
+ *
+ * ## Example
+ *
+ * $('div.drag').bind('dragdown', function(elm,event,drag){
+ * drag.selection();
+ * });
+ */
+ selection: function() {
+ if(this.selectionDisabled) {
+ document.documentElement.onselectstart = function() {};
+ document.documentElement.unselectable = "off";
+ this.selectionDisabled.css('-moz-user-select', '');
+ }
+ },
+
+ init: function( element, event ) {
+ element = $(element);
+ //the element that has been clicked on
+ var startElement = (this.movingElement = (this.element = $(element)));
+ //if a mousemove has come after the click
+ //if the drag has been cancelled
+ this._cancelled = false;
+ this.event = event;
+
+ /**
+ * @attribute mouseElementPosition
+ * The position of start of the cursor on the element
+ */
+ this.mouseElementPosition = this.mouseStartPosition.minus(this.element.offsetv()); //where the mouse is on the Element
+ this.callEvents('init', element, event);
+
+ // Check what they have set and respond accordingly if they canceled
+ if ( this._cancelled === true ) {
+ return;
+ }
+ // if they set something else as the element
+ this.startPosition = startElement != this.movingElement ? this.movingElement.offsetv() : this.currentDelta();
+
+ this.makePositioned(this.movingElement);
+ // Adjust the drag elements z-index to a high value
+ this.oldZIndex = this.movingElement.css('zIndex');
+ this.movingElement.css('zIndex', 1000);
+ if (!this._only && this.constructor.responder ) {
+ // calls $.Drop.prototype.compile if there is a drop element
+ this.constructor.responder.compile(event, this);
+ }
+ },
+ makePositioned: function( that ) {
+ var style, pos = that.css('position');
+
+ // Position properly, set top and left to 0px for Opera
+ if (!pos || pos == 'static' ) {
+ style = {
+ position: 'relative'
+ };
+
+ if ( window.opera ) {
+ style.top = '0px';
+ style.left = '0px';
+ }
+ that.css(style);
+ }
+ },
+ callEvents: function( type, element, event, drop ) {
+ var i, cbs = this.callbacks[this.constructor.lowerName + type];
+ for ( i = 0; i < cbs.length; i++ ) {
+ cbs[i].call(element, event, this, drop);
+ }
+ return cbs.length;
+ },
+ /**
+ * Returns the position of the movingElement by taking its top and left.
+ * @hide
+ * @return {$.Vector}
+ */
+ currentDelta: function() {
+ return new $.Vector(parseInt(this.movingElement.css('left'), 10) || 0, parseInt(this.movingElement.css('top'), 10) || 0);
+ },
+ //draws the position of the dragmove object
+ draw: function( pointer, event ) {
+ // only drag if we haven't been cancelled;
+ if ( this._cancelled ) {
+ return;
+ }
+ clearSelection();
+ /**
+ * @attribute location
+ * `drag.location` is a [jQuery.Vector] specifying where the element should be in the page. This
+ * takes into account the start position of the cursor on the element.
+ *
+ * If the drag is going to be moved to an unacceptable location, you can call preventDefault in
+ * dragmove to prevent it from being moved there.
+ *
+ * $('.mover').bind("dragmove", function(ev, drag){
+ * if(drag.location.top() < 100){
+ * ev.preventDefault()
+ * }
+ * });
+ *
+ * You can also set the location to where it should be on the page.
+ *
+ */
+ // the offset between the mouse pointer and the representative that the user asked for
+ this.location = pointer.minus(this.mouseElementPosition);
+
+ // call move events
+ this.move(event);
+ if ( this._cancelled ) {
+ return;
+ }
+ if (!event.isDefaultPrevented() ) {
+ this.position(this.location);
+ }
+
+ // fill in
+ if (!this._only && this.constructor.responder ) {
+ this.constructor.responder.show(pointer, this, event);
+ }
+ },
+ /**
+ * `drag.position( newOffsetVector )` sets the position of the movingElement. This is overwritten by
+ * the [$.Drag::scrolls], [$.Drag::limit] and [$.Drag::step] plugins
+ * to make sure the moving element scrolls some element
+ * or stays within some boundary. This function is exposed and documented so you could do the same.
+ *
+ * The following approximates how step does it:
+ *
+ * var oldPosition = $.Drag.prototype.position;
+ * $.Drag.prototype.position = function( offsetPositionv ) {
+ * if(this._step){
+ * // change offsetPositionv to be on the step value
+ * }
+ *
+ * oldPosition.call(this, offsetPosition)
+ * }
+ *
+ * @param {jQuery.Vector} newOffsetv the new [$.Drag::location] of the element.
+ */
+ position: function( newOffsetv ) { //should draw it on the page
+ var style, dragged_element_css_offset = this.currentDelta(),
+ // the drag element's current left + top css attributes
+ // the vector between the movingElement's page and css positions
+ // this can be thought of as the original offset
+ dragged_element_position_vector = this.movingElement.offsetv().minus(dragged_element_css_offset);
+ this.required_css_position = newOffsetv.minus(dragged_element_position_vector);
+
+ this.offsetv = newOffsetv;
+ style = this.movingElement[0].style;
+ if (!this._cancelled && !this._horizontal ) {
+ style.top = this.required_css_position.top() + "px";
+ }
+ if (!this._cancelled && !this._vertical ) {
+ style.left = this.required_css_position.left() + "px";
+ }
+ },
+ move: function( event ) {
+ this.callEvents('move', this.element, event);
+ },
+ over: function( event, drop ) {
+ this.callEvents('over', this.element, event, drop);
+ },
+ out: function( event, drop ) {
+ this.callEvents('out', this.element, event, drop);
+ },
+ /**
+ * Called on drag up
+ * @hide
+ * @param {Event} event a mouseup event signalling drag/drop has completed
+ */
+ end: function( event ) {
+ // If canceled do nothing
+ if ( this._cancelled ) {
+ return;
+ }
+ // notify the responder - usually a $.Drop instance
+ if (!this._only && this.constructor.responder ) {
+ this.constructor.responder.end(event, this);
+ }
+
+ this.callEvents('end', this.element, event);
+
+ if ( this._revert ) {
+ var self = this;
+ // animate moving back to original position
+ this.movingElement.animate({
+ top: this.startPosition.top() + "px",
+ left: this.startPosition.left() + "px"
+ }, function() {
+ self.cleanup.apply(self, arguments);
+ });
+ }
+ else {
+ this.cleanup(event);
+ }
+ this.event = null;
+ },
+ /**
+ * Cleans up drag element after drag drop.
+ * @hide
+ */
+ cleanup: function(event) {
+ this.movingElement.css({
+ zIndex: this.oldZIndex
+ });
+ if ( this.movingElement[0] !== this.element[0] &&
+ !this.movingElement.has(this.element[0]).length &&
+ !this.element.has(this.movingElement[0]).length ) {
+ this.movingElement.css({
+ display: 'none'
+ });
+ }
+ if ( this._removeMovingElement ) {
+ // Remove the element when using drag.ghost()
+ this.movingElement.remove();
+ }
+
+ if(event) {
+ this.callEvents('cleanup', this.element, event);
+ }
+
+ this.movingElement = this.element = this.event = null;
+ },
+ /**
+ * `drag.cancel()` stops a drag motion from from running. This also stops any other events from firing, meaning
+ * that "dragend" will not be called.
+ *
+ * $("#todos").on(".handle", "draginit", function( ev, drag ) {
+ * if(drag.movingElement.hasClass("evil")){
+ * drag.cancel();
+ * }
+ * })
+ *
+ */
+ cancel: function() {
+ this._cancelled = true;
+ if (!this._only && this.constructor.responder ) {
+ // clear the drops
+ this.constructor.responder.clear(this.event.vector(), this, this.event);
+ }
+ this.destroy();
+
+ },
+ /**
+ * `drag.ghost( [parent] )` clones the element and uses it as the
+ * moving element, leaving the original dragged element in place. The `parent` option can
+ * be used to specify where the ghost element should be temporarily added into the
+ * DOM. This method should be called in "draginit".
+ *
+ * $("#todos").on(".handle", "draginit", function( ev, drag ) {
+ * drag.ghost();
+ * })
+ *
+ * @param {HTMLElement} [parent] the parent element of the newly created ghost element. If not provided the
+ * ghost element is added after the moving element.
+ * @return {jQuery.fn} the ghost element to do whatever you want with it.
+ */
+ ghost: function( parent ) {
+ // create a ghost by cloning the source element and attach the clone to the dom after the source element
+ var ghost = this.movingElement.clone().css('position', 'absolute');
+ if( parent ) {
+ $(parent).append(ghost);
+ } else {
+ $(this.movingElement).after(ghost)
+ }
+ ghost.width(this.movingElement.width()).height(this.movingElement.height());
+ // put the ghost in the right location ...
+ ghost.offset(this.movingElement.offset())
+
+ // store the original element and make the ghost the dragged element
+ this.movingElement = ghost;
+ this.noSelection(ghost)
+ this._removeMovingElement = true;
+ return ghost;
+ },
+ /**
+ * `drag.representative( element, [offsetX], [offsetY])` tells the drag motion to use
+ * a different element than the one that began the drag motion.
+ *
+ * For example, instead of
+ * dragging an drag-icon of a todo element, you want to move some other representation of
+ * the todo element (or elements). To do this you might:
+ *
+ * $("#todos").on(".handle", "draginit", function( ev, drag ) {
+ * // create what we'll drag
+ * var rep = $('').text("todos")
+ * .appendTo(document.body)
+ * // indicate we want our mouse on the top-right of it
+ * drag.representative(rep, rep.width(), 0);
+ * })
+ *
+ * @param {HTMLElement} element the element you want to actually drag. This should be
+ * already in the DOM.
+ * @param {Number} offsetX the x position where you want your mouse on the representative element (defaults to 0)
+ * @param {Number} offsetY the y position where you want your mouse on the representative element (defaults to 0)
+ * @return {drag} returns the drag object for chaining.
+ */
+ representative: function( element, offsetX, offsetY ) {
+ this._offsetX = offsetX || 0;
+ this._offsetY = offsetY || 0;
+
+ var p = this.mouseStartPosition;
+ // Just set the representative as the drag element
+ this.movingElement = $(element);
+ this.movingElement.css({
+ top: (p.y() - this._offsetY) + "px",
+ left: (p.x() - this._offsetX) + "px",
+ display: 'block',
+ position: 'absolute'
+ }).show();
+ this.noSelection(this.movingElement)
+ this.mouseElementPosition = new $.Vector(this._offsetX, this._offsetY);
+ return this;
+ },
+ /**
+ * `drag.revert([val])` makes the [$.Drag::representative representative] element revert back to it
+ * original position after the drag motion has completed. The revert is done with an animation.
+ *
+ * $("#todos").on(".handle","dragend",function( ev, drag ) {
+ * drag.revert();
+ * })
+ *
+ * @param {Boolean} [val] optional, set to false if you don't want to revert.
+ * @return {drag} the drag object for chaining
+ */
+ revert: function( val ) {
+ this._revert = val === undefined ? true : val;
+ return this;
+ },
+ /**
+ * `drag.vertical()` isolates the drag to vertical movement. For example:
+ *
+ * $("#images").on(".thumbnail","draginit", function(ev, drag){
+ * drag.vertical();
+ * });
+ *
+ * Call `vertical()` in "draginit" or "dragdown".
+ *
+ * @return {drag} the drag object for chaining.
+ */
+ vertical: function() {
+ this._vertical = true;
+ return this;
+ },
+ /**
+ * `drag.horizontal()` isolates the drag to horizontal movement. For example:
+ *
+ * $("#images").on(".thumbnail","draginit", function(ev, drag){
+ * drag.horizontal();
+ * });
+ *
+ * Call `horizontal()` in "draginit" or "dragdown".
+ *
+ * @return {drag} the drag object for chaining.
+ */
+ horizontal: function() {
+ this._horizontal = true;
+ return this;
+ },
+ /**
+ * `drag.only([only])` indicates if you __only__ want a drag motion and the drag should
+ * not notify drops. The default value is `false`. Call it with no arguments or pass it true
+ * to prevent drop events.
+ *
+ * $("#images").on(".thumbnail","dragdown", function(ev, drag){
+ * drag.only();
+ * });
+ *
+ * @param {Boolean} [only] true if you want to prevent drops, false if otherwise.
+ * @return {Boolean} the current value of only.
+ */
+ only: function( only ) {
+ return (this._only = (only === undefined ? true : only));
+ },
+
+ /**
+ * `distance([val])` sets or reads the distance the mouse must move before a drag motion is started. This should be set in
+ * "dragdown" and delays "draginit" being called until the distance is covered. The distance
+ * is measured in pixels. The default distance is 0 pixels meaning the drag motion starts on the first
+ * mousemove after a mousedown.
+ *
+ * Set this to make drag motion a little "stickier" to start.
+ *
+ * $("#images").on(".thumbnail","dragdown", function(ev, drag){
+ * drag.distance(10);
+ * });
+ *
+ * @param {Number} [val] The number of pixels the mouse must move before "draginit" is called.
+ * @return {drag|Number} returns the drag instance for chaining if the drag value is being set or the
+ * distance value if the distance is being read.
+ */
+ distance: function(val){
+ if(val !== undefined){
+ this._distance = val;
+ return this;
+ }else{
+ return this._distance
+ }
+ }
+ });
+ /**
+ * @add jQuery.event.special
+ */
+ event.setupHelper([
+ /**
+ * @attribute dragdown
+ * @parent jQuery.event.drag
+ *
+ * `dragdown` is called when a drag movement has started on a mousedown.
+ * The event handler gets an instance of [jQuery.Drag] passed as the second
+ * parameter. Listening to `dragdown` allows you to customize
+ * the behavior of a drag motion, especially when `draginit` should be called.
+ *
+ * $(".handles").delegate("dragdown", function(ev, drag){
+ * // call draginit only when the mouse has moved 20 px
+ * drag.distance(20);
+ * })
+ *
+ * Typically, when a drag motion is started, `event.preventDefault` is automatically
+ * called, preventing text selection. However, if you listen to
+ * `dragdown`, this default behavior is not called. You are responsible for calling it
+ * if you want it (you probably do).
+ *
+ * ### Why might you not want to call `preventDefault`?
+ *
+ * You might want it if you want to allow text selection on element
+ * within the drag element. Typically these are input elements.
+ *
+ * $(".handles").delegate("dragdown", function(ev, drag){
+ * if(ev.target.nodeName === "input"){
+ * drag.cancel();
+ * } else {
+ * ev.preventDefault();
+ * }
+ * })
+ */
+ 'dragdown',
+ /**
+ * @attribute draginit
+ * @parent jQuery.event.drag
+ *
+ * `draginit` is triggered when the drag motion starts. Use it to customize the drag behavior
+ * using the [jQuery.Drag] instance passed as the second parameter:
+ *
+ * $(".draggable").on('draginit', function(ev, drag) {
+ * // Only allow vertical drags
+ * drag.vertical();
+ * // Create a draggable copy of the element
+ * drag.ghost();
+ * });
+ */
+ 'draginit',
+ /**
+ * @attribute dragover
+ * @parent jQuery.event.drag
+ *
+ * `dragover` is triggered when a drag is over a [jQuery.event.drop drop element].
+ * The event handler gets an instance of [jQuery.Drag] passed as the second
+ * parameter and an instance of [jQuery.Drop] passed as the third argument:
+ *
+ * $('.draggable').on('dragover', function(ev, drag, drop) {
+ * // Add the drop-here class indicating that the drag
+ * // can be dropped here
+ * drag.element.addClass('drop-here');
+ * });
+ */
+ 'dragover',
+ /**
+ * @attribute dragmove
+ * @parent jQuery.event.drag
+ *
+ * `dragmove` is triggered when the drag element moves (similar to a mousemove).
+ * The event handler gets an instance of [jQuery.Drag] passed as the second
+ * parameter.
+ * Use [jQuery.Drag::location] to determine the current position
+ * as a [jQuery.Vector vector].
+ *
+ * For example, `dragmove` can be used to create a draggable element to resize
+ * a container:
+ *
+ * $('.resizer').on('dragmove', function(ev, drag) {
+ * $('#container').width(drag.location.x())
+ * .height(drag.location.y());
+ * });
+ */
+ 'dragmove',
+ /**
+ * @attribute dragout
+ * @parent jQuery.event.drag
+ *
+ * `dragout` is called when the drag leaves a drop point.
+ * The event handler gets an instance of [jQuery.Drag] passed as the second
+ * parameter.
+ *
+ * $('.draggable').on('dragout', function(ev, drag) {
+ * // Remove the drop-here class
+ * // (e.g. crossing the drag element out indicating that it
+ * // can't be dropped here
+ * drag.element.removeClass('drop-here');
+ * });
+ */
+ 'dragout',
+ /**
+ * @attribute dragend
+ * @parent jQuery.event.drag
+ *
+ * `dragend` is called when the drag operation is completed.
+ * The event handler gets an instance of [jQuery.Drag] passed as the second
+ * parameter.
+ *
+ * $('.draggable').on('dragend', function(ev, drag)
+ * // Calculation on whether revert should be invoked, alterations based on position of the end event
+ * });
+ */
+ 'dragend',
+ /**
+ * @attribute dragcleanup
+ * @parent jQuery.event.drag
+ *
+ * `dragcleanup` is called after dragend and revert (if applied)
+ * The event handler gets an instance of [jQuery.Drag] passed as the second
+ * parameter.
+ *
+ * $('.draggable').on('dragcleanup', function(ev, drag)
+ * // cleanup
+ * });
+ */
+ 'dragcleanup'], startEvent, function( e ) {
+ $.Drag.mousedown.call($.Drag, e, this);
+ });
+
+ return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/event/drag/limit.js b/dist/event/drag/limit.js
new file mode 100644
index 00000000..b24eaa1a
--- /dev/null
+++ b/dist/event/drag/limit.js
@@ -0,0 +1,76 @@
+(function( $ ) {
+
+
+ $.Drag.prototype
+ /**
+ * @function limit
+ * @plugin jquery/event/drag/limit
+ * @download http://jmvcsite.heroku.com/pluginify?plugins[]=jquery/event/event/drag/limit/limit.js
+ * `drag.limit(container, [center])` limits a drag to a containing element.
+ *
+ * $("#todos").on(".todo","draginit", function( ev, drag ) {
+ * drag.limit($("#todos").parent())
+ * })
+ *
+ * @param {jQuery} container the jQuery-wrapped container element you do not want the drag element to escape.
+ * @param {String} [center] can set the limit to the center of the object. Can be
+ * 'x', 'y' or 'both'. By default it will keep the outer edges of the moving element within the
+ * container element. If you provide x, it will keep the horizontal center of the moving element
+ * within the container element. If you provide y, it will keep the vertical center of the moving
+ * element within the container element. If you provide both, it will keep the center of the
+ * moving element within the containing element.
+ * @return {drag} returns the drag for chaining.
+ */
+ .limit = function( container, center ) {
+ //on draws ... make sure this happens
+ var styles = container.styles('borderTopWidth', 'paddingTop', 'borderLeftWidth', 'paddingLeft'),
+ paddingBorder = new $.Vector(
+ parseInt(styles.borderLeftWidth, 10) + parseInt(styles.paddingLeft, 10) || 0, parseInt(styles.borderTopWidth, 10) + parseInt(styles.paddingTop, 10) || 0);
+
+ this._limit = {
+ offset: container.offsetv().plus(paddingBorder),
+ size: container.dimensionsv(),
+ center : center === true ? 'both' : center
+ };
+ return this;
+ };
+
+ var oldPosition = $.Drag.prototype.position;
+ $.Drag.prototype.position = function( offsetPositionv ) {
+ //adjust required_css_position accordingly
+ if ( this._limit ) {
+ var limit = this._limit,
+ center = limit.center && limit.center.toLowerCase(),
+ movingSize = this.movingElement.dimensionsv('outer'),
+ halfHeight = center && center != 'x' ? movingSize.height() / 2 : 0,
+ halfWidth = center && center != 'y' ? movingSize.width() / 2 : 0,
+ lot = limit.offset.top(),
+ lof = limit.offset.left(),
+ height = limit.size.height(),
+ width = limit.size.width();
+
+ //check if we are out of bounds ...
+ //above
+ if ( offsetPositionv.top()+halfHeight < lot ) {
+ offsetPositionv.top(lot - halfHeight);
+ }
+ //below
+ if ( offsetPositionv.top() + movingSize.height() - halfHeight > lot + height ) {
+ offsetPositionv.top(lot + height - movingSize.height() + halfHeight);
+ }
+ //left
+ if ( offsetPositionv.left()+halfWidth < lof ) {
+ offsetPositionv.left(lof - halfWidth);
+ }
+ //right
+ if ( offsetPositionv.left() + movingSize.width() -halfWidth > lof + width ) {
+ offsetPositionv.left(lof + width - movingSize.left()+halfWidth);
+ }
+ }
+
+ oldPosition.call(this, offsetPositionv);
+ };
+
+ return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/event/drag/scroll.js b/dist/event/drag/scroll.js
new file mode 100644
index 00000000..f0669449
--- /dev/null
+++ b/dist/event/drag/scroll.js
@@ -0,0 +1,153 @@
+(function($) { //needs drop to determine if respondable
+
+/**
+ * @add jQuery.Drag.prototype
+ */
+$.Drag.prototype.
+ /**
+ * @plugin jquery/event/drag/scroll
+ * @download http://jmvcsite.heroku.com/pluginify?plugins[]=jquery/event/drag/scroll/scroll.js
+ *
+ * `drag.scrolls(elements, [options])` scroll elements with
+ * a scroll bar as the drag moves to borders.
+ *
+ * The following sets up the drag motions to scroll `#todos` and the window. Scrolling will
+ * start 50px away from a boundary and the speed will increase to 50px of scroll every 15ms.
+ *
+ * $('#todos').on(".todo","draginit", function(ev, drag){
+ * drag.scrolls($('#todos').add(window), {
+ * distance : 50,
+ * delta : function(diff) { return (50 - diff) / 2},
+ * direction : "y"
+ * })
+ * })
+ *
+ * @param {jQuery} elements an array of elements to scroll. The window can be in this array.
+ * @param {Object} [options] changes the default settings.
+ *
+ * - `distance` {number} 30 - how many pixels away from a boundry where we start scrolling
+ * - `delta(diff)` {Function} - returns how far we should scroll. It is passed how many pixels the cursor is
+ * from the boundry.
+ * - `direction` {String} - direction scrolling should happen. "xy" is the default.
+ */
+ scrolls = function(elements, options){
+ var elements = $(elements);
+
+ for(var i = 0 ; i < elements.length; i++){
+ this.constructor.responder._elements.push( elements.eq(i).data("_dropData", new $.Scrollable(elements[i], options) )[0] )
+ }
+ },
+
+$.Scrollable = function(element, options){
+ this.element = jQuery(element);
+ this.options = $.extend({
+ // when we should start scrolling
+ distance : 30,
+ // how far we should move
+ delta : function(diff, distance){
+ return (distance - diff) / 2;
+ },
+ direction: "xy"
+ }, options);
+ this.x = this.options.direction.indexOf("x") != -1;
+ this.y = this.options.direction.indexOf("y") != -1;
+}
+$.extend($.Scrollable.prototype,{
+ init: function( element ) {
+ this.element = jQuery(element);
+ },
+ callHandlers: function( method, el, ev, drag ) {
+ this[method](el || this.element[0], ev, this, drag)
+ },
+ dropover: function() {
+
+ },
+ dropon: function() {
+ this.clear_timeout();
+ },
+ dropout: function() {
+ this.clear_timeout();
+ },
+ dropinit: function() {
+
+ },
+ dropend: function() {},
+ clear_timeout: function() {
+ if(this.interval){
+ clearTimeout(this.interval)
+ this.interval = null;
+ }
+ },
+ distance: function( diff ) {
+ return (30 - diff) / 2;
+ },
+ dropmove: function( el, ev, drop, drag ) {
+
+ //if we were about to call a move, clear it.
+ this.clear_timeout();
+
+ //position of the mouse
+ var mouse = ev.vector(),
+
+ //get the object we are going to get the boundries of
+ location_object = $(el == document.documentElement ? window : el),
+
+ //get the dimension and location of that object
+ dimensions = location_object.dimensionsv('outer'),
+ position = location_object.offsetv(),
+
+ //how close our mouse is to the boundries
+ bottom = position.y()+dimensions.y() - mouse.y(),
+ top = mouse.y() - position.y(),
+ right = position.x()+dimensions.x() - mouse.x(),
+ left = mouse.x() - position.x(),
+
+ //how far we should scroll
+ dx =0, dy =0,
+ distance = this.options.distance;
+
+ //check if we should scroll
+ if(bottom < distance && this.y) {
+ dy = this.options.delta(bottom,distance);
+ } else if(top < distance && this.y) {
+ dy = -this.options.delta(top,distance);
+ }
+
+ if(right < distance && this.options && this.x) {
+ dx = this.options.delta(right,distance);
+ } else if(left < distance && this.x) {
+ dx = -this.options.delta(left,distance);
+ }
+
+ //if we should scroll
+ if(dx || dy){
+ //set a timeout that will create a mousemove on that object
+ var self = this;
+ this.interval = setTimeout( function(){
+ self.move($(el), drag.movingElement, dx, dy, ev, ev.clientX, ev.clientY, ev.screenX, ev.screenY)
+ },15)
+ }
+ },
+ /**
+ * Scrolls an element then calls mouse a mousemove in the same location.
+ * @hide
+ */
+ move: function( scroll_element, drag_element, dx, dy, ev/*, x,y,sx, sy*/ ) {
+ scroll_element.scrollTop( scroll_element.scrollTop() + dy);
+ scroll_element.scrollLeft(scroll_element.scrollLeft() + dx);
+
+ drag_element.trigger(
+ $.event.fix({type: "mousemove",
+ clientX: ev.clientX,
+ clientY: ev.clientY,
+ screenX: ev.screenX,
+ screenY: ev.screenY,
+ pageX: ev.pageX,
+ pageY: ev.pageY}))
+ //drag_element.synthetic('mousemove',{clientX: x, clientY: y, screenX: sx, screenY: sy})
+ }
+})
+
+return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/event/drag/step.js b/dist/event/drag/step.js
new file mode 100644
index 00000000..37504c94
--- /dev/null
+++ b/dist/event/drag/step.js
@@ -0,0 +1,81 @@
+(function( $ ) {
+ var round = function( x, m ) {
+ return Math.round(x / m) * m;
+ }
+
+ $.Drag.prototype.
+ /**
+ * @function step
+ * @plugin jquery/event/drag/step
+ * @download http://jmvcsite.heroku.com/pluginify?plugins[]=jquery/event/drag/step/step.js
+ * makes the drag move in steps of amount pixels.
+ *
+ * drag.step({x: 5}, $('foo'), "xy")
+ *
+ * ## Demo
+ *
+ * @demo jquery/event/drag/step/step.html
+ *
+ * @param {number|Object} amount make the drag move the amount in pixels from the top-left of container.
+ *
+ * If the amount is a `number`, the drag will move step-wise that number pixels in both
+ * dimensions. If it's an object like `{x: 20, y: 10}` the drag will move in steps 20px from
+ * left to right and 10px up and down.
+ *
+ * @param {jQuery} [container] the container to move in reference to. If not provided, the document is used.
+ * @param {String} [center] Indicates how to position the drag element in relationship to the container.
+ *
+ * - If nothing is provided, places the top left corner of the drag element at
+ * 'amount' intervals from the top left corner of the container.
+ * - If 'x' is provided, it centers the element horizontally on the top-left corner.
+ * - If 'y' is provided, it centers the element vertically on the top-left corner of the container.
+ * - If 'xy' is provided, it centers the element on the top-left corner of the container.
+ *
+ * @return {jQuery.Drag} the drag object for chaining.
+ */
+ step = function( amount, container, center ) {
+ //on draws ... make sure this happens
+ if ( typeof amount == 'number' ) {
+ amount = {
+ x: amount,
+ y: amount
+ }
+ }
+ container = container || $(document.body);
+ this._step = amount;
+
+ var styles = container.styles("borderTopWidth", "paddingTop", "borderLeftWidth", "paddingLeft");
+ var top = parseInt(styles.borderTopWidth) + parseInt(styles.paddingTop),
+ left = parseInt(styles.borderLeftWidth) + parseInt(styles.paddingLeft);
+
+ this._step.offset = container.offsetv().plus(left, top);
+ this._step.center = center;
+ return this;
+ };
+
+ (function() {
+ var oldPosition = $.Drag.prototype.position;
+ $.Drag.prototype.position = function( offsetPositionv ) {
+ //adjust required_css_position accordingly
+ if ( this._step ) {
+ var step = this._step,
+ center = step.center && step.center.toLowerCase(),
+ movingSize = this.movingElement.dimensionsv('outer'),
+ lot = step.offset.top()- (center && center != 'x' ? movingSize.height() / 2 : 0),
+ lof = step.offset.left() - (center && center != 'y' ? movingSize.width() / 2 : 0);
+
+ if ( this._step.x ) {
+ offsetPositionv.left(Math.round(lof + round(offsetPositionv.left() - lof, this._step.x)))
+ }
+ if ( this._step.y ) {
+ offsetPositionv.top(Math.round(lot + round(offsetPositionv.top() - lot, this._step.y)))
+ }
+ }
+
+ oldPosition.call(this, offsetPositionv)
+ }
+ })();
+
+ return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/event/drop.js b/dist/event/drop.js
new file mode 100644
index 00000000..e6a78c8f
--- /dev/null
+++ b/dist/event/drop.js
@@ -0,0 +1,456 @@
+(function($){
+ var event = $.event;
+ /**
+ * @add jQuery.event.special
+ */
+ var eventNames = [
+ /**
+ * @attribute dropover
+ * @parent jQuery.event.drop
+ *
+ * `dropover` is triggered when a [jQuery.event.drag drag] is first moved onto this
+ * drop element.
+ * The event handler gets an instance of [jQuery.Drag] passed as the second and a
+ * [jQuery.Drop] as the third parameter.
+ * This event can be used to highlight the element when a drag is moved over it:
+ *
+ * $('.droparea').on('dropover', function(ev, drop, drag) {
+ * $(this).addClass('highlight');
+ * });
+ */
+ "dropover",
+ /**
+ * @attribute dropon
+ * @parent jQuery.event.drop
+ *
+ * `dropon` is triggered when a drag is dropped on a drop element.
+ * The event handler gets an instance of [jQuery.Drag] passed as the second and a
+ * [jQuery.Drop] as the third parameter.
+ *
+ * $('.droparea').on('dropon', function(ev, drop, drag) {
+ * $(this).html('Dropped: ' + drag.element.text());
+ * });
+ */
+ "dropon",
+ /**
+ * @attribute dropout
+ * @parent jQuery.event.drop
+ *
+ * `dropout` is called when a drag is moved out of this drop element.
+ * The event handler gets an instance of [jQuery.Drag] passed as the second and a
+ * [jQuery.Drop] as the third parameter.
+ *
+ * $('.droparea').on('dropover', function(ev, drop, drag) {
+ * // Remove the drop element highlight
+ * $(this).removeClass('highlight');
+ * });
+ */
+ "dropout",
+ /**
+ * @attribute dropinit
+ * @parent jQuery.event.drop
+ *
+ * `dropinit` is called when a drag motion starts and the drop elements are initialized.
+ * The event handler gets an instance of [jQuery.Drag] passed as the second and a
+ * [jQuery.Drop] as the third parameter.
+ * Calling [jQuery.Drop.prototype.cancel drop.cancel()] prevents the element from
+ * being dropped on:
+ *
+ * $('.droparea').on('dropover', function(ev, drop, drag) {
+ * if(drag.element.hasClass('not-me')) {
+ * drop.cancel();
+ * }
+ * });
+ */
+ "dropinit",
+ /**
+ * @attribute dropmove
+ * @parent jQuery.event.drop
+ *
+ * `dropmove` is triggered repeatedly when a drag is moved over a drop
+ * (similar to a mousemove).
+ * The event handler gets an instance of [jQuery.Drag] passed as the second and a
+ * [jQuery.Drop] as the third parameter.
+ *
+ * $('.droparea').on('dropmove', function(ev, drop, drag) {
+ * $(this).html(drag.location.x() + '/' + drag.location.y());
+ * });
+ */
+ "dropmove",
+ /**
+ * @attribute dropend
+ * @parent jQuery.event.drop
+ *
+ * `dropend` is called when the drag motion is done for this drop element.
+ * The event handler gets an instance of [jQuery.Drag] passed as the second and a
+ * [jQuery.Drop] as the third parameter.
+ *
+ *
+ * $('.droparea').on('dropend', function(ev, drop, drag) {
+ * // Remove the drop element highlight
+ * $(this).removeClass('highlight');
+ * });
+ */
+ "dropend"];
+
+ /**
+ * @class jQuery.Drop
+ * @parent jQuery.event.drop
+ * @plugin jquery/event/drop
+ * @download http://jmvcsite.heroku.com/pluginify?plugins[]=jquery/event/drop/drop.js
+ * @test jquery/event/drag/qunit.html
+ *
+ * The `jQuery.Drop` constructor is never called directly but an instance is passed to the
+ * to the `dropinit`, `dropover`, `dropmove`, `dropon`, and `dropend` event handlers as the
+ * third argument (the second will be the [jQuery.Drag]):
+ *
+ * $('#dropper').on('dropover', function(el, drop, drag) {
+ * // drop -> $.Drop
+ * // drag -> $.Drag
+ * });
+ */
+ $.Drop = function(callbacks, element){
+ $.extend(this,callbacks);
+ this.element = $(element);
+ }
+ // add the elements ...
+ $.each(eventNames, function(){
+ event.special[this] = {
+ add: function( handleObj ) {
+ //add this element to the compiles list
+ var el = $(this), current = (el.data("dropEventCount") || 0);
+ el.data("dropEventCount", current+1 )
+ if(current==0){
+ $.Drop.addElement(this);
+ }
+ },
+ remove: function() {
+ var el = $(this), current = (el.data("dropEventCount") || 0);
+ el.data("dropEventCount", current-1 )
+ if(current<=1){
+ $.Drop.removeElement(this);
+ }
+ }
+ }
+ });
+
+ $.extend($.Drop,{
+ /**
+ * @static
+ */
+ lowerName: "drop",
+ _rootElements: [], //elements that are listening for drops
+ _elements: $(), //elements that can be dropped on
+ last_active: [],
+ endName: "dropon",
+ // adds an element as a 'root' element
+ // this element might have events that we need to respond to
+ addElement: function( el ) {
+ // check other elements
+ for(var i =0; i < this._rootElements.length ; i++ ){
+ if(el ==this._rootElements[i]) return;
+ }
+ this._rootElements.push(el);
+ },
+ removeElement: function( el ) {
+ for(var i =0; i < this._rootElements.length ; i++ ){
+ if(el == this._rootElements[i]){
+ this._rootElements.splice(i,1)
+ return;
+ }
+ }
+ },
+ /**
+ * @hide
+ * For a list of affected drops, sorts them by which is deepest in the DOM first.
+ */
+ sortByDeepestChild: function( a, b ) {
+ // Use jQuery.compare to compare two elements
+ var compare = a.element.compare(b.element);
+ if(compare & 16 || compare & 4) return 1;
+ if(compare & 8 || compare & 2) return -1;
+ return 0;
+ },
+ /**
+ * @hide
+ * Tests if a drop is within the point.
+ */
+ isAffected: function( point, moveable, responder ) {
+ return ((responder.element != moveable.element) && (responder.element.within(point[0], point[1], responder._cache).length == 1));
+ },
+ /**
+ * @hide
+ * Calls dropout and sets last active to null
+ * @param {Object} drop
+ * @param {Object} drag
+ * @param {Object} event
+ */
+ deactivate: function( responder, mover, event ) {
+ mover.out(event, responder)
+ responder.callHandlers(this.lowerName+'out',responder.element[0], event, mover)
+ },
+ /**
+ * @hide
+ * Calls dropover
+ * @param {Object} drop
+ * @param {Object} drag
+ * @param {Object} event
+ */
+ activate: function( responder, mover, event ) { //this is where we should call over
+ mover.over(event, responder)
+ responder.callHandlers(this.lowerName+'over',responder.element[0], event, mover);
+ },
+ move: function( responder, mover, event ) {
+ responder.callHandlers(this.lowerName+'move',responder.element[0], event, mover)
+ },
+ /**
+ * `$.Drop.compile()` gets all elements that are droppable and adds them to a list.
+ *
+ * This should be called if and when new drops are added to the page
+ * during the motion of a single drag.
+ *
+ * This is called by default when a drag motion starts.
+ *
+ * ## Use
+ *
+ * After adding an element or drop, call compile.
+ *
+ * $("#midpoint").bind("dropover",function(){
+ * // when a drop hovers over midpoint,
+ * // make drop a drop.
+ * $("#drop").bind("dropover", function(){
+ *
+ * });
+ * $.Drop.compile();
+ * });
+ */
+ compile: function( event, drag ) {
+ // if we called compile w/o a current drag
+ if(!this.dragging && !drag){
+ return;
+ }else if(!this.dragging){
+ this.dragging = drag;
+ this.last_active = [];
+ }
+ var el,
+ drops,
+ selector,
+ dropResponders,
+ newEls = [],
+ dragging = this.dragging;
+
+ // go to each root element and look for drop elements
+ for(var i=0; i < this._rootElements.length; i++){ //for each element
+ el = this._rootElements[i]
+
+ // gets something like {"": ["dropinit"], ".foo" : ["dropover","dropmove"] }
+ var drops = $.event.findBySelector(el, eventNames)
+
+ // get drop elements by selector
+ for(selector in drops){
+ dropResponders = selector ? jQuery(selector, el) : [el];
+
+ // for each drop element
+ for(var e= 0; e < dropResponders.length; e++){
+
+ // add the callbacks to the element's Data
+ // there already might be data, so we merge it
+ if( this.addCallbacks(dropResponders[e], drops[selector], dragging) ){
+ newEls.push(dropResponders[e])
+ };
+ }
+ }
+ }
+ // once all callbacks are added, call init on everything ...
+ this.add(newEls, event, dragging)
+ },
+
+ // adds the drag callbacks object to the element or merges other callbacks ...
+ // returns true or false if the element is new ...
+ // onlyNew lets only new elements add themselves
+ addCallbacks : function(el, callbacks, onlyNew){
+ var origData = $.data(el,"_dropData");
+ if(!origData){
+ $.data(el,"_dropData", new $.Drop(callbacks, el));
+ return true;
+ }else if(!onlyNew){
+ var origCbs = origData;
+ // merge data
+ for(var eventName in callbacks){
+ origCbs[eventName] = origCbs[eventName] ?
+ origCbs[eventName].concat(callbacks[eventName]) :
+ callbacks[eventName];
+ }
+ return false;
+ }
+ },
+ // calls init on each element's drags.
+ // if its cancelled it's removed
+ // adds to the current elements ...
+ add: function( newEls, event, drag , dragging) {
+ var i = 0,
+ drop;
+
+ while(i < newEls.length){
+ drop = $.data(newEls[i],"_dropData");
+ drop.callHandlers(this.lowerName+'init', newEls[i], event, drag)
+ if(drop._canceled){
+ newEls.splice(i,1)
+ }else{
+ i++;
+ }
+ }
+ this._elements.push.apply(this._elements, newEls)
+ },
+ show: function( point, moveable, event ) {
+ var element = moveable.element;
+ if(!this._elements.length) return;
+
+ var respondable,
+ affected = [],
+ propagate = true,
+ i = 0,
+ j,
+ la,
+ toBeActivated,
+ aff,
+ oldLastActive = this.last_active,
+ responders = [],
+ self = this,
+ drag;
+
+ // what's still affected ... we can also move element out here
+ while( i < this._elements.length){
+ drag = $.data(this._elements[i],"_dropData");
+
+ if (!drag) {
+ this._elements.splice(i, 1)
+ }
+ else {
+ i++;
+ if (self.isAffected(point, moveable, drag)) {
+ affected.push(drag);
+ }
+ }
+ }
+
+ // we should only trigger on lowest children
+ affected.sort(this.sortByDeepestChild);
+ event.stopRespondPropagate = function(){
+ propagate = false;
+ }
+
+ toBeActivated = affected.slice();
+
+ // all these will be active
+ this.last_active = affected;
+
+ // deactivate everything in last_active that isn't active
+ for (j = 0; j < oldLastActive.length; j++) {
+ la = oldLastActive[j];
+ i = 0;
+ while((aff = toBeActivated[i])){
+ if(la == aff){
+ toBeActivated.splice(i,1);break;
+ }else{
+ i++;
+ }
+ }
+ if(!aff){
+ this.deactivate(la, moveable, event);
+ }
+ if(!propagate) return;
+ }
+ for(var i =0; i < toBeActivated.length; i++){
+ this.activate(toBeActivated[i], moveable, event);
+ if(!propagate) return;
+ }
+
+ // activate everything in affected that isn't in last_active
+ for (i = 0; i < affected.length; i++) {
+ this.move(affected[i], moveable, event);
+
+ if(!propagate) return;
+ }
+ },
+ end: function( event, moveable ) {
+ var la,
+ endName = this.lowerName+'end',
+ onEvent = $.Event(this.endName, event),
+ dropData;
+
+ // call dropon
+ // go through the actives ... if you are over one, call dropped on it
+ for(var i = 0; i < this.last_active.length; i++){
+ la = this.last_active[i]
+ if( this.isAffected(event.vector(), moveable, la) && la[this.endName]){
+ la.callHandlers(this.endName, null, onEvent, moveable);
+ }
+
+ if (onEvent.isPropagationStopped()) {
+ break;
+ }
+ }
+ // call dropend
+ for(var r =0; r< length; i++){
+ this[method][i].call(el || this.element[0], ev, this, drag)
+ }
+ },
+ /**
+ * `drop.cache(value)` sets the drop to cache positions of draggable elements.
+ * This should be called on `dropinit`. For example:
+ *
+ * $('#dropable').on('dropinit', function( el, ev, drop ) {
+ * drop.cache();
+ * });
+ *
+ * @param {Boolean} [value=true] Whether to cache drop elements or not.
+ */
+ cache: function( value ) {
+ this._cache = value != null ? value : true;
+ },
+ /**
+ * `drop.cancel()` prevents this drop from being dropped on.
+ *
+ * $('.droparea').on('dropover', function(ev, drop, drag) {
+ * if(drag.element.hasClass('not-me')) {
+ * drop.cancel();
+ * }
+ * });
+ */
+ cancel: function() {
+ this._canceled = true;
+ }
+ });
+
+ return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/event/fastfix.js b/dist/event/fastfix.js
new file mode 100644
index 00000000..467c723f
--- /dev/null
+++ b/dist/event/fastfix.js
@@ -0,0 +1,113 @@
+(function ($) {
+ // http://bitovi.com/blog/2012/04/faster-jquery-event-fix.html
+ // https://gist.github.com/2377196
+
+ // IE 8 has Object.defineProperty but it only defines DOM Nodes. According to
+ // http://kangax.github.com/es5-compat-table/#define-property-ie-note
+ // All browser that have Object.defineProperties also support Object.defineProperty properly
+ if(Object.defineProperties) {
+ var
+ // Use defineProperty on an object to set the value and return it
+ set = function (obj, prop, val) {
+ if(val !== undefined) {
+ Object.defineProperty(obj, prop, {
+ value : val
+ });
+ }
+ return val;
+ },
+ // special converters
+ special = {
+ pageX : function (original) {
+ if(!original) {
+ return;
+ }
+
+ var eventDoc = this.target.ownerDocument || document;
+ doc = eventDoc.documentElement;
+ body = eventDoc.body;
+ return original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+ },
+ pageY : function (original) {
+ if(!original) {
+ return;
+ }
+
+ var eventDoc = this.target.ownerDocument || document;
+ doc = eventDoc.documentElement;
+ body = eventDoc.body;
+ return original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
+ },
+ relatedTarget : function (original) {
+ if(!original) {
+ return;
+ }
+
+ return original.fromElement === this.target ? original.toElement : original.fromElement;
+ },
+ metaKey : function (originalEvent) {
+ if(!originalEvent) {
+ return;
+ }
+ return originalEvent.ctrlKey;
+ },
+ which : function (original) {
+ if(!original) {
+ return;
+ }
+
+ return original.charCode != null ? original.charCode : original.keyCode;
+ }
+ };
+
+ // Get all properties that should be mapped
+ $.each($.event.keyHooks.props.concat($.event.mouseHooks.props).concat($.event.props), function (i, prop) {
+ if (prop !== "target") {
+ (function () {
+ Object.defineProperty($.Event.prototype, prop, {
+ get : function () {
+ // get the original value, undefined when there is no original event
+ var originalValue = this.originalEvent && this.originalEvent[prop];
+ // overwrite getter lookup
+ return this['_' + prop] !== undefined ? this['_' + prop] : set(this, prop,
+ // if we have a special function and no value
+ special[prop] && originalValue === undefined ?
+ // call the special function
+ special[prop].call(this, this.originalEvent) :
+ // use the original value
+ originalValue)
+ },
+ set : function (newValue) {
+ // Set the property with underscore prefix
+ this['_' + prop] = newValue;
+ }
+ });
+ })();
+ }
+ });
+
+ $.event.fix = function (event) {
+ if (event[ $.expando ]) {
+ return event;
+ }
+ // Create a jQuery event with at minimum a target and type set
+ var originalEvent = event,
+ event = $.Event(originalEvent);
+ event.target = originalEvent.target;
+ // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
+ if (!event.target) {
+ event.target = originalEvent.srcElement || document;
+ }
+
+ // Target should not be a text node (#504, Safari)
+ if (event.target.nodeType === 3) {
+ event.target = event.target.parentNode;
+ }
+
+ return event;
+ }
+ }
+
+ return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/event/hover.js b/dist/event/hover.js
new file mode 100644
index 00000000..3e2fecae
--- /dev/null
+++ b/dist/event/hover.js
@@ -0,0 +1,291 @@
+(function ($) {
+ /**
+ * @class jQuery.Hover
+ * @plugin jquery/event/hover
+ * @download http://jmvcsite.heroku.com/pluginify?plugins[]=jquery/event/hover/hover.js
+ * @parent jQuery.event.hover
+ *
+ * Creates a new hover. The constructor should not be called directly.
+ *
+ * An instance of `$.Hover` is passed as the second argument to each
+ * [jQuery.event.hover] event handler:
+ *
+ * $('#menu').on("hoverinit", function(ev, hover) {
+ * // Set the hover distance to 20px
+ * hover.distance(20);
+ * });
+ */
+ $.Hover = function () {
+ this._delay = $.Hover.delay;
+ this._distance = $.Hover.distance;
+ this._leave = $.Hover.leave
+ };
+ /**
+ * @Static
+ */
+ $.extend($.Hover, {
+ /**
+ * @attribute delay
+ *
+ * `$.Hover.delay` is the delay (in milliseconds) after which the hover is
+ * activated by default.
+ *
+ * Set this value as a global default. The default is 100ms.
+ *
+ * // Set the global hover delay to 1 second
+ * $.Hover.delay = 1000;
+ */
+ delay : 100,
+ /**
+ * @attribute distance
+ *
+ * `$.Hover.distance` is the maximum distance (in pixels) that the mouse is allowed to
+ * travel within the time of [jQuery.Hover.delay] in order to activate a hover.
+ *
+ * Set this value as a global default. The default is 10px.
+ *
+ * // Set the global hover distance to 1 pixel
+ * $.Hover.distance = 1;
+ */
+ distance : 10,
+ leave : 0
+ })
+
+ /**
+ * @Prototype
+ */
+ $.extend($.Hover.prototype, {
+ /**
+ * `hover.delay(time)` sets the delay (in ms) for this hover.
+ * This method should only be used in [jQuery.event.hover.hoverinit hoverinit]:
+ *
+ * $('.hoverable').on('hoverinit', function(ev, hover) {
+ * // Set the delay to 500ms
+ * hover.delay(500);
+ * });
+ *
+ * @param {Number} delay the number of milliseconds used to determine a hover
+ * @return {$.Hover} The hover object
+ */
+ delay : function (delay) {
+ this._delay = delay;
+ return this;
+ },
+ /**
+ * `hover.distance(px) sets the maximum distance (in pixels) the mouse is allowed to travel in order to activate
+ * the hover. This method should only be used in [jQuery.event.hover.hoverinit hoverinit]:
+ *
+ * $('.hoverable').on('hoverinit', function(ev, hover) {
+ * // Set the distance to 1px
+ * hover.distance(1);
+ * });
+ *
+ * @param {Number} distance the max distance in pixels a mouse can move to be considered a hover
+ * @return {$.Hover} The hover object
+ */
+ distance : function (distance) {
+ this._distance = distance;
+ return this;
+ },
+ /**
+ * `hover.leave(delay)` sets a delay for how long the hover should stay active after the mouse left.
+ * This method should only be used in [jQuery.event.hover.hoverinit hoverinit]:
+ *
+ * $('.hoverable').on('hoverinit', function(ev, hover) {
+ * // Stay active for another second after the mouse left
+ * hover.leave(1000);
+ * });
+ *
+ * @param {Number} delay the number of milliseconds the hover should stay active after the mouse leaves
+ * @return {$.Hover} The hover object
+ */
+ leave : function (leave) {
+ this._leave = leave;
+ return this;
+ }
+ })
+ var event = $.event,
+ handle = event.handle,
+ onmouseenter = function (ev) {
+ // now start checking mousemoves to update location
+ var delegate = ev.delegateTarget || ev.currentTarget;
+ var selector = ev.handleObj.selector;
+ var pending = $.data(delegate,"_hover"+selector);
+ // prevents another mouseenter until current has run its course
+ if(pending) {
+ // Under some circumstances, mouseleave may never fire
+ // (e.g., the element is removed while hovered)
+ // so if we've entered another element, wait the leave time,
+ // then force it to release.
+ if(!pending.forcing) {
+ pending.forcing = true;
+ clearTimeout(pending.leaveTimer);
+ var leaveTime = pending.leaving ?
+ Math.max(0,pending.hover.leave - (new Date() - pending.leaving)) :
+ pending.hover.leave;
+ var self = this;
+
+ setTimeout(function() {
+ pending.callHoverLeave();
+ onmouseenter.call(self,ev);
+ },leaveTime);
+ }
+ return;
+ }
+ var loc = {
+ pageX : ev.pageX,
+ pageY : ev.pageY
+ },
+ // The current distance
+ dist = 0,
+ // Timer that checks for the distance travelled
+ timer,
+ enteredEl = this,
+ // If we are hovered
+ hovered = false,
+ // The previous event
+ lastEv = ev,
+ // The $.Hover instance passed to events
+ hover = new $.Hover(),
+ // timer if hover.leave has been called
+ leaveTimer,
+ // Callback for triggering hoverleave
+ callHoverLeave = function () {
+ $.each(event.find(delegate, ["hoverleave"], selector), function () {
+ this.call(enteredEl, ev, hover)
+ })
+ cleanUp();
+ },
+ mousemove = function (ev) {
+ clearTimeout(leaveTimer);
+ // Update the distance and location
+ dist += Math.pow(ev.pageX - loc.pageX, 2) + Math.pow(ev.pageY - loc.pageY, 2);
+ loc = {
+ pageX : ev.pageX,
+ pageY : ev.pageY
+ }
+ lastEv = ev
+ },
+ mouseleave = function (ev) {
+ clearTimeout(timer);
+ if (hovered) {
+ // go right away
+ if (hover._leave === 0) {
+ callHoverLeave();
+ } else {
+ clearTimeout(leaveTimer);
+ // leave the hover after the time set in hover.leave(time)
+ pending.leaving = new Date();
+ leaveTimer = pending.leaveTimer = setTimeout(function(){
+ callHoverLeave();
+ }, hover._leave)
+ }
+ } else {
+ cleanUp();
+ }
+ },
+ cleanUp = function () {
+ // Unbind all events and data
+ $(enteredEl).unbind("mouseleave", mouseleave)
+ $(enteredEl).unbind("mousemove", mousemove);
+ $.removeData(delegate, "_hover" + selector)
+ },
+ hoverenter = function() {
+ $.each(event.find(delegate, ["hoverenter"], selector), function () {
+ this.call(enteredEl, lastEv, hover)
+ })
+ hovered = true;
+ };
+ pending = {
+ callHoverLeave: callHoverLeave,
+ hover: hover
+ };
+ $.data(delegate,"_hover"+selector, pending);
+
+ // Bind the mousemove event
+ $(enteredEl).bind("mousemove", mousemove).bind("mouseleave", mouseleave);
+ // call hoverinit for each element with the hover instance
+ $.each(event.find(delegate, ["hoverinit"], selector), function () {
+ this.call(enteredEl, ev, hover)
+ })
+
+ if (hover._delay === 0) {
+ hoverenter();
+ } else {
+ timer = setTimeout(function () {
+ // check that we aren't moving around
+ if (dist < hover._distance && $(enteredEl).queue().length == 0) {
+ hoverenter();
+ return;
+ } else {
+ // Reset distance and timer
+ dist = 0;
+ timer = setTimeout(arguments.callee, hover._delay)
+ }
+ }, hover._delay);
+ }
+ };
+
+ /**
+ * @add jQuery.event.special
+ */
+// Attach events
+ event.setupHelper([
+ /**
+ * @attribute hoverinit
+ * @parent jQuery.event.hover
+ *
+ * `hoverinit` is called when a hover is about to start (on `mouseenter`). Listen for `hoverinit` events to configure
+ * [jQuery.Hover::delay delay] and [jQuery.Hover::distance distance]
+ * for this specific event:
+ *
+ * $(".option").on("hoverinit", function(ev, hover){
+ * //set the distance to 10px
+ * hover.distance(10);
+ * //set the delay to 200ms
+ * hover.delay(10);
+ * // End the hover one second after the mouse leaves
+ * hover.leave(1000);
+ * })
+ */
+ "hoverinit",
+ /**
+ * @attribute hoverenter
+ * @parent jQuery.event.hover
+ *
+ * `hoverenter` events are called when the mouses less than [jQuery.Hover.prototype.distance] pixels in
+ * [jQuery.Hover.prototype.delay delay] milliseconds.
+ *
+ * $(".option").on("hoverenter", function(ev, hover){
+ * $(this).addClass("hovering");
+ * })
+ */
+ "hoverenter",
+ /**
+ * @attribute hoverleave
+ * @parent jQuery.event.hover
+ *
+ * `hoverleave` is called when the mouse leaves an element that has been hovered.
+ *
+ * $(".option").on("hoverleave", function(ev, hover){
+ * $(this).removeClass("hovering");
+ * })
+ */
+ "hoverleave",
+ /**
+ * @attribute hovermove
+ * @parent jQuery.event.hover
+ *
+ * `hovermove` is called when a `mousemove` occurs on an element that has been hovered.
+ *
+ * $(".option").on("hovermove", function(ev, hover){
+ * // not sure why you would want to listen for this
+ * // but we provide it just in case
+ * })
+ */
+ "hovermove"], "mouseenter", onmouseenter)
+
+
+ return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/event/key.js b/dist/event/key.js
new file mode 100644
index 00000000..d0c4cf8c
--- /dev/null
+++ b/dist/event/key.js
@@ -0,0 +1,174 @@
+(function($){
+
+ // copied from jQuery 1.8.3
+ var uaMatch = function( ua ) {
+ ua = ua.toLowerCase();
+
+ var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
+ /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
+ /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
+ /(msie) ([\w.]+)/.exec( ua ) ||
+ ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
+ [];
+
+ return {
+ browser: match[ 1 ] || "",
+ version: match[ 2 ] || "0"
+ };
+ }
+
+ var keymap = {},
+ reverseKeyMap = {},
+ currentBrowser = uaMatch(navigator.userAgent).browser;
+
+ /**
+ * @hide
+ * @parent jQuery.Event.prototype.key
+ *
+ * Allows you to set alternate key maps or overwrite existing key codes.
+ * For example::
+ *
+ * $.event.key({"~" : 177});
+ *
+ * @param {Object} map A map of character - keycode pairs.
+ */
+ $.event.key = function(browser, map){
+ if(browser === undefined) {
+ return keymap;
+ }
+
+ if(map === undefined) {
+ map = browser;
+ browser = currentBrowser;
+ }
+
+ // extend the keymap
+ if(!keymap[browser]) {
+ keymap[browser] = {};
+ }
+ $.extend(keymap[browser], map);
+ // and also update the reverse keymap
+ if(!reverseKeyMap[browser]) {
+ reverseKeyMap[browser] = {};
+ }
+ for(var name in map){
+ reverseKeyMap[browser][map[name]] = name;
+ }
+ };
+
+ $.event.key({
+ // backspace
+ '\b':'8',
+
+ // tab
+ '\t':'9',
+
+ // enter
+ '\r':'13',
+
+ // special
+ 'shift':'16','ctrl':'17','alt':'18',
+
+ // others
+ 'pause-break':'19',
+ 'caps':'20',
+ 'escape':'27',
+ 'num-lock':'144',
+ 'scroll-lock':'145',
+ 'print' : '44',
+
+ // navigation
+ 'page-up':'33','page-down':'34','end':'35','home':'36',
+ 'left':'37','up':'38','right':'39','down':'40','insert':'45','delete':'46',
+
+ // normal characters
+ ' ':'32',
+ '0':'48','1':'49','2':'50','3':'51','4':'52','5':'53','6':'54','7':'55','8':'56','9':'57',
+ 'a':'65','b':'66','c':'67','d':'68','e':'69','f':'70','g':'71','h':'72','i':'73','j':'74','k':'75','l':'76','m':'77',
+ 'n':'78','o':'79','p':'80','q':'81','r':'82','s':'83','t':'84','u':'85','v':'86','w':'87','x':'88','y':'89','z':'90',
+ // normal-characters, numpad
+ 'num0':'96','num1':'97','num2':'98','num3':'99','num4':'100','num5':'101','num6':'102','num7':'103','num8':'104','num9':'105',
+ '*':'106','+':'107','-':'109','.':'110',
+ // normal-characters, others
+ '/':'111',
+ ';':'186',
+ '=':'187',
+ ',':'188',
+ '-':'189',
+ '.':'190',
+ '/':'191',
+ '`':'192',
+ '[':'219',
+ '\\':'220',
+ ']':'221',
+ "'":'222',
+
+ // ignore these, you shouldn't use them
+ 'left window key':'91','right window key':'92','select key':'93',
+
+
+ 'f1':'112','f2':'113','f3':'114','f4':'115','f5':'116','f6':'117',
+ 'f7':'118','f8':'119','f9':'120','f10':'121','f11':'122','f12':'123'
+ });
+
+ /**
+ * @parent jQuery.event.key
+ * @plugin jquery/event/key
+ * @function jQuery.Event.prototype.keyName
+ *
+ * Returns a string representation of the key pressed:
+ *
+ * $("input").on('keypress', function(ev){
+ * if(ev.keyName() == 'ctrl') {
+ * $(this).addClass('highlight');
+ * }
+ * });
+ *
+ * The key names mapped by default can be found in the [jQuery.event.key jQuery.event.key overview].
+ *
+ * @return {String} The string representation of of the key pressed.
+ */
+ $.Event.prototype.keyName = function(){
+ var event = this,
+ test = /\w/,
+ // It can be either keyCode or charCode.
+ // Look both cases up in the reverse key map and converted to a string
+ key_Key = reverseKeyMap[currentBrowser][(event.keyCode || event.which)+""],
+ char_Key = String.fromCharCode(event.keyCode || event.which),
+ key_Char = event.charCode && reverseKeyMap[currentBrowser][event.charCode+""],
+ char_Char = event.charCode && String.fromCharCode(event.charCode);
+
+ if( char_Char && test.test(char_Char) ) {
+ // string representation of event.charCode
+ return char_Char.toLowerCase()
+ }
+ if( key_Char && test.test(key_Char) ) {
+ // reverseKeyMap representation of event.charCode
+ return char_Char.toLowerCase()
+ }
+ if( char_Key && test.test(char_Key) ) {
+ // string representation of event.keyCode
+ return char_Key.toLowerCase()
+ }
+ if( key_Key && test.test(key_Key) ) {
+ // reverseKeyMap representation of event.keyCode
+ return key_Key.toLowerCase()
+ }
+
+ if (event.type == 'keypress'){
+ // keypress doesn't capture everything
+ return event.keyCode ? String.fromCharCode(event.keyCode) : String.fromCharCode(event.which)
+ }
+
+ if (!event.keyCode && event.which) {
+ // event.which
+ return String.fromCharCode(event.which)
+ }
+
+ // default
+ return reverseKeyMap[currentBrowser][event.keyCode+""]
+ }
+
+return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/event/livehack.js b/dist/event/livehack.js
new file mode 100644
index 00000000..f204cff7
--- /dev/null
+++ b/dist/event/livehack.js
@@ -0,0 +1,178 @@
+(function($) {
+
+ var event = $.event,
+
+ //helper that finds handlers by type and calls back a function, this is basically handle
+ // events - the events object
+ // types - an array of event types to look for
+ // callback(type, handlerFunc, selector) - a callback
+ // selector - an optional selector to filter with, if there, matches by selector
+ // if null, matches anything, otherwise, matches with no selector
+ findHelper = function( events, types, callback, selector ) {
+ var t, type, typeHandlers, all, h, handle,
+ namespaces, namespace,
+ match;
+ for ( t = 0; t < types.length; t++ ) {
+ type = types[t];
+ all = type.indexOf(".") < 0;
+ if (!all ) {
+ namespaces = type.split(".");
+ type = namespaces.shift();
+ namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
+ }
+ typeHandlers = (events[type] || []).slice(0);
+
+ for ( h = 0; h < typeHandlers.length; h++ ) {
+ handle = typeHandlers[h];
+
+ match = (all || namespace.test(handle.namespace));
+
+ if(match){
+ if(selector){
+ if (handle.selector === selector ) {
+ callback(type, handle.origHandler || handle.handler);
+ }
+ } else if (selector === null){
+ callback(type, handle.origHandler || handle.handler, handle.selector);
+ }
+ else if (!handle.selector ) {
+ callback(type, handle.origHandler || handle.handler);
+
+ }
+ }
+
+
+ }
+ }
+ };
+
+ /**
+ * Finds event handlers of a given type on an element.
+ * @param {HTMLElement} el
+ * @param {Array} types an array of event names
+ * @param {String} [selector] optional selector
+ * @return {Array} an array of event handlers
+ */
+ event.find = function( el, types, selector ) {
+ var events = ( $._data(el) || {} ).events,
+ handlers = [],
+ t, liver, live;
+
+ if (!events ) {
+ return handlers;
+ }
+ findHelper(events, types, function( type, handler ) {
+ handlers.push(handler);
+ }, selector);
+ return handlers;
+ };
+ /**
+ * Finds all events. Group by selector.
+ * @param {HTMLElement} el the element
+ * @param {Array} types event types
+ */
+ event.findBySelector = function( el, types ) {
+ var events = $._data(el).events,
+ selectors = {},
+ //adds a handler for a given selector and event
+ add = function( selector, event, handler ) {
+ var select = selectors[selector] || (selectors[selector] = {}),
+ events = select[event] || (select[event] = []);
+ events.push(handler);
+ };
+
+ if (!events ) {
+ return selectors;
+ }
+ //first check live:
+ /*$.each(events.live || [], function( i, live ) {
+ if ( $.inArray(live.origType, types) !== -1 ) {
+ add(live.selector, live.origType, live.origHandler || live.handler);
+ }
+ });*/
+ //then check straight binds
+ findHelper(events, types, function( type, handler, selector ) {
+ add(selector || "", type, handler);
+ }, null);
+
+ return selectors;
+ };
+ event.supportTouch = "ontouchend" in document;
+
+ $.fn.respondsTo = function( events ) {
+ if (!this.length ) {
+ return false;
+ } else {
+ //add default ?
+ return event.find(this[0], $.isArray(events) ? events : [events]).length > 0;
+ }
+ };
+ $.fn.triggerHandled = function( event, data ) {
+ event = (typeof event == "string" ? $.Event(event) : event);
+ this.trigger(event, data);
+ return event.handled;
+ };
+ /**
+ * Only attaches one event handler for all types ...
+ * @param {Array} types llist of types that will delegate here
+ * @param {Object} startingEvent the first event to start listening to
+ * @param {Object} onFirst a function to call
+ */
+ event.setupHelper = function( types, startingEvent, onFirst ) {
+ if (!onFirst ) {
+ onFirst = startingEvent;
+ startingEvent = null;
+ }
+ var add = function( handleObj ) {
+ var bySelector,
+ selector = handleObj.selector || "",
+ namespace = handleObj.namespace ? '.'+handleObj.namespace : '';
+
+ if ( selector ) {
+ bySelector = event.find(this, types, selector);
+ if (!bySelector.length ) {
+ $(this).delegate(selector, startingEvent + namespace, onFirst);
+ }
+ }
+ else {
+ //var bySelector = event.find(this, types, selector);
+ if (!event.find(this, types, selector).length ) {
+ event.add(this, startingEvent + namespace, onFirst, {
+ selector: selector,
+ delegate: this
+ });
+ }
+
+ }
+
+ },
+ remove = function( handleObj ) {
+ var bySelector, selector = handleObj.selector || "";
+ if ( selector ) {
+ bySelector = event.find(this, types, selector);
+ if (!bySelector.length ) {
+ $(this).undelegate(selector, startingEvent, onFirst);
+ }
+ }
+ else {
+ if (!event.find(this, types, selector).length ) {
+ event.remove(this, startingEvent, onFirst, {
+ selector: selector,
+ delegate: this
+ });
+ }
+ }
+ };
+ $.each(types, function() {
+ event.special[this] = {
+ add: add,
+ remove: remove,
+ setup: function() {},
+ teardown: function() {}
+ };
+ });
+ };
+
+ return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/event/pause.js b/dist/event/pause.js
new file mode 100644
index 00000000..0e54c30f
--- /dev/null
+++ b/dist/event/pause.js
@@ -0,0 +1,89 @@
+(function($) {
+
+
+var current,
+ rnamespaces = /\.(.*)$/,
+ returnFalse = function(){return false},
+ returnTrue = function(){return true};
+
+$.Event.prototype.isPaused = returnFalse
+
+/**
+ * @function jQuery.Event.prototype.pause
+ * @parent jQuery.event.pause
+ *
+ * `event.paused()` pauses an event (to be resumed later):
+ *
+ * $('.tab').on('show', function(ev) {
+ * ev.pause();
+ * // Resume the event after 1 second
+ * setTimeout(function() {
+ * ev.resume();
+ * }, 1000);
+ * });
+ */
+$.Event.prototype.pause = function(){
+ // stop the event from continuing temporarily
+ // keep the current state of the event ...
+ this.pausedState = {
+ isDefaultPrevented : this.isDefaultPrevented() ?
+ returnTrue : returnFalse,
+ isPropagationStopped : this.isPropagationStopped() ?
+ returnTrue : returnFalse
+ };
+
+ this.stopImmediatePropagation();
+ this.preventDefault();
+ this.isPaused = returnTrue;
+};
+
+/**
+ * @function jQuery.Event.prototype.resume
+ * @parent jQuery.event.pause
+ *
+ * `event.resume()` resumes a paused event:
+ *
+ * $('.tab').on('show', function(ev) {
+ * ev.pause();
+ * // Resume the event after 1 second
+ * setTimeout(function() {
+ * ev.resume();
+ * }, 1000);
+ * });
+ */
+$.Event.prototype.resume = function(){
+ // temporarily remove all event handlers of this type
+ var handleObj = this.handleObj,
+ currentTarget = this.currentTarget;
+ // temporarily overwrite special handle
+ var origType = $.event.special[ handleObj.origType ],
+ origHandle = origType && origType.handle;
+
+ if(!origType){
+ $.event.special[ handleObj.origType ] = {};
+ }
+ $.event.special[ handleObj.origType ].handle = function(ev){
+ // remove this once we have passed the handleObj
+ if(ev.handleObj === handleObj && ev.currentTarget === currentTarget){
+ if(!origType){
+ delete $.event.special[ handleObj.origType ];
+ } else {
+ $.event.special[ handleObj.origType ].handle = origHandle;
+ }
+ }
+ }
+ delete this.pausedState;
+ // reset stuff
+ this.isPaused = this.isImmediatePropagationStopped = returnFalse;
+
+ if(!this.isPropagationStopped()){
+ // fire the event again, no events will get fired until
+ // same currentTarget / handler
+ $.event.trigger(this, [], this.target);
+ }
+
+};
+
+return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/event/resize.js b/dist/event/resize.js
new file mode 100644
index 00000000..7a3c4cc8
--- /dev/null
+++ b/dist/event/resize.js
@@ -0,0 +1,45 @@
+(function($) {
+ var
+ // bind on the window window resizes to happen
+ win = $(window),
+ windowWidth = 0,
+ windowHeight = 0,
+ timer;
+
+ $(function() {
+ windowWidth = win.width();
+ windowHeight = win.height();
+ });
+
+ $.event.reverse('resize', {
+ handler : function(ev, data) {
+ var isWindow = this === window;
+
+ // if we are the window and a real resize has happened
+ // then we check if the dimensions actually changed
+ // if they did, we will wait a brief timeout and
+ // trigger resize on the window
+ // this is for IE, to prevent window resize 'infinate' loop issues
+ if ( isWindow && ev.originalEvent ) {
+ var width = win.width(),
+ height = win.height();
+
+
+ if ((width != windowWidth || height != windowHeight)) {
+ //update the new dimensions
+ windowWidth = width;
+ windowHeight = height;
+ clearTimeout(timer)
+ timer = setTimeout(function() {
+ win.trigger("resize");
+ }, 1);
+
+ }
+ return true;
+ }
+ }
+ });
+
+ return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/event/reverse.js b/dist/event/reverse.js
new file mode 100644
index 00000000..e2b51872
--- /dev/null
+++ b/dist/event/reverse.js
@@ -0,0 +1,92 @@
+(function($) {
+ $.event.reverse = function(name, attributes) {
+ var bound = $(),
+ count = 0,
+ dispatch = $.event.handle || $.event.dispatch;
+
+ $.event.special[name] = {
+ setup: function() {
+ // add and sort the resizers array
+ // don't add window because it can't be compared easily
+ if ( this !== window ) {
+ bound.push(this);
+ $.unique(bound);
+ }
+ // returns false if the window
+ return this !== window;
+ },
+ teardown: function() {
+ // we shouldn't have to sort
+ bound = bound.not(this);
+ // returns false if the window
+ return this !== window;
+ },
+ add: function( handleObj ) {
+ var origHandler = handleObj.handler;
+ handleObj.origHandler = origHandler;
+
+ handleObj.handler = function( ev, data ) {
+ var isWindow = this === window;
+ if(attributes && attributes.handler) {
+ var result = attributes.handler.apply(this, arguments);
+ if(result === true) {
+ return;
+ }
+ }
+
+ // if this is the first handler for this event ...
+ if ( count === 0 ) {
+ // prevent others from doing what we are about to do
+ count++;
+ var where = data === false ? ev.target : this
+
+ // trigger all this element's handlers
+ dispatch.call(where, ev, data);
+ if ( ev.isPropagationStopped() ) {
+ count--;
+ return;
+ }
+
+ // get all other elements within this element that listen to move
+ // and trigger their resize events
+ var index = bound.index(this),
+ length = bound.length,
+ child, sub;
+
+ // if index == -1 it's the window
+ while (++index < length && (child = bound[index]) && (isWindow || $.contains(where, child)) ) {
+
+ // call the event
+ dispatch.call(child, ev, data);
+
+ if ( ev.isPropagationStopped() ) {
+ // move index until the item is not in the current child
+ while (++index < length && (sub = bound[index]) ) {
+ if (!$.contains(child, sub) ) {
+ // set index back one
+ index--;
+ break
+ }
+ }
+ }
+ }
+
+ // prevent others from responding
+ ev.stopImmediatePropagation();
+ count--;
+ } else {
+ handleObj.origHandler.call(this, ev, data);
+ }
+ }
+ }
+ };
+
+ // automatically bind on these
+ $([document, window]).bind(name, function() {});
+
+ return $.event.special[name];
+ }
+
+ return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/event/swipe.js b/dist/event/swipe.js
new file mode 100644
index 00000000..aa00ed0c
--- /dev/null
+++ b/dist/event/swipe.js
@@ -0,0 +1,130 @@
+(function($) {
+var isPhantom = /Phantom/.test(navigator.userAgent),
+ supportTouch = !isPhantom && "ontouchend" in document,
+ scrollEvent = "touchmove scroll",
+ // Use touch events or map it to mouse events
+ touchStartEvent = supportTouch ? "touchstart" : "mousedown",
+ touchStopEvent = supportTouch ? "touchend" : "mouseup",
+ touchMoveEvent = supportTouch ? "touchmove" : "mousemove",
+ data = function(event){
+ var d = event.originalEvent.touches ?
+ event.originalEvent.touches[ 0 ] :
+ event;
+ return {
+ time: (new Date).getTime(),
+ coords: [ d.pageX, d.pageY ],
+ origin: $( event.target )
+ };
+ };
+
+/**
+ * @add jQuery.event.swipe
+ */
+var swipe = $.event.swipe = {
+ /**
+ * @attribute delay
+ * Delay is the upper limit of time the swipe motion can take in milliseconds. This defaults to 500.
+ *
+ * A user must perform the swipe motion in this much time.
+ */
+ delay : 500,
+ /**
+ * @attribute max
+ * The maximum distance the pointer must travel in pixels. The default is 75 pixels.
+ */
+ max : 320,
+ /**
+ * @attribute min
+ * The minimum distance the pointer must travel in pixels. The default is 30 pixels.
+ */
+ min : 30
+};
+
+$.event.setupHelper( [
+
+/**
+ * @hide
+ * @attribute swipe
+ */
+"swipe",
+/**
+ * @hide
+ * @attribute swipeleft
+ */
+'swipeleft',
+/**
+ * @hide
+ * @attribute swiperight
+ */
+'swiperight',
+/**
+ * @hide
+ * @attribute swipeup
+ */
+'swipeup',
+/**
+ * @hide
+ * @attribute swipedown
+ */
+'swipedown'], touchStartEvent, function(ev){
+ var
+ // update with data when the event was started
+ start = data(ev),
+ stop,
+ delegate = ev.delegateTarget || ev.currentTarget,
+ selector = ev.handleObj.selector,
+ entered = this;
+
+ function moveHandler(event){
+ if ( !start ) {
+ return;
+ }
+ // update stop with the data from the current event
+ stop = data(event);
+
+ // prevent scrolling
+ if ( Math.abs( start.coords[0] - stop.coords[0] ) > 10 ) {
+ event.preventDefault();
+ }
+ };
+
+ // Attach to the touch move events
+ $(document.documentElement).bind(touchMoveEvent, moveHandler)
+ .one(touchStopEvent, function(event){
+ $(this).unbind( touchMoveEvent, moveHandler);
+ // if start and stop contain data figure out if we have a swipe event
+ if ( start && stop ) {
+ // calculate the distance between start and stop data
+ var deltaX = Math.abs(start.coords[0] - stop.coords[0]),
+ deltaY = Math.abs(start.coords[1] - stop.coords[1]),
+ distance = Math.sqrt(deltaX*deltaX+deltaY*deltaY);
+
+ // check if the delay and distance are matched
+ if ( stop.time - start.time < swipe.delay && distance >= swipe.min && distance <= swipe.max ) {
+ var events = ['swipe'];
+ // check if we moved horizontally
+ if( deltaX >= swipe.min && deltaY < swipe.min) {
+ // based on the x coordinate check if we moved left or right
+ events.push( start.coords[0] > stop.coords[0] ? "swipeleft" : "swiperight" );
+ } else
+ // check if we moved vertically
+ if(deltaY >= swipe.min && deltaX < swipe.min){
+ // based on the y coordinate check if we moved up or down
+ events.push( start.coords[1] < stop.coords[1] ? "swipedown" : "swipeup" );
+ }
+
+ // trigger swipe events on this guy
+ $.each($.event.find(delegate, events, selector), function(){
+ this.call(entered, ev, {start : start, end: stop})
+ })
+
+ }
+ }
+ // reset start and stop
+ start = stop = undefined;
+ })
+});
+
+return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/jquerypp.js b/dist/jquerypp.js
new file mode 100644
index 00000000..56ec5b0e
--- /dev/null
+++ b/dist/jquerypp.js
@@ -0,0 +1,3828 @@
+/*!
+* jQuery++ - 1.0.1 (2013-02-08)
+* http://jquerypp.com
+* Copyright (c) 2013 Bitovi
+* Licensed MIT
+*/
+(function (window, $, undefined) {
+ // ## jquery/dom/styles/styles.js
+ var getComputedStyle = document.defaultView && document.defaultView.getComputedStyle,
+ // The following variables are used to convert camelcased attribute names
+ // into dashed names, e.g. borderWidth to border-width
+ rupper = /([A-Z])/g,
+ rdashAlpha = /-([a-z])/ig,
+ fcamelCase = function (all, letter) {
+ return letter.toUpperCase();
+ },
+ // Returns the computed style for an elementn
+ getStyle = function (elem) {
+ if (getComputedStyle) {
+ return getComputedStyle(elem, null);
+ }
+ else if (elem.currentStyle) {
+ return elem.currentStyle;
+ }
+ },
+ // Checks for float px and numeric values
+ rfloat = /float/i,
+ rnumpx = /^-?\d+(?:px)?$/i,
+ rnum = /^-?\d/;
+
+ // Returns a list of styles for a given element
+ $.styles = function (el, styles) {
+ if (!el) {
+ return null;
+ }
+ var currentS = getStyle(el),
+ oldName, val, style = el.style,
+ results = {},
+ i = 0,
+ left, rsLeft, camelCase, name;
+
+ // Go through each style
+ for (; i < styles.length; i++) {
+ name = styles[i];
+ oldName = name.replace(rdashAlpha, fcamelCase);
+
+ if (rfloat.test(name)) {
+ name = $.support.cssFloat ? "float" : "styleFloat";
+ oldName = "cssFloat";
+ }
+
+ // If we have getComputedStyle available
+ if (getComputedStyle) {
+ // convert camelcased property names to dashed name
+ name = name.replace(rupper, "-$1").toLowerCase();
+ // use getPropertyValue of the current style object
+ val = currentS.getPropertyValue(name);
+ // default opacity is 1
+ if (name === "opacity" && val === "") {
+ val = "1";
+ }
+ results[oldName] = val;
+ } else {
+ // Without getComputedStyles
+ camelCase = name.replace(rdashAlpha, fcamelCase);
+ results[oldName] = currentS[name] || currentS[camelCase];
+
+ // convert to px
+ if (!rnumpx.test(results[oldName]) && rnum.test(results[oldName])) {
+ // Remember the original values
+ left = style.left;
+ rsLeft = el.runtimeStyle.left;
+
+ // Put in the new values to get a computed value out
+ el.runtimeStyle.left = el.currentStyle.left;
+ style.left = camelCase === "fontSize" ? "1em" : (results[oldName] || 0);
+ results[oldName] = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ el.runtimeStyle.left = rsLeft;
+ }
+
+ }
+ }
+
+ return results;
+ };
+
+
+ $.fn.styles = function () {
+ // Pass the arguments as an array to $.styles
+ return $.styles(this[0], $.makeArray(arguments));
+ };
+
+ // ## jquery/dom/animate/animate.js
+ // Overwrites `jQuery.fn.animate` to use CSS 3 animations if possible
+ var
+ // The global animation counter
+ animationNum = 0,
+ // The stylesheet for our animations
+ styleSheet = null,
+ // The animation cache
+ cache = [],
+ // Stores the browser properties like transition end event name and prefix
+ browser = null,
+ // Store the original $.fn.animate
+ oldanimate = $.fn.animate,
+
+ // Return the stylesheet, create it if it doesn't exists
+ getStyleSheet = function () {
+ if (!styleSheet) {
+ var style = document.createElement('style');
+ style.setAttribute("type", "text/css");
+ style.setAttribute("media", "screen");
+
+ document.getElementsByTagName('head')[0].appendChild(style);
+ if (!window.createPopup) {
+ style.appendChild(document.createTextNode(''));
+ }
+
+ styleSheet = style.sheet;
+ }
+
+ return styleSheet;
+ },
+
+ //removes an animation rule from a sheet
+ removeAnimation = function (sheet, name) {
+ for (var j = sheet.cssRules.length - 1; j >= 0; j--) {
+ var rule = sheet.cssRules[j];
+ // 7 means the keyframe rule
+ if (rule.type === 7 && rule.name == name) {
+ sheet.deleteRule(j)
+ return;
+ }
+ }
+ },
+
+ // Returns whether the animation should be passed to the original $.fn.animate.
+ passThrough = function (props, ops) {
+ var nonElement = !(this[0] && this[0].nodeType),
+ isInline = !nonElement && $(this).css("display") === "inline" && $(this).css("float") === "none";
+
+ for (var name in props) {
+ // jQuery does something with these values
+ if (props[name] == 'show' || props[name] == 'hide' || props[name] == 'toggle'
+ // Arrays for individual easing
+ || $.isArray(props[name])
+ // Negative values not handled the same
+ || props[name] < 0
+ // unit-less value
+ || name == 'zIndex' || name == 'z-index' || name == 'scrollTop' || name == 'scrollLeft') {
+ return true;
+ }
+ }
+
+ return props.jquery === true || getBrowser() === null ||
+ // Animating empty properties
+ $.isEmptyObject(props) ||
+ // We can't do custom easing
+ (ops && ops.length == 4) || (ops && typeof ops[2] == 'string') ||
+ // Second parameter is an object - we can only handle primitives
+ $.isPlainObject(ops) ||
+ // Inline and non elements
+ isInline || nonElement;
+ },
+
+ // Gets a CSS number (with px added as the default unit if the value is a number)
+ cssValue = function (origName, value) {
+ if (typeof value === "number" && !$.cssNumber[origName]) {
+ return value += "px";
+ }
+ return value;
+ },
+
+ // Feature detection borrowed by http://modernizr.com/
+ getBrowser = function () {
+ if (!browser) {
+ var t, el = document.createElement('fakeelement'),
+ transitions = {
+ 'transition': {
+ transitionEnd: 'transitionEnd',
+ prefix: ''
+ },
+ // 'OTransition': {
+ // transitionEnd : 'oTransitionEnd',
+ // prefix : '-o-'
+ // },
+ // 'MSTransition': {
+ // transitionEnd : 'msTransitionEnd',
+ // prefix : '-ms-'
+ // },
+ 'MozTransition': {
+ transitionEnd: 'animationend',
+ prefix: '-moz-'
+ },
+ 'WebkitTransition': {
+ transitionEnd: 'webkitAnimationEnd',
+ prefix: '-webkit-'
+ }
+ }
+
+ for (t in transitions) {
+ if (el.style[t] !== undefined) {
+ browser = transitions[t];
+ }
+ }
+ }
+ return browser;
+ },
+
+ // Properties that Firefox can't animate if set to 'auto':
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=571344
+ // Provides a converter that returns the actual value
+ ffProps = {
+ top: function (el) {
+ return el.position().top;
+ },
+ left: function (el) {
+ return el.position().left;
+ },
+ width: function (el) {
+ return el.width();
+ },
+ height: function (el) {
+ return el.height();
+ },
+ fontSize: function (el) {
+ return '1em';
+ }
+ },
+
+ // Add browser specific prefix
+ addPrefix = function (properties) {
+ var result = {};
+ $.each(properties, function (name, value) {
+ result[getBrowser().prefix + name] = value;
+ });
+ return result;
+ },
+
+ // Returns the animation name for a given style. It either uses a cached
+ // version or adds it to the stylesheet, removing the oldest style if the
+ // cache has reached a certain size.
+ getAnimation = function (style) {
+ var sheet, name, last;
+
+ // Look up the cached style, set it to that name and reset age if found
+ // increment the age for any other animation
+ $.each(cache, function (i, animation) {
+ if (style === animation.style) {
+ name = animation.name;
+ animation.age = 0;
+ } else {
+ animation.age += 1;
+ }
+ });
+
+ if (!name) { // Add a new style
+ sheet = getStyleSheet();
+ name = "jquerypp_animation_" + (animationNum++);
+ // get the last sheet and insert this rule into it
+ sheet.insertRule("@" + getBrowser().prefix + "keyframes " + name + ' ' + style, (sheet.cssRules && sheet.cssRules.length) || 0);
+ cache.push({
+ name: name,
+ style: style,
+ age: 0
+ });
+
+ // Sort the cache by age
+ cache.sort(function (first, second) {
+ return first.age - second.age;
+ });
+
+ // Remove the last (oldest) item from the cache if it has more than 20 items
+ if (cache.length > 20) {
+ last = cache.pop();
+ removeAnimation(sheet, last.name);
+ }
+ }
+
+ return name;
+ };
+
+
+ $.fn.animate = function (props, speed, easing, callback) {
+ //default to normal animations if browser doesn't support them
+ if (passThrough.apply(this, arguments)) {
+ return oldanimate.apply(this, arguments);
+ }
+
+ var optall = $.speed(speed, easing, callback);
+
+ // Add everything to the animation queue
+ this.queue(optall.queue, function (done) {
+ var
+ //current CSS values
+ current,
+ // The list of properties passed
+ properties = [],
+ to = "",
+ prop, self = $(this),
+ duration = optall.duration,
+ //the animation keyframe name
+ animationName,
+ // The key used to store the animation hook
+ dataKey,
+ //the text for the keyframe
+ style = "{ from {",
+ // The animation end event handler.
+ // Will be called both on animation end and after calling .stop()
+ animationEnd = function (currentCSS, exec) {
+ self.css(currentCSS);
+
+ self.css(addPrefix({
+ "animation-duration": "",
+ "animation-name": "",
+ "animation-fill-mode": "",
+ "animation-play-state": ""
+ }));
+
+ // Call the original callback
+ if ($.isFunction(optall.old) && exec) {
+ // Call success, pass the DOM element as the this reference
+ optall.old.call(self[0], true)
+ }
+
+ $.removeData(self, dataKey, true);
+ },
+ finishAnimation = function () {
+ // Call animationEnd using the passed properties
+ animationEnd(props, true);
+ done();
+ };
+
+ for (prop in props) {
+ properties.push(prop);
+ }
+
+ if (getBrowser().prefix === '-moz-') {
+ // Normalize 'auto' properties in FF
+ $.each(properties, function (i, prop) {
+ var converter = ffProps[$.camelCase(prop)];
+ if (converter && self.css(prop) == 'auto') {
+ self.css(prop, converter(self));
+ }
+ });
+ }
+
+ // Use $.styles
+ current = self.styles.apply(self, properties);
+ $.each(properties, function (i, cur) {
+ // Convert a camelcased property name
+ var name = cur.replace(/([A-Z]|^ms)/g, "-$1").toLowerCase();
+ style += name + " : " + cssValue(cur, current[cur]) + "; ";
+ to += name + " : " + cssValue(cur, props[cur]) + "; ";
+ });
+
+ style += "} to {" + to + " }}";
+
+ animationName = getAnimation(style);
+ dataKey = animationName + '.run';
+
+ // Add a hook which will be called when the animation stops
+ $._data(this, dataKey, {
+ stop: function (gotoEnd) {
+ // Pause the animation
+ self.css(addPrefix({
+ 'animation-play-state': 'paused'
+ }));
+ // Unbind the animation end handler
+ self.off(getBrowser().transitionEnd, finishAnimation);
+ if (!gotoEnd) {
+ // We were told not to finish the animation
+ // Call animationEnd but set the CSS to the current computed style
+ animationEnd(self.styles.apply(self, properties), false);
+ } else {
+ // Finish animaion
+ animationEnd(props, true);
+ }
+ }
+ });
+
+ // set this element to point to that animation
+ self.css(addPrefix({
+ "animation-duration": duration + "ms",
+ "animation-name": animationName,
+ "animation-fill-mode": "forwards"
+ }));
+
+ // Attach the transition end event handler to run only once
+ self.one(getBrowser().transitionEnd, finishAnimation);
+
+ });
+
+ return this;
+ };
+
+ // ## jquery/dom/compare/compare.js
+ // See http://ejohn.org/blog/comparing-document-position/
+ $.fn.compare = function (element) { //usually
+ try {
+ // Firefox 3 throws an error with XUL - we can't use compare then
+ element = element.jquery ? element[0] : element;
+ } catch (e) {
+ return null;
+ }
+
+ // make sure we aren't coming from XUL element
+ if (window.HTMLElement) {
+ var s = HTMLElement.prototype.toString.call(element)
+ if (s == '[xpconnect wrapped native prototype]' || s == '[object XULElement]' || s === '[object Window]') {
+ return null;
+ }
+ }
+
+ if (this[0].compareDocumentPosition) {
+ // For browsers that support it, use compareDocumentPosition
+ // https://developer.mozilla.org/en/DOM/Node.compareDocumentPosition
+ return this[0].compareDocumentPosition(element);
+ }
+
+ // this[0] contains element
+ if (this[0] == document && element != document) return 8;
+
+ var number =
+ // this[0] contains element
+ (this[0] !== element && this[0].contains(element) && 16) +
+ // element contains this[0]
+ (this[0] != element && element.contains(this[0]) && 8),
+ docEl = document.documentElement;
+
+ // Use the sourceIndex
+ if (this[0].sourceIndex) {
+ // this[0] precedes element
+ number += (this[0].sourceIndex < element.sourceIndex && 4)
+ // element precedes foo[0]
+ number += (this[0].sourceIndex > element.sourceIndex && 2)
+ // The nodes are in different documents
+ number += (this[0].ownerDocument !== element.ownerDocument || (this[0] != docEl && this[0].sourceIndex <= 0) || (element != docEl && element.sourceIndex <= 0)) && 1
+ }
+
+ return number;
+ }
+
+ // ## jquery/lang/json/json.js
+
+ $.toJSON = function (o, replacer, space, recurse) {
+ if (typeof(JSON) == 'object' && JSON.stringify) return JSON.stringify(o, replacer, space);
+
+ if (!recurse && $.isFunction(replacer)) o = replacer("", o);
+
+ if (typeof space == "number") space = " ".substring(0, space);
+ space = (typeof space == "string") ? space.substring(0, 10) : "";
+
+ var type = typeof(o);
+
+ if (o === null) return "null";
+
+ if (type == "undefined" || type == "function") return undefined;
+
+ if (type == "number" || type == "boolean") return o + "";
+
+ if (type == "string") return $.quoteString(o);
+
+ if (type == 'object') {
+ if (typeof o.toJSON == "function") return $.toJSON(o.toJSON(), replacer, space, true);
+
+ if (o.constructor === Date) {
+ var month = o.getUTCMonth() + 1;
+ if (month < 10) month = '0' + month;
+
+ var day = o.getUTCDate();
+ if (day < 10) day = '0' + day;
+
+ var year = o.getUTCFullYear();
+
+ var hours = o.getUTCHours();
+ if (hours < 10) hours = '0' + hours;
+
+ var minutes = o.getUTCMinutes();
+ if (minutes < 10) minutes = '0' + minutes;
+
+ var seconds = o.getUTCSeconds();
+ if (seconds < 10) seconds = '0' + seconds;
+
+ var milli = o.getUTCMilliseconds();
+ if (milli < 100) milli = '0' + milli;
+ if (milli < 10) milli = '0' + milli;
+
+ return '"' + year + '-' + month + '-' + day + 'T' + hours + ':' + minutes + ':' + seconds + '.' + milli + 'Z"';
+ }
+
+ var process = ($.isFunction(replacer)) ?
+ function (k, v) {
+ return replacer(k, v);
+ } : function (k, v) {
+ return v;
+ },
+ nl = (space) ? "\n" : "",
+ sp = (space) ? " " : "";
+
+ if (o.constructor === Array) {
+ var ret = [];
+ for (var i = 0; i < o.length; i++)
+ ret.push(($.toJSON(process(i, o[i]), replacer, space, true) || "null").replace(/^/gm, space));
+
+ return "[" + nl + ret.join("," + nl) + nl + "]";
+ }
+
+ var pairs = [],
+ proplist;
+ if ($.isArray(replacer)) {
+ proplist = $.map(replacer, function (v) {
+ return (typeof v == "string" || typeof v == "number") ? v + "" : null;
+ });
+ }
+ for (var k in o) {
+ var name, val, type = typeof k;
+
+ if (proplist && $.inArray(k + "", proplist) == -1) continue;
+
+ if (type == "number") name = '"' + k + '"';
+ else if (type == "string") name = $.quoteString(k);
+ else continue; //skip non-string or number keys
+ val = $.toJSON(process(k, o[k]), replacer, space, true);
+
+ if (typeof val == "undefined") continue; //skip pairs where the value is a function.
+ pairs.push((name + ":" + sp + val).replace(/^/gm, space));
+ }
+
+ return "{" + nl + pairs.join("," + nl) + nl + "}";
+ }
+ };
+
+
+ $.evalJSON = function (src) {
+ if (typeof(JSON) == 'object' && JSON.parse) return JSON.parse(src);
+ return eval("(" + src + ")");
+ };
+
+
+ $.secureEvalJSON = function (src) {
+ if (typeof(JSON) == 'object' && JSON.parse) return JSON.parse(src);
+
+ var filtered = src;
+ filtered = filtered.replace(/\\["\\\/bfnrtu]/g, '@');
+ filtered = filtered.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
+ filtered = filtered.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
+
+ if (/^[\],:{}\s]*$/.test(filtered)) return eval("(" + src + ")");
+ else throw new SyntaxError("Error parsing JSON, source is not valid.");
+ };
+
+
+ $.quoteString = function (string) {
+ if (string.match(_escapeable)) {
+ return '"' + string.replace(_escapeable, function (a) {
+ var c = _meta[a];
+ if (typeof c === 'string') return c;
+ c = a.charCodeAt();
+ return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
+ }) + '"';
+ }
+ return '"' + string + '"';
+ };
+
+ var _escapeable = /["\\\x00-\x1f\x7f-\x9f]/g;
+
+ var _meta = {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"': '\\"',
+ '\\': '\\\\'
+ };
+
+ // ## jquery/dom/cookie/cookie.js
+
+ $.cookie = function (name, value, options) {
+ if (typeof value != 'undefined') {
+ // name and value given, set cookie
+ options = options || {};
+ if (value === null) {
+ value = '';
+ options.expires = -1;
+ }
+ // convert value to JSON string
+ if (typeof value == 'object' && $.toJSON) {
+ value = $.toJSON(value);
+ }
+ var expires = '';
+ // Set expiry
+ if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
+ var date;
+ if (typeof options.expires == 'number') {
+ date = new Date();
+ date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
+ }
+ else {
+ date = options.expires;
+ }
+ expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
+ }
+ // CAUTION: Needed to parenthesize options.path and options.domain
+ // in the following expressions, otherwise they evaluate to undefined
+ // in the packed version for some reason...
+ var path = options.path ? '; path=' + (options.path) : '';
+ var domain = options.domain ? '; domain=' + (options.domain) : '';
+ var secure = options.secure ? '; secure' : '';
+ // Set the cookie name=value;expires=;path=;domain=;secure-
+ document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
+ }
+ else { // only name given, get cookie
+ var cookieValue = null;
+ if (document.cookie && document.cookie != '') {
+ var cookies = document.cookie.split(';');
+ for (var i = 0; i < cookies.length; i++) {
+ var cookie = $.trim(cookies[i]);
+ // Does this cookie string begin with the name we want?
+ if (cookie.substring(0, name.length + 1) == (name + '=')) {
+ // Get the cookie value
+ cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
+ break;
+ }
+ }
+ }
+ // Parse JSON from the cookie into an object
+ if ($.evalJSON && cookieValue && cookieValue.match(/^\s*\{/)) {
+ try {
+ cookieValue = $.evalJSON(cookieValue);
+ }
+ catch (e) {}
+ }
+ return cookieValue;
+ }
+ };
+
+ // ## jquery/dom/dimensions/dimensions.js
+ var
+ //margin is inside border
+ weird = /button|select/i,
+ getBoxes = {},
+ checks = {
+ width: ["Left", "Right"],
+ height: ['Top', 'Bottom'],
+ oldOuterHeight: $.fn.outerHeight,
+ oldOuterWidth: $.fn.outerWidth,
+ oldInnerWidth: $.fn.innerWidth,
+ oldInnerHeight: $.fn.innerHeight
+ },
+ supportsSetter = $.fn.jquery >= '1.8.0';
+
+ $.each({
+
+ width:
+
+ "Width",
+
+ height:
+
+ // for each 'height' and 'width'
+ "Height"
+ }, function (lower, Upper) {
+
+ //used to get the padding and border for an element in a given direction
+ getBoxes[lower] = function (el, boxes) {
+ var val = 0;
+ if (!weird.test(el.nodeName)) {
+ //make what to check for ....
+ var myChecks = [];
+ $.each(checks[lower], function () {
+ var direction = this;
+ $.each(boxes, function (name, val) {
+ if (val) myChecks.push(name + direction + (name == 'border' ? "Width" : ""));
+ })
+ })
+ $.each($.styles(el, myChecks), function (name, value) {
+ val += (parseFloat(value) || 0);
+ })
+ }
+ return val;
+ }
+
+ //getter / setter
+ if (!supportsSetter) {
+ $.fn["outer" + Upper] = function (v, margin) {
+ var first = this[0];
+ if (typeof v == 'number') {
+ // Setting the value
+ first && this[lower](v - getBoxes[lower](first, {
+ padding: true,
+ border: true,
+ margin: margin
+ }))
+ return this;
+ } else {
+ // Return the old value
+ return first ? checks["oldOuter" + Upper].apply(this, arguments) : null;
+ }
+ }
+ $.fn["inner" + Upper] = function (v) {
+ var first = this[0];
+ if (typeof v == 'number') {
+ // Setting the value
+ first && this[lower](v - getBoxes[lower](first, {
+ padding: true
+ }))
+ return this;
+ } else {
+ // Return the old value
+ return first ? checks["oldInner" + Upper].apply(this, arguments) : null;
+ }
+ }
+ }
+
+ //provides animations
+ var animate = function (boxes) {
+ // Return the animation function
+ return function (fx) {
+ if (fx[supportsSetter ? 'pos' : 'state'] == 0) {
+ fx.start = $(fx.elem)[lower]();
+ fx.end = fx.end - getBoxes[lower](fx.elem, boxes);
+ }
+ fx.elem.style[lower] = (fx.pos * (fx.end - fx.start) + fx.start) + "px"
+ }
+ }
+ $.fx.step["outer" + Upper] = animate({
+ padding: true,
+ border: true
+ })
+ $.fx.step["outer" + Upper + "Margin"] = animate({
+ padding: true,
+ border: true,
+ margin: true
+ })
+ $.fx.step["inner" + Upper] = animate({
+ padding: true
+ })
+
+ })
+
+ // ## jquery/dom/form_params/form_params.js
+ var
+ // use to parse bracket notation like my[name][attribute]
+ keyBreaker = /[^\[\]]+/g,
+ // converts values that look like numbers and booleans and removes empty strings
+ convertValue = function (value) {
+ if ($.isNumeric(value)) {
+ return parseFloat(value);
+ } else if (value === 'true') {
+ return true;
+ } else if (value === 'false') {
+ return false;
+ } else if (value === '' || value === null) {
+ return undefined;
+ }
+ return value;
+ },
+ // Access nested data
+ nestData = function (elem, type, data, parts, value, seen, fullName) {
+ var name = parts.shift();
+ // Keep track of the dot separated fullname. Used to uniquely track seen values
+ // and if they should be converted to an array or not
+ fullName = fullName ? fullName + '.' + name : name;
+
+ if (parts.length) {
+ if (!data[name]) {
+ data[name] = {};
+ }
+
+ // Recursive call
+ nestData(elem, type, data[name], parts, value, seen, fullName);
+ } else {
+
+ // Handle same name case, as well as "last checkbox checked"
+ // case
+ if (fullName in seen && type != "radio" && !$.isArray(data[name])) {
+ if (name in data) {
+ data[name] = [data[name]];
+ } else {
+ data[name] = [];
+ }
+ } else {
+ seen[fullName] = true;
+ }
+
+ // Finally, assign data
+ if ((type == "radio" || type == "checkbox") && !elem.is(":checked")) {
+ return
+ }
+
+ if (!data[name]) {
+ data[name] = value;
+ } else {
+ data[name].push(value);
+ }
+
+
+ }
+
+ };
+
+
+ $.fn.extend({
+ formParams: function (params) {
+
+ var convert;
+
+ // Quick way to determine if something is a boolean
+ if ( !! params === params) {
+ convert = params;
+ params = null;
+ }
+
+ if (params) {
+ return this.setParams(params);
+ } else {
+ return this.getParams(convert);
+ }
+ },
+ setParams: function (params) {
+
+ // Find all the inputs
+ this.find("[name]").each(function () {
+ var $this = $(this),
+ value = params[$this.attr("name")];
+
+ // Don't do all this work if there's no value
+ if (value !== undefined) {
+
+ // Nested these if statements for performance
+ if ($this.is(":radio")) {
+ if ($this.val() == value) {
+ $this.prop("checked", true).trigger('change');
+ }
+ } else if ($this.is(":checkbox")) {
+ // Convert single value to an array to reduce
+ // complexity
+ value = $.isArray(value) ? value : [value];
+ if ($.inArray($this.val(), value) > -1) {
+ $this.prop("checked", true).trigger('change');
+ }
+ } else {
+ $this.val(value);
+ }
+ }
+ });
+ },
+ getParams: function (convert) {
+ var data = {},
+ // This is used to keep track of the checkbox names that we've
+ // already seen, so we know that we should return an array if
+ // we see it multiple times. Fixes last checkbox checked bug.
+ seen = {},
+ current;
+
+ this.find("[name]:not(:disabled)").each(function () {
+ var $this = $(this),
+ type = $this.attr("type"),
+ name = $this.attr("name"),
+ value = $this.val(),
+ parts;
+
+ // Don't accumulate submit buttons and nameless elements
+ if (type == "submit" || !name) {
+ return;
+ }
+
+ // Figure out name parts
+ parts = name.match(keyBreaker);
+ if (!parts.length) {
+ parts = [name];
+ }
+
+ // Convert the value
+ if (convert) {
+ value = convertValue(value);
+ }
+
+ // Assign data recursively
+ nestData($this, type, data, parts, value, seen);
+
+ });
+
+ return data;
+ }
+ });
+
+ // ## jquery/dom/range/range.js
+ $.fn.range =
+
+ function () {
+ return $.Range(this[0])
+ }
+
+ var convertType = function (type) {
+ return type.replace(/([a-z])([a-z]+)/gi, function (all, first, next) {
+ return first + next.toLowerCase()
+ }).replace(/_/g, "");
+ },
+ // reverses things like START_TO_END into END_TO_START
+ reverse = function (type) {
+ return type.replace(/^([a-z]+)_TO_([a-z]+)/i, function (all, first, last) {
+ return last + "_TO_" + first;
+ });
+ },
+ getWindow = function (element) {
+ return element ? element.ownerDocument.defaultView || element.ownerDocument.parentWindow : window
+ },
+ bisect = function (el, start, end) {
+ //split the start and end ... figure out who is touching ...
+ if (end - start == 1) {
+ return
+ }
+ },
+ support = {};
+
+ $.Range = function (range) {
+ // If it's called w/o new, call it with new!
+ if (this.constructor !== $.Range) {
+ return new $.Range(range);
+ }
+ // If we are passed a jQuery-wrapped element, get the raw element
+ if (range && range.jquery) {
+ range = range[0];
+ }
+ // If we have an element, or nothing
+ if (!range || range.nodeType) {
+ // create a range
+ this.win = getWindow(range)
+ if (this.win.document.createRange) {
+ this.range = this.win.document.createRange()
+ } else if (this.win && this.win.document.body && this.win.document.body.createTextRange) {
+ this.range = this.win.document.body.createTextRange()
+ }
+ // if we have an element, make the range select it
+ if (range) {
+ this.select(range)
+ }
+ }
+ // if we are given a point
+ else if (range.clientX != null || range.pageX != null || range.left != null) {
+ this.moveToPoint(range);
+ }
+ // if we are given a touch event
+ else if (range.originalEvent && range.originalEvent.touches && range.originalEvent.touches.length) {
+ this.moveToPoint(range.originalEvent.touches[0])
+
+ }
+ // if we are a normal event
+ else if (range.originalEvent && range.originalEvent.changedTouches && range.originalEvent.changedTouches.length) {
+ this.moveToPoint(range.originalEvent.changedTouches[0])
+ }
+ // given a TextRange or something else?
+ else {
+ this.range = range;
+ }
+ };
+
+ $.Range.
+
+ current = function (el) {
+ var win = getWindow(el),
+ selection;
+ if (win.getSelection) {
+ // If we can get the selection
+ selection = win.getSelection()
+ return new $.Range(selection.rangeCount ? selection.getRangeAt(0) : win.document.createRange())
+ } else {
+ // Otherwise use document.selection
+ return new $.Range(win.document.selection.createRange());
+ }
+ };
+
+ $.extend($.Range.prototype,
+
+ {
+
+ moveToPoint: function (point) {
+ var clientX = point.clientX,
+ clientY = point.clientY
+ if (!clientX) {
+ var off = scrollOffset();
+ clientX = (point.pageX || point.left || 0) - off.left;
+ clientY = (point.pageY || point.top || 0) - off.top;
+ }
+ if (support.moveToPoint) {
+ this.range = $.Range().range
+ this.range.moveToPoint(clientX, clientY);
+ return this;
+ }
+
+ // it's some text node in this range ...
+ var parent = document.elementFromPoint(clientX, clientY);
+
+ //typically it will be 'on' text
+ for (var n = 0; n < parent.childNodes.length; n++) {
+ var node = parent.childNodes[n];
+ if (node.nodeType === 3 || node.nodeType === 4) {
+ var range = $.Range(node),
+ length = range.toString().length;
+
+ // now lets start moving the end until the boundingRect is within our range
+ for (var i = 1; i < length + 1; i++) {
+ var rect = range.end(i).rect();
+ if (rect.left <= clientX && rect.left + rect.width >= clientX && rect.top <= clientY && rect.top + rect.height >= clientY) {
+ range.start(i - 1);
+ this.range = range.range;
+ return this;
+ }
+ }
+ }
+ }
+
+ // if not 'on' text, recursively go through and find out when we shift to next
+ // 'line'
+ var previous;
+ iterate(parent.childNodes, function (textNode) {
+ var range = $.Range(textNode);
+ if (range.rect().top > point.clientY) {
+ return false;
+ } else {
+ previous = range;
+ }
+ });
+
+ if (previous) {
+ previous.start(previous.toString().length);
+ this.range = previous.range;
+ } else {
+ this.range = $.Range(parent).range
+ }
+ },
+
+ window: function () {
+ return this.win || window;
+ },
+
+ overlaps: function (elRange) {
+ if (elRange.nodeType) {
+ elRange = $.Range(elRange).select(elRange);
+ }
+ //if the start is within the element ...
+ var startToStart = this.compare("START_TO_START", elRange),
+ endToEnd = this.compare("END_TO_END", elRange)
+
+ // if we wrap elRange
+ if (startToStart <= 0 && endToEnd >= 0) {
+ return true;
+ }
+ // if our start is inside of it
+ if (startToStart >= 0 && this.compare("START_TO_END", elRange) <= 0) {
+ return true;
+ }
+ // if our end is inside of elRange
+ if (this.compare("END_TO_START", elRange) >= 0 && endToEnd <= 0) {
+ return true;
+ }
+ return false;
+ },
+
+ collapse: function (toStart) {
+ this.range.collapse(toStart === undefined ? true : toStart);
+ return this;
+ },
+
+ toString: function () {
+ return typeof this.range.text == "string" ? this.range.text : this.range.toString();
+ },
+
+ start: function (set) {
+ // return start
+ if (set === undefined) {
+ if (this.range.startContainer) {
+ return {
+ container: this.range.startContainer,
+ offset: this.range.startOffset
+ }
+ } else {
+ // Get the start parent element
+ var start = this.clone().collapse().parent();
+ // used to get the start element offset
+ var startRange = $.Range(start).select(start).collapse();
+ startRange.move("END_TO_START", this);
+ return {
+ container: start,
+ offset: startRange.toString().length
+ }
+ }
+ } else {
+ if (this.range.setStart) {
+ // supports setStart
+ if (typeof set == 'number') {
+ this.range.setStart(this.range.startContainer, set)
+ } else if (typeof set == 'string') {
+ var res = callMove(this.range.startContainer, this.range.startOffset, parseInt(set, 10))
+ this.range.setStart(res.node, res.offset);
+ } else {
+ this.range.setStart(set.container, set.offset)
+ }
+ } else {
+ if (typeof set == "string") {
+ this.range.moveStart('character', parseInt(set, 10))
+ } else {
+ // get the current end container
+ var container = this.start().container,
+ offset
+ if (typeof set == "number") {
+ offset = set
+ } else {
+ container = set.container
+ offset = set.offset
+ }
+ var newPoint = $.Range(container).collapse();
+ //move it over offset characters
+ newPoint.range.move(offset);
+ this.move("START_TO_START", newPoint);
+ }
+ }
+ return this;
+ }
+
+ },
+
+ end: function (set) {
+ // read end
+ if (set === undefined) {
+ if (this.range.startContainer) {
+ return {
+ container: this.range.endContainer,
+ offset: this.range.endOffset
+ }
+ }
+ else {
+ var
+ // Get the end parent element
+ end = this.clone().collapse(false).parent(),
+ // used to get the end elements offset
+ endRange = $.Range(end).select(end).collapse();
+ endRange.move("END_TO_END", this);
+ return {
+ container: end,
+ offset: endRange.toString().length
+ }
+ }
+ } else {
+ if (this.range.setEnd) {
+ if (typeof set == 'number') {
+ this.range.setEnd(this.range.endContainer, set)
+ } else if (typeof set == 'string') {
+ var res = callMove(this.range.endContainer, this.range.endOffset, parseInt(set, 10))
+ this.range.setEnd(res.node, res.offset);
+ } else {
+ this.range.setEnd(set.container, set.offset)
+ }
+ } else {
+ if (typeof set == "string") {
+ this.range.moveEnd('character', parseInt(set, 10));
+ } else {
+ // get the current end container
+ var container = this.end().container,
+ offset
+ if (typeof set == "number") {
+ offset = set
+ } else {
+ container = set.container
+ offset = set.offset
+ }
+ var newPoint = $.Range(container).collapse();
+ //move it over offset characters
+ newPoint.range.move(offset);
+ this.move("END_TO_START", newPoint);
+ }
+ }
+ return this;
+ }
+ },
+
+ parent: function () {
+ if (this.range.commonAncestorContainer) {
+ return this.range.commonAncestorContainer;
+ } else {
+
+ var parentElement = this.range.parentElement(),
+ range = this.range;
+
+ // IE's parentElement will always give an element, we want text ranges
+ iterate(parentElement.childNodes, function (txtNode) {
+ if ($.Range(txtNode).range.inRange(range)) {
+ // swap out the parentElement
+ parentElement = txtNode;
+ return false;
+ }
+ });
+
+ return parentElement;
+ }
+ },
+
+ rect: function (from) {
+ var rect = this.range.getBoundingClientRect();
+ // for some reason in webkit this gets a better value
+ if (!rect.height && !rect.width) {
+ rect = this.range.getClientRects()[0]
+ }
+ if (from === 'page') {
+ // Add the scroll offset
+ var off = scrollOffset();
+ rect = $.extend({}, rect);
+ rect.top += off.top;
+ rect.left += off.left;
+ }
+ return rect;
+ },
+
+ rects: function (from) {
+ // order rects by size
+ var rects = $.map($.makeArray(this.range.getClientRects()).sort(function (rect1, rect2) {
+ return rect2.width * rect2.height - rect1.width * rect1.height;
+ }), function (rect) {
+ return $.extend({}, rect)
+ }),
+ i = 0,
+ j, len = rects.length;
+
+ // safari returns overlapping client rects
+ // - big rects can contain 2 smaller rects
+ // - some rects can contain 0 - width rects
+ // - we don't want these 0 width rects
+ while (i < rects.length) {
+ var cur = rects[i],
+ found = false;
+
+ j = i + 1;
+ while (j < rects.length) {
+ if (withinRect(cur, rects[j])) {
+ if (!rects[j].width) {
+ rects.splice(j, 1)
+ } else {
+ found = rects[j];
+ break;
+ }
+ } else {
+ j++;
+ }
+ }
+
+ if (found) {
+ rects.splice(i, 1)
+ } else {
+ i++;
+ }
+
+ }
+ // safari will be return overlapping ranges ...
+ if (from == 'page') {
+ var off = scrollOffset();
+ return $.each(rects, function (ith, item) {
+ item.top += off.top;
+ item.left += off.left;
+ })
+ }
+
+ return rects;
+ }
+
+ });
+ (function () {
+ //method branching ....
+ var fn = $.Range.prototype,
+ range = $.Range().range;
+
+
+ fn.compare = range.compareBoundaryPoints ?
+ function (type, range) {
+ return this.range.compareBoundaryPoints(this.window().Range[reverse(type)], range.range)
+ } : function (type, range) {
+ return this.range.compareEndPoints(convertType(type), range.range)
+ }
+
+
+ fn.move = range.setStart ?
+ function (type, range) {
+
+ var rangesRange = range.range;
+ switch (type) {
+ case "START_TO_END":
+ this.range.setStart(rangesRange.endContainer, rangesRange.endOffset)
+ break;
+ case "START_TO_START":
+ this.range.setStart(rangesRange.startContainer, rangesRange.startOffset)
+ break;
+ case "END_TO_END":
+ this.range.setEnd(rangesRange.endContainer, rangesRange.endOffset)
+ break;
+ case "END_TO_START":
+ this.range.setEnd(rangesRange.startContainer, rangesRange.startOffset)
+ break;
+ }
+
+ return this;
+ } : function (type, range) {
+ this.range.setEndPoint(convertType(type), range.range)
+ return this;
+ };
+ var cloneFunc = range.cloneRange ? "cloneRange" : "duplicate",
+ selectFunc = range.selectNodeContents ? "selectNodeContents" : "moveToElementText";
+
+ fn.
+
+ clone = function () {
+ return $.Range(this.range[cloneFunc]());
+ };
+
+ fn.
+
+ select = range.selectNodeContents ?
+ function (el) {
+ if (!el) {
+ var selection = this.window().getSelection();
+ selection.removeAllRanges();
+ selection.addRange(this.range);
+ } else {
+ this.range.selectNodeContents(el);
+ }
+ return this;
+ } : function (el) {
+ if (!el) {
+ this.range.select()
+ } else if (el.nodeType === 3) {
+ //select this node in the element ...
+ var parent = el.parentNode,
+ start = 0,
+ end;
+ iterate(parent.childNodes, function (txtNode) {
+ if (txtNode === el) {
+ end = start + txtNode.nodeValue.length;
+ return false;
+ } else {
+ start = start + txtNode.nodeValue.length
+ }
+ })
+ this.range.moveToElementText(parent);
+
+ this.range.moveEnd('character', end - this.range.text.length)
+ this.range.moveStart('character', start);
+ } else {
+ this.range.moveToElementText(el);
+ }
+ return this;
+ };
+
+ })();
+
+ // helpers -----------------
+ // iterates through a list of elements, calls cb on every text node
+ // if cb returns false, exits the iteration
+ var iterate = function (elems, cb) {
+ var elem, start;
+ for (var i = 0; elems[i]; i++) {
+ elem = elems[i];
+ // Get the text from text nodes and CDATA nodes
+ if (elem.nodeType === 3 || elem.nodeType === 4) {
+ if (cb(elem) === false) {
+ return false;
+ }
+ // Traverse everything else, except comment nodes
+ }
+ else if (elem.nodeType !== 8) {
+ if (iterate(elem.childNodes, cb) === false) {
+ return false;
+ }
+ }
+ }
+
+ },
+ isText = function (node) {
+ return node.nodeType === 3 || node.nodeType === 4
+ },
+ iteratorMaker = function (toChildren, toNext) {
+ return function (node, mustMoveRight) {
+ // first try down
+ if (node[toChildren] && !mustMoveRight) {
+ return isText(node[toChildren]) ? node[toChildren] : arguments.callee(node[toChildren])
+ } else if (node[toNext]) {
+ return isText(node[toNext]) ? node[toNext] : arguments.callee(node[toNext])
+ } else if (node.parentNode) {
+ return arguments.callee(node.parentNode, true)
+ }
+ }
+ },
+ getNextTextNode = iteratorMaker("firstChild", "nextSibling"),
+ getPrevTextNode = iteratorMaker("lastChild", "previousSibling"),
+ callMove = function (container, offset, howMany) {
+ var mover = howMany < 0 ? getPrevTextNode : getNextTextNode;
+
+ // find the text element
+ if (!isText(container)) {
+ // sometimes offset isn't actually an element
+ container = container.childNodes[offset] ? container.childNodes[offset] :
+ // if this happens, use the last child
+ container.lastChild;
+
+ if (!isText(container)) {
+ container = mover(container)
+ }
+ return move(container, howMany)
+ } else {
+ if (offset + howMany < 0) {
+ return move(mover(container), offset + howMany)
+ } else {
+ return move(container, offset + howMany)
+ }
+
+ }
+ },
+ // Moves howMany characters from the start of
+ // from
+ move = function (from, howMany) {
+ var mover = howMany < 0 ? getPrevTextNode : getNextTextNode;
+
+ howMany = Math.abs(howMany);
+
+ while (from && howMany >= from.nodeValue.length) {
+ howMany = howMany - from.nodeValue.length;
+ from = mover(from)
+ }
+ return {
+ node: from,
+ offset: mover === getNextTextNode ? howMany : from.nodeValue.length - howMany
+ }
+ },
+ supportWhitespace, isWhitespace = function (el) {
+ if (supportWhitespace == null) {
+ supportWhitespace = 'isElementContentWhitespace' in el;
+ }
+ return (supportWhitespace ? el.isElementContentWhitespace : (el.nodeType === 3 && '' == el.data.trim()));
+
+ },
+ // if a point is within a rectangle
+ within = function (rect, point) {
+
+ return rect.left <= point.clientX && rect.left + rect.width >= point.clientX && rect.top <= point.clientY && rect.top + rect.height >= point.clientY
+ },
+ // if a rectangle is within another rectangle
+ withinRect = function (outer, inner) {
+ return within(outer, {
+ clientX: inner.left,
+ clientY: inner.top
+ }) && //top left
+ within(outer, {
+ clientX: inner.left + inner.width,
+ clientY: inner.top
+ }) && //top right
+ within(outer, {
+ clientX: inner.left,
+ clientY: inner.top + inner.height
+ }) && //bottom left
+ within(outer, {
+ clientX: inner.left + inner.width,
+ clientY: inner.top + inner.height
+ }) //bottom right
+ },
+ // gets the scroll offset from a window
+ scrollOffset = function (win) {
+ var win = win || window;
+ doc = win.document.documentElement, body = win.document.body;
+
+ return {
+ left: (doc && doc.scrollLeft || body && body.scrollLeft || 0) + (doc.clientLeft || 0),
+ top: (doc && doc.scrollTop || body && body.scrollTop || 0) + (doc.clientTop || 0)
+ };
+ };
+
+ support.moveToPoint = !! $.Range().range.moveToPoint
+
+ // ## jquery/dom/selection/selection.js
+ var getWindow = function (element) {
+ return element ? element.ownerDocument.defaultView || element.ownerDocument.parentWindow : window
+ },
+ // A helper that uses range to abstract out getting the current start and endPos.
+ getElementsSelection = function (el, win) {
+ // get a copy of the current range and a range that spans the element
+ var current = $.Range.current(el).clone(),
+ entireElement = $.Range(el).select(el);
+ // if there is no overlap, there is nothing selected
+ if (!current.overlaps(entireElement)) {
+ return null;
+ }
+ // if the current range starts before our element
+ if (current.compare("START_TO_START", entireElement) < 1) {
+ // the selection within the element begins at 0
+ startPos = 0;
+ // move the current range to start at our element
+ current.move("START_TO_START", entireElement);
+ } else {
+ // Make a copy of the element's range.
+ // Move it's end to the start of the selected range
+ // The length of the copy is the start of the selected
+ // range.
+ fromElementToCurrent = entireElement.clone();
+ fromElementToCurrent.move("END_TO_START", current);
+ startPos = fromElementToCurrent.toString().length
+ }
+
+ // If the current range ends after our element
+ if (current.compare("END_TO_END", entireElement) >= 0) {
+ // the end position is the last character
+ endPos = entireElement.toString().length
+ } else {
+ // otherwise, it's the start position plus the current range
+ // TODO: this doesn't seem like it works if current
+ // extends to the left of the element.
+ endPos = startPos + current.toString().length
+ }
+ return {
+ start: startPos,
+ end: endPos,
+ width: endPos - startPos
+ };
+ },
+ // Text selection works differently for selection in an input vs
+ // normal html elements like divs, spans, and ps.
+ // This function branches between the various methods of getting the selection.
+ getSelection = function (el) {
+ var win = getWindow(el);
+
+ // `selectionStart` means this is an input element in a standards browser.
+ if (el.selectionStart !== undefined) {
+
+ if (document.activeElement && document.activeElement != el && el.selectionStart == el.selectionEnd && el.selectionStart == 0) {
+ return {
+ start: el.value.length,
+ end: el.value.length,
+ width: 0
+ };
+ }
+ return {
+ start: el.selectionStart,
+ end: el.selectionEnd,
+ width: el.selectionEnd - el.selectionStart
+ };
+ }
+ // getSelection means a 'normal' element in a standards browser.
+ else if (win.getSelection) {
+ return getElementsSelection(el, win)
+ } else {
+ // IE will freak out, where there is no way to detect it, so we provide a callback if it does.
+ try {
+ // The following typically works for input elements in IE:
+ if (el.nodeName.toLowerCase() == 'input') {
+ var real = getWindow(el).document.selection.createRange(),
+ r = el.createTextRange();
+ r.setEndPoint("EndToStart", real);
+
+ var start = r.text.length
+ return {
+ start: start,
+ end: start + real.text.length,
+ width: real.text.length
+ }
+ }
+ // This works on textareas and other elements
+ else {
+ var res = getElementsSelection(el, win)
+ if (!res) {
+ return res;
+ }
+ // we have to clean up for ie's textareas which don't count for
+ // newlines correctly
+ var current = $.Range.current().clone(),
+ r2 = current.clone().collapse().range,
+ r3 = current.clone().collapse(false).range;
+
+ r2.moveStart('character', -1)
+ r3.moveStart('character', -1)
+ // if we aren't at the start, but previous is empty, we are at start of newline
+ if (res.startPos != 0 && r2.text == "") {
+ res.startPos += 2;
+ }
+ // do a similar thing for the end of the textarea
+ if (res.endPos != 0 && r3.text == "") {
+ res.endPos += 2;
+ }
+
+ return res
+ }
+ } catch (e) {
+ return {
+ start: el.value.length,
+ end: el.value.length,
+ width: 0
+ };
+ }
+ }
+ },
+ // Selects text within an element. Depending if it's a form element or
+ // not, or a standards based browser or not, we do different things.
+ select = function (el, start, end) {
+ var win = getWindow(el);
+ // IE behaves bad even if it sorta supports
+ // getSelection so we have to try the IE methods first. barf.
+ if (el.setSelectionRange) {
+ if (end === undefined) {
+ el.focus();
+ el.setSelectionRange(start, start);
+ } else {
+ el.select();
+ el.selectionStart = start;
+ el.selectionEnd = end;
+ }
+ } else if (el.createTextRange) {
+ var r = el.createTextRange();
+ r.moveStart('character', start);
+ end = end || start;
+ r.moveEnd('character', end - el.value.length);
+
+ r.select();
+ } else if (win.getSelection) {
+ var doc = win.document,
+ sel = win.getSelection(),
+ range = doc.createRange(),
+ ranges = [start, end !== undefined ? end : start];
+ getCharElement([el], ranges);
+ range.setStart(ranges[0].el, ranges[0].count);
+ range.setEnd(ranges[1].el, ranges[1].count);
+
+ // removeAllRanges is necessary for webkit
+ sel.removeAllRanges();
+ sel.addRange(range);
+
+ } else if (win.document.body.createTextRange) { //IE's weirdness
+ var range = document.body.createTextRange();
+ range.moveToElementText(el);
+ range.collapse()
+ range.moveStart('character', start)
+ range.moveEnd('character', end !== undefined ? end : start)
+ range.select();
+ }
+
+ },
+ // If one of the range values is within start and len, replace the range
+ // value with the element and its offset.
+ replaceWithLess = function (start, len, range, el) {
+ if (typeof range[0] === 'number' && range[0] < len) {
+ range[0] = {
+ el: el,
+ count: range[0] - start
+ };
+ }
+ if (typeof range[1] === 'number' && range[1] <= len) {
+ range[1] = {
+ el: el,
+ count: range[1] - start
+ };
+ }
+ },
+ getCharElement = function (elems, range, len) {
+ var elem, start;
+
+ len = len || 0;
+ for (var i = 0; elems[i]; i++) {
+ elem = elems[i];
+ // Get the text from text nodes and CDATA nodes
+ if (elem.nodeType === 3 || elem.nodeType === 4) {
+ start = len
+ len += elem.nodeValue.length;
+ //check if len is now greater than what's in counts
+ replaceWithLess(start, len, range, elem)
+ // Traverse everything else, except comment nodes
+ } else if (elem.nodeType !== 8) {
+ len = getCharElement(elem.childNodes, range, len);
+ }
+ }
+ return len;
+ };
+
+ $.fn.selection = function (start, end) {
+ if (start !== undefined) {
+ return this.each(function () {
+ select(this, start, end)
+ })
+ } else {
+ return getSelection(this[0])
+ }
+ };
+ // for testing
+ $.fn.selection.getCharElement = getCharElement;
+
+ // ## jquery/dom/within/within.js
+ // Checks if x and y coordinates are within a box with left, top, width and height
+ var withinBox = function (x, y, left, top, width, height) {
+ return (y >= top && y < top + height && x >= left && x < left + width);
+ }
+
+ $.fn.within = function (left, top, useOffsetCache) {
+ var ret = []
+ this.each(function () {
+ var q = jQuery(this);
+
+ if (this == document.documentElement) {
+ return ret.push(this);
+ }
+
+ // uses either the cached offset or .offset()
+ var offset = useOffsetCache ? $.data(this, "offsetCache") || $.data(this, "offsetCache", q.offset()) : q.offset();
+
+ // Check if the given coordinates are within the area of the current element
+ var res = withinBox(left, top, offset.left, offset.top, this.offsetWidth, this.offsetHeight);
+
+ if (res) {
+ // Add it to the results
+ ret.push(this);
+ }
+ });
+
+ return this.pushStack($.unique(ret), "within", left + "," + top);
+ }
+
+ $.fn.withinBox = function (left, top, width, height, useOffsetCache) {
+ var ret = []
+ this.each(function () {
+ var q = jQuery(this);
+
+ if (this == document.documentElement) return ret.push(this);
+
+ // use cached offset or .offset()
+ var offset = useOffsetCache ? $.data(this, "offset") || $.data(this, "offset", q.offset()) : q.offset();
+
+ var ew = q.width(),
+ eh = q.height(),
+ // Checks if the element offset is within the given box
+ res = !((offset.top > top + height) || (offset.top + eh < top) || (offset.left > left + width) || (offset.left + ew < left));
+
+ if (res) ret.push(this);
+ });
+ return this.pushStack($.unique(ret), "withinBox", $.makeArray(arguments).join(","));
+ }
+
+ // ## jquery/event/default/default.js
+ $.fn.triggerAsync = function (type, data, success, prevented) {
+ if (typeof data == 'function') {
+ prevented = success;
+ success = data;
+ data = undefined;
+ }
+
+ if (this.length) {
+ var el = this;
+ // Trigger the event with the success callback as the success handler
+ // when triggerAsync called within another triggerAsync,it's the same tick time so we should use timeout
+ // http://javascriptweblog.wordpress.com/2010/06/28/understanding-javascript-timers/
+ setTimeout(function () {
+ el.trigger({
+ type: type,
+ _success: success,
+ _prevented: prevented
+ }, data);
+ }, 0);
+
+ } else {
+ // If we have no elements call the success callback right away
+ if (success) success.call(this);
+ }
+ return this;
+ }
+
+
+ //cache default types for performance
+ var types = {},
+ rnamespaces = /\.(.*)$/,
+ $event = $.event;
+
+ $event.special["default"] = {
+ add: function (handleObj) {
+ //save the type
+ types[handleObj.namespace.replace(rnamespaces, "")] = true;
+ },
+ setup: function () {
+ return true
+ }
+ }
+
+ // overwrite trigger to allow default types
+ var oldTrigger = $event.trigger;
+
+ $event.trigger = function defaultTriggerer(event, data, elem, onlyHandlers) {
+
+ // Event object or event type
+ var type = event.type || event,
+ // Caller can pass in an Event, Object, or just an event type string
+ event = typeof event === "object" ?
+ // jQuery.Event object
+ event[$.expando] ? event :
+ // Object literal
+ new $.Event(type, event) :
+ // Just the event type (string)
+ new $.Event(type),
+ res = oldTrigger.call($.event, event, data, elem, onlyHandlers),
+ paused = event.isPaused && event.isPaused();
+
+ if (!onlyHandlers && !event.isDefaultPrevented() && event.type.indexOf("default") !== 0) {
+ // Trigger the default. event
+ oldTrigger("default." + event.type, data, elem)
+ if (event._success) {
+ event._success(event)
+ }
+ }
+
+ if (!onlyHandlers && event.isDefaultPrevented() && event.type.indexOf("default") !== 0 && !paused) {
+ if (event._prevented) {
+ event._prevented(event);
+ }
+ }
+
+ // code for paused
+ if (paused) {
+ // set back original stuff
+ event.isDefaultPrevented =
+ event.pausedState.isDefaultPrevented;
+ event.isPropagationStopped =
+ event.pausedState.isPropagationStopped;
+ }
+ return res;
+ };
+
+ // ## jquery/event/destroyed/destroyed.js
+
+
+ // Store the old jQuery.cleanData
+ var oldClean = $.cleanData;
+
+ // Overwrites cleanData which is called by jQuery on manipulation methods
+ $.cleanData = function (elems) {
+ for (var i = 0, elem;
+ (elem = elems[i]) !== undefined; i++) {
+ // Trigger the destroyed event
+ $(elem).triggerHandler("destroyed");
+ }
+ // Call the old jQuery.cleanData
+ oldClean(elems);
+ };
+ // ## jquery/lang/vector/vector.js
+ var getSetZero = function (v) {
+ return v !== undefined ? (this.array[0] = v) : this.array[0]
+ },
+ getSetOne = function (v) {
+ return v !== undefined ? (this.array[1] = v) : this.array[1]
+ };
+
+ $.Vector = function (arr) {
+ var array = $.isArray(arr) ? arr : $.makeArray(arguments);
+ this.update(array);
+ };
+ $.Vector.prototype =
+
+ {
+
+ app: function (f) {
+ var i, newArr = [];
+
+ for (i = 0; i < this.array.length; i++) {
+ newArr.push(f(this.array[i], i));
+ }
+ return new $.Vector(newArr);
+ },
+
+ plus: function () {
+ var i, args = arguments[0] instanceof $.Vector ? arguments[0].array : $.makeArray(arguments),
+ arr = this.array.slice(0),
+ vec = new $.Vector();
+ for (i = 0; i < args.length; i++) {
+ arr[i] = (arr[i] ? arr[i] : 0) + args[i];
+ }
+ return vec.update(arr);
+ },
+
+ minus: function () {
+ var i, args = arguments[0] instanceof $.Vector ? arguments[0].array : $.makeArray(arguments),
+ arr = this.array.slice(0),
+ vec = new $.Vector();
+ for (i = 0; i < args.length; i++) {
+ arr[i] = (arr[i] ? arr[i] : 0) - args[i];
+ }
+ return vec.update(arr);
+ },
+
+ equals: function () {
+ var i, args = arguments[0] instanceof $.Vector ? arguments[0].array : $.makeArray(arguments),
+ arr = this.array.slice(0),
+ vec = new $.Vector();
+ for (i = 0; i < args.length; i++) {
+ if (arr[i] != args[i]) {
+ return null;
+ }
+ }
+ return vec.update(arr);
+ },
+
+ x: getSetZero,
+
+ left: getSetZero,
+
+ width: getSetZero,
+
+ y: getSetOne,
+
+ top: getSetOne,
+
+ height: getSetOne,
+
+ toString: function () {
+ return "(" + this.array.join(', ') + ")";
+ },
+
+ update: function (array) {
+ var i;
+ if (this.array) {
+ for (i = 0; i < this.array.length; i++) {
+ delete this.array[i];
+ }
+ }
+ this.array = array;
+ for (i = 0; i < array.length; i++) {
+ this[i] = this.array[i];
+ }
+ return this;
+ }
+ };
+
+ $.Event.prototype.vector = function () {
+ // Get the first touch element for touch events
+ var touches = "ontouchend" in document && this.originalEvent.touches && this.originalEvent.touches.length ? this.originalEvent.changedTouches[0] : this;
+ if (this.originalEvent.synthetic) {
+ var doc = document.documentElement,
+ body = document.body;
+ return new $.Vector(touches.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0), touches.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0));
+ } else {
+ return new $.Vector(touches.pageX, touches.pageY);
+ }
+ };
+
+ $.fn.offsetv = function () {
+ if (this[0] == window) {
+ return new $.Vector(window.pageXOffset ? window.pageXOffset : document.documentElement.scrollLeft, window.pageYOffset ? window.pageYOffset : document.documentElement.scrollTop);
+ } else {
+ var offset = this.offset();
+ return new $.Vector(offset.left, offset.top);
+ }
+ };
+
+ $.fn.dimensionsv = function (which) {
+ if (this[0] == window || !which) {
+ return new $.Vector(this.width(), this.height());
+ }
+ else {
+ return new $.Vector(this[which + "Width"](), this[which + "Height"]());
+ }
+ };
+
+ // ## jquery/event/livehack/livehack.js
+ var event = $.event,
+
+ //helper that finds handlers by type and calls back a function, this is basically handle
+ // events - the events object
+ // types - an array of event types to look for
+ // callback(type, handlerFunc, selector) - a callback
+ // selector - an optional selector to filter with, if there, matches by selector
+ // if null, matches anything, otherwise, matches with no selector
+ findHelper = function (events, types, callback, selector) {
+ var t, type, typeHandlers, all, h, handle, namespaces, namespace, match;
+ for (t = 0; t < types.length; t++) {
+ type = types[t];
+ all = type.indexOf(".") < 0;
+ if (!all) {
+ namespaces = type.split(".");
+ type = namespaces.shift();
+ namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
+ }
+ typeHandlers = (events[type] || []).slice(0);
+
+ for (h = 0; h < typeHandlers.length; h++) {
+ handle = typeHandlers[h];
+
+ match = (all || namespace.test(handle.namespace));
+
+ if (match) {
+ if (selector) {
+ if (handle.selector === selector) {
+ callback(type, handle.origHandler || handle.handler);
+ }
+ } else if (selector === null) {
+ callback(type, handle.origHandler || handle.handler, handle.selector);
+ }
+ else if (!handle.selector) {
+ callback(type, handle.origHandler || handle.handler);
+
+ }
+ }
+
+
+ }
+ }
+ };
+
+
+ event.find = function (el, types, selector) {
+ var events = ($._data(el) || {}).events,
+ handlers = [],
+ t, liver, live;
+
+ if (!events) {
+ return handlers;
+ }
+ findHelper(events, types, function (type, handler) {
+ handlers.push(handler);
+ }, selector);
+ return handlers;
+ };
+
+ event.findBySelector = function (el, types) {
+ var events = $._data(el).events,
+ selectors = {},
+ //adds a handler for a given selector and event
+ add = function (selector, event, handler) {
+ var select = selectors[selector] || (selectors[selector] = {}),
+ events = select[event] || (select[event] = []);
+ events.push(handler);
+ };
+
+ if (!events) {
+ return selectors;
+ }
+ //first check live:
+ //then check straight binds
+ findHelper(events, types, function (type, handler, selector) {
+ add(selector || "", type, handler);
+ }, null);
+
+ return selectors;
+ };
+ event.supportTouch = "ontouchend" in document;
+
+ $.fn.respondsTo = function (events) {
+ if (!this.length) {
+ return false;
+ } else {
+ //add default ?
+ return event.find(this[0], $.isArray(events) ? events : [events]).length > 0;
+ }
+ };
+ $.fn.triggerHandled = function (event, data) {
+ event = (typeof event == "string" ? $.Event(event) : event);
+ this.trigger(event, data);
+ return event.handled;
+ };
+
+ event.setupHelper = function (types, startingEvent, onFirst) {
+ if (!onFirst) {
+ onFirst = startingEvent;
+ startingEvent = null;
+ }
+ var add = function (handleObj) {
+ var bySelector, selector = handleObj.selector || "",
+ namespace = handleObj.namespace ? '.' + handleObj.namespace : '';
+
+ if (selector) {
+ bySelector = event.find(this, types, selector);
+ if (!bySelector.length) {
+ $(this).delegate(selector, startingEvent + namespace, onFirst);
+ }
+ }
+ else {
+ //var bySelector = event.find(this, types, selector);
+ if (!event.find(this, types, selector).length) {
+ event.add(this, startingEvent + namespace, onFirst, {
+ selector: selector,
+ delegate: this
+ });
+ }
+
+ }
+
+ },
+ remove = function (handleObj) {
+ var bySelector, selector = handleObj.selector || "";
+ if (selector) {
+ bySelector = event.find(this, types, selector);
+ if (!bySelector.length) {
+ $(this).undelegate(selector, startingEvent, onFirst);
+ }
+ }
+ else {
+ if (!event.find(this, types, selector).length) {
+ event.remove(this, startingEvent, onFirst, {
+ selector: selector,
+ delegate: this
+ });
+ }
+ }
+ };
+ $.each(types, function () {
+ event.special[this] = {
+ add: add,
+ remove: remove,
+ setup: function () {},
+ teardown: function () {}
+ };
+ });
+ };
+
+ // ## jquery/event/reverse/reverse.js
+ $.event.reverse = function (name, attributes) {
+ var bound = $(),
+ count = 0,
+ dispatch = $.event.handle || $.event.dispatch;
+
+ $.event.special[name] = {
+ setup: function () {
+ // add and sort the resizers array
+ // don't add window because it can't be compared easily
+ if (this !== window) {
+ bound.push(this);
+ $.unique(bound);
+ }
+ // returns false if the window
+ return this !== window;
+ },
+ teardown: function () {
+ // we shouldn't have to sort
+ bound = bound.not(this);
+ // returns false if the window
+ return this !== window;
+ },
+ add: function (handleObj) {
+ var origHandler = handleObj.handler;
+ handleObj.origHandler = origHandler;
+
+ handleObj.handler = function (ev, data) {
+ var isWindow = this === window;
+ if (attributes && attributes.handler) {
+ var result = attributes.handler.apply(this, arguments);
+ if (result === true) {
+ return;
+ }
+ }
+
+ // if this is the first handler for this event ...
+ if (count === 0) {
+ // prevent others from doing what we are about to do
+ count++;
+ var where = data === false ? ev.target : this
+
+ // trigger all this element's handlers
+ dispatch.call(where, ev, data);
+ if (ev.isPropagationStopped()) {
+ count--;
+ return;
+ }
+
+ // get all other elements within this element that listen to move
+ // and trigger their resize events
+ var index = bound.index(this),
+ length = bound.length,
+ child, sub;
+
+ // if index == -1 it's the window
+ while (++index < length && (child = bound[index]) && (isWindow || $.contains(where, child))) {
+
+ // call the event
+ dispatch.call(child, ev, data);
+
+ if (ev.isPropagationStopped()) {
+ // move index until the item is not in the current child
+ while (++index < length && (sub = bound[index])) {
+ if (!$.contains(child, sub)) {
+ // set index back one
+ index--;
+ break
+ }
+ }
+ }
+ }
+
+ // prevent others from responding
+ ev.stopImmediatePropagation();
+ count--;
+ } else {
+ handleObj.origHandler.call(this, ev, data);
+ }
+ }
+ }
+ };
+
+ // automatically bind on these
+ $([document, window]).bind(name, function () {});
+
+ return $.event.special[name];
+ }
+
+ // ## jquery/event/drag/core/core.js
+ if (!$.event.special.move) {
+ $.event.reverse('move');
+ }
+
+ //modify live
+ //steal the live handler ....
+ var bind = function (object, method) {
+ var args = Array.prototype.slice.call(arguments, 2);
+ return function () {
+ var args2 = [this].concat(args, $.makeArray(arguments));
+ return method.apply(object, args2);
+ };
+ },
+ event = $.event,
+ // function to clear the window selection if there is one
+ clearSelection = window.getSelection ?
+ function () {
+ window.getSelection().removeAllRanges()
+ } : function () {},
+
+ supportTouch = "ontouchend" in document,
+ // Use touch events or map it to mouse events
+ startEvent = supportTouch ? "touchstart" : "mousedown",
+ stopEvent = supportTouch ? "touchend" : "mouseup",
+ moveEvent = supportTouch ? "touchmove" : "mousemove",
+ // On touchmove events the default (scrolling) event has to be prevented
+ preventTouchScroll = function (ev) {
+ ev.preventDefault();
+ };
+
+
+ $.Drag = function () {};
+
+
+ $.extend($.Drag, {
+ lowerName: "drag",
+ current: null,
+ distance: 0,
+
+ mousedown: function (ev, element) {
+ var isLeftButton = ev.button === 0 || ev.button == 1,
+ doEvent = isLeftButton || supportTouch;
+
+ if (!doEvent || this.current) {
+ return;
+ }
+
+ //create Drag
+ var drag = new $.Drag(),
+ delegate = ev.delegateTarget || element,
+ selector = ev.handleObj.selector,
+ self = this;
+ this.current = drag;
+
+ drag.setup({
+ element: element,
+ delegate: ev.delegateTarget || element,
+ selector: ev.handleObj.selector,
+ moved: false,
+ _distance: this.distance,
+ callbacks: {
+ dragdown: event.find(delegate, ["dragdown"], selector),
+ draginit: event.find(delegate, ["draginit"], selector),
+ dragover: event.find(delegate, ["dragover"], selector),
+ dragmove: event.find(delegate, ["dragmove"], selector),
+ dragout: event.find(delegate, ["dragout"], selector),
+ dragend: event.find(delegate, ["dragend"], selector),
+ dragcleanup: event.find(delegate, ["dragcleanup"], selector)
+ },
+ destroyed: function () {
+ self.current = null;
+ }
+ }, ev);
+ }
+ });
+
+
+ $.extend($.Drag.prototype, {
+ setup: function (options, ev) {
+ $.extend(this, options);
+
+ this.element = $(this.element);
+ this.event = ev;
+ this.moved = false;
+ this.allowOtherDrags = false;
+ var mousemove = bind(this, this.mousemove),
+ mouseup = bind(this, this.mouseup);
+ this._mousemove = mousemove;
+ this._mouseup = mouseup;
+ this._distance = options.distance ? options.distance : 0;
+
+ //where the mouse is located
+ this.mouseStartPosition = ev.vector();
+
+ $(document).bind(moveEvent, mousemove);
+ $(document).bind(stopEvent, mouseup);
+ if (supportTouch) {
+ // On touch devices we want to disable scrolling
+ $(document).bind(moveEvent, preventTouchScroll);
+ }
+
+ if (!this.callEvents('down', this.element, ev)) {
+ this.noSelection(this.delegate);
+ //this is for firefox
+ clearSelection();
+ }
+ },
+
+
+
+ destroy: function () {
+ // Unbind the mouse handlers attached for dragging
+ $(document).unbind(moveEvent, this._mousemove);
+ $(document).unbind(stopEvent, this._mouseup);
+ if (supportTouch) {
+ // Enable scrolling again for touch devices when the drag is done
+ $(document).unbind(moveEvent, preventTouchScroll);
+ }
+
+ if (!this.moved) {
+ this.event = this.element = null;
+ }
+
+ if (!supportTouch) {
+ this.selection(this.delegate);
+ }
+ this.destroyed();
+ },
+ mousemove: function (docEl, ev) {
+ if (!this.moved) {
+ var dist = Math.sqrt(Math.pow(ev.pageX - this.event.pageX, 2) + Math.pow(ev.pageY - this.event.pageY, 2));
+ // Don't initialize the drag if it hasn't been moved the minimum distance
+ if (dist < this._distance) {
+ return false;
+ }
+ // Otherwise call init and indicate that the drag has moved
+ this.init(this.element, ev);
+ this.moved = true;
+ }
+
+ this.element.trigger('move', this);
+ var pointer = ev.vector();
+ if (this._start_position && this._start_position.equals(pointer)) {
+ return;
+ }
+ this.draw(pointer, ev);
+ },
+
+ mouseup: function (docEl, event) {
+ //if there is a current, we should call its dragstop
+ if (this.moved) {
+ this.end(event);
+ }
+ this.destroy();
+ },
+
+
+ noSelection: function (elm) {
+ elm = elm || this.delegate
+ document.documentElement.onselectstart = function () {
+ // Disables selection
+ return false;
+ };
+ document.documentElement.unselectable = "on";
+ this.selectionDisabled = (this.selectionDisabled ? this.selectionDisabled.add(elm) : $(elm));
+ this.selectionDisabled.css('-moz-user-select', '-moz-none');
+ },
+
+
+ selection: function () {
+ if (this.selectionDisabled) {
+ document.documentElement.onselectstart = function () {};
+ document.documentElement.unselectable = "off";
+ this.selectionDisabled.css('-moz-user-select', '');
+ }
+ },
+
+ init: function (element, event) {
+ element = $(element);
+ //the element that has been clicked on
+ var startElement = (this.movingElement = (this.element = $(element)));
+ //if a mousemove has come after the click
+ //if the drag has been cancelled
+ this._cancelled = false;
+ this.event = event;
+
+
+ this.mouseElementPosition = this.mouseStartPosition.minus(this.element.offsetv()); //where the mouse is on the Element
+ this.callEvents('init', element, event);
+
+ // Check what they have set and respond accordingly if they canceled
+ if (this._cancelled === true) {
+ return;
+ }
+ // if they set something else as the element
+ this.startPosition = startElement != this.movingElement ? this.movingElement.offsetv() : this.currentDelta();
+
+ this.makePositioned(this.movingElement);
+ // Adjust the drag elements z-index to a high value
+ this.oldZIndex = this.movingElement.css('zIndex');
+ this.movingElement.css('zIndex', 1000);
+ if (!this._only && this.constructor.responder) {
+ // calls $.Drop.prototype.compile if there is a drop element
+ this.constructor.responder.compile(event, this);
+ }
+ },
+ makePositioned: function (that) {
+ var style, pos = that.css('position');
+
+ // Position properly, set top and left to 0px for Opera
+ if (!pos || pos == 'static') {
+ style = {
+ position: 'relative'
+ };
+
+ if (window.opera) {
+ style.top = '0px';
+ style.left = '0px';
+ }
+ that.css(style);
+ }
+ },
+ callEvents: function (type, element, event, drop) {
+ var i, cbs = this.callbacks[this.constructor.lowerName + type];
+ for (i = 0; i < cbs.length; i++) {
+ cbs[i].call(element, event, this, drop);
+ }
+ return cbs.length;
+ },
+
+ currentDelta: function () {
+ return new $.Vector(parseInt(this.movingElement.css('left'), 10) || 0, parseInt(this.movingElement.css('top'), 10) || 0);
+ },
+ //draws the position of the dragmove object
+ draw: function (pointer, event) {
+ // only drag if we haven't been cancelled;
+ if (this._cancelled) {
+ return;
+ }
+ clearSelection();
+
+ // the offset between the mouse pointer and the representative that the user asked for
+ this.location = pointer.minus(this.mouseElementPosition);
+
+ // call move events
+ this.move(event);
+ if (this._cancelled) {
+ return;
+ }
+ if (!event.isDefaultPrevented()) {
+ this.position(this.location);
+ }
+
+ // fill in
+ if (!this._only && this.constructor.responder) {
+ this.constructor.responder.show(pointer, this, event);
+ }
+ },
+
+ position: function (newOffsetv) { //should draw it on the page
+ var style, dragged_element_css_offset = this.currentDelta(),
+ // the drag element's current left + top css attributes
+ // the vector between the movingElement's page and css positions
+ // this can be thought of as the original offset
+ dragged_element_position_vector = this.movingElement.offsetv().minus(dragged_element_css_offset);
+ this.required_css_position = newOffsetv.minus(dragged_element_position_vector);
+
+ this.offsetv = newOffsetv;
+ style = this.movingElement[0].style;
+ if (!this._cancelled && !this._horizontal) {
+ style.top = this.required_css_position.top() + "px";
+ }
+ if (!this._cancelled && !this._vertical) {
+ style.left = this.required_css_position.left() + "px";
+ }
+ },
+ move: function (event) {
+ this.callEvents('move', this.element, event);
+ },
+ over: function (event, drop) {
+ this.callEvents('over', this.element, event, drop);
+ },
+ out: function (event, drop) {
+ this.callEvents('out', this.element, event, drop);
+ },
+
+ end: function (event) {
+ // If canceled do nothing
+ if (this._cancelled) {
+ return;
+ }
+ // notify the responder - usually a $.Drop instance
+ if (!this._only && this.constructor.responder) {
+ this.constructor.responder.end(event, this);
+ }
+
+ this.callEvents('end', this.element, event);
+
+ if (this._revert) {
+ var self = this;
+ // animate moving back to original position
+ this.movingElement.animate({
+ top: this.startPosition.top() + "px",
+ left: this.startPosition.left() + "px"
+ }, function () {
+ self.cleanup.apply(self, arguments);
+ });
+ }
+ else {
+ this.cleanup(event);
+ }
+ this.event = null;
+ },
+
+ cleanup: function (event) {
+ this.movingElement.css({
+ zIndex: this.oldZIndex
+ });
+ if (this.movingElement[0] !== this.element[0] && !this.movingElement.has(this.element[0]).length && !this.element.has(this.movingElement[0]).length) {
+ this.movingElement.css({
+ display: 'none'
+ });
+ }
+ if (this._removeMovingElement) {
+ // Remove the element when using drag.ghost()
+ this.movingElement.remove();
+ }
+
+ if (event) {
+ this.callEvents('cleanup', this.element, event);
+ }
+
+ this.movingElement = this.element = this.event = null;
+ },
+
+ cancel: function () {
+ this._cancelled = true;
+ if (!this._only && this.constructor.responder) {
+ // clear the drops
+ this.constructor.responder.clear(this.event.vector(), this, this.event);
+ }
+ this.destroy();
+
+ },
+
+ ghost: function (parent) {
+ // create a ghost by cloning the source element and attach the clone to the dom after the source element
+ var ghost = this.movingElement.clone().css('position', 'absolute');
+ if (parent) {
+ $(parent).append(ghost);
+ } else {
+ $(this.movingElement).after(ghost)
+ }
+ ghost.width(this.movingElement.width()).height(this.movingElement.height());
+ // put the ghost in the right location ...
+ ghost.offset(this.movingElement.offset())
+
+ // store the original element and make the ghost the dragged element
+ this.movingElement = ghost;
+ this.noSelection(ghost)
+ this._removeMovingElement = true;
+ return ghost;
+ },
+
+ representative: function (element, offsetX, offsetY) {
+ this._offsetX = offsetX || 0;
+ this._offsetY = offsetY || 0;
+
+ var p = this.mouseStartPosition;
+ // Just set the representative as the drag element
+ this.movingElement = $(element);
+ this.movingElement.css({
+ top: (p.y() - this._offsetY) + "px",
+ left: (p.x() - this._offsetX) + "px",
+ display: 'block',
+ position: 'absolute'
+ }).show();
+ this.noSelection(this.movingElement)
+ this.mouseElementPosition = new $.Vector(this._offsetX, this._offsetY);
+ return this;
+ },
+
+ revert: function (val) {
+ this._revert = val === undefined ? true : val;
+ return this;
+ },
+
+ vertical: function () {
+ this._vertical = true;
+ return this;
+ },
+
+ horizontal: function () {
+ this._horizontal = true;
+ return this;
+ },
+
+ only: function (only) {
+ return (this._only = (only === undefined ? true : only));
+ },
+
+
+ distance: function (val) {
+ if (val !== undefined) {
+ this._distance = val;
+ return this;
+ } else {
+ return this._distance
+ }
+ }
+ });
+
+ event.setupHelper([
+
+ 'dragdown',
+
+ 'draginit',
+
+ 'dragover',
+
+ 'dragmove',
+
+ 'dragout',
+
+ 'dragend',
+
+ 'dragcleanup'], startEvent, function (e) {
+ $.Drag.mousedown.call($.Drag, e, this);
+ });
+
+ // ## jquery/event/drag/step/step.js
+ var round = function (x, m) {
+ return Math.round(x / m) * m;
+ }
+
+ $.Drag.prototype.
+
+ step = function (amount, container, center) {
+ //on draws ... make sure this happens
+ if (typeof amount == 'number') {
+ amount = {
+ x: amount,
+ y: amount
+ }
+ }
+ container = container || $(document.body);
+ this._step = amount;
+
+ var styles = container.styles("borderTopWidth", "paddingTop", "borderLeftWidth", "paddingLeft");
+ var top = parseInt(styles.borderTopWidth) + parseInt(styles.paddingTop),
+ left = parseInt(styles.borderLeftWidth) + parseInt(styles.paddingLeft);
+
+ this._step.offset = container.offsetv().plus(left, top);
+ this._step.center = center;
+ return this;
+ };
+
+ (function () {
+ var oldPosition = $.Drag.prototype.position;
+ $.Drag.prototype.position = function (offsetPositionv) {
+ //adjust required_css_position accordingly
+ if (this._step) {
+ var step = this._step,
+ center = step.center && step.center.toLowerCase(),
+ movingSize = this.movingElement.dimensionsv('outer'),
+ lot = step.offset.top() - (center && center != 'x' ? movingSize.height() / 2 : 0),
+ lof = step.offset.left() - (center && center != 'y' ? movingSize.width() / 2 : 0);
+
+ if (this._step.x) {
+ offsetPositionv.left(Math.round(lof + round(offsetPositionv.left() - lof, this._step.x)))
+ }
+ if (this._step.y) {
+ offsetPositionv.top(Math.round(lot + round(offsetPositionv.top() - lot, this._step.y)))
+ }
+ }
+
+ oldPosition.call(this, offsetPositionv)
+ }
+ })();
+
+ // ## jquery/event/drag/limit/limit.js
+ $.Drag.prototype
+
+ .limit = function (container, center) {
+ //on draws ... make sure this happens
+ var styles = container.styles('borderTopWidth', 'paddingTop', 'borderLeftWidth', 'paddingLeft'),
+ paddingBorder = new $.Vector(
+ parseInt(styles.borderLeftWidth, 10) + parseInt(styles.paddingLeft, 10) || 0, parseInt(styles.borderTopWidth, 10) + parseInt(styles.paddingTop, 10) || 0);
+
+ this._limit = {
+ offset: container.offsetv().plus(paddingBorder),
+ size: container.dimensionsv(),
+ center: center === true ? 'both' : center
+ };
+ return this;
+ };
+
+ var oldPosition = $.Drag.prototype.position;
+ $.Drag.prototype.position = function (offsetPositionv) {
+ //adjust required_css_position accordingly
+ if (this._limit) {
+ var limit = this._limit,
+ center = limit.center && limit.center.toLowerCase(),
+ movingSize = this.movingElement.dimensionsv('outer'),
+ halfHeight = center && center != 'x' ? movingSize.height() / 2 : 0,
+ halfWidth = center && center != 'y' ? movingSize.width() / 2 : 0,
+ lot = limit.offset.top(),
+ lof = limit.offset.left(),
+ height = limit.size.height(),
+ width = limit.size.width();
+
+ //check if we are out of bounds ...
+ //above
+ if (offsetPositionv.top() + halfHeight < lot) {
+ offsetPositionv.top(lot - halfHeight);
+ }
+ //below
+ if (offsetPositionv.top() + movingSize.height() - halfHeight > lot + height) {
+ offsetPositionv.top(lot + height - movingSize.height() + halfHeight);
+ }
+ //left
+ if (offsetPositionv.left() + halfWidth < lof) {
+ offsetPositionv.left(lof - halfWidth);
+ }
+ //right
+ if (offsetPositionv.left() + movingSize.width() - halfWidth > lof + width) {
+ offsetPositionv.left(lof + width - movingSize.left() + halfWidth);
+ }
+ }
+
+ oldPosition.call(this, offsetPositionv);
+ };
+
+ // ## jquery/event/drop/drop.js
+ var event = $.event;
+
+ var eventNames = [
+
+ "dropover",
+
+ "dropon",
+
+ "dropout",
+
+ "dropinit",
+
+ "dropmove",
+
+ "dropend"];
+
+
+ $.Drop = function (callbacks, element) {
+ $.extend(this, callbacks);
+ this.element = $(element);
+ }
+ // add the elements ...
+ $.each(eventNames, function () {
+ event.special[this] = {
+ add: function (handleObj) {
+ //add this element to the compiles list
+ var el = $(this),
+ current = (el.data("dropEventCount") || 0);
+ el.data("dropEventCount", current + 1)
+ if (current == 0) {
+ $.Drop.addElement(this);
+ }
+ },
+ remove: function () {
+ var el = $(this),
+ current = (el.data("dropEventCount") || 0);
+ el.data("dropEventCount", current - 1)
+ if (current <= 1) {
+ $.Drop.removeElement(this);
+ }
+ }
+ }
+ });
+
+ $.extend($.Drop, {
+
+ lowerName: "drop",
+ _rootElements: [],
+ //elements that are listening for drops
+ _elements: $(),
+ //elements that can be dropped on
+ last_active: [],
+ endName: "dropon",
+ // adds an element as a 'root' element
+ // this element might have events that we need to respond to
+ addElement: function (el) {
+ // check other elements
+ for (var i = 0; i < this._rootElements.length; i++) {
+ if (el == this._rootElements[i]) return;
+ }
+ this._rootElements.push(el);
+ },
+ removeElement: function (el) {
+ for (var i = 0; i < this._rootElements.length; i++) {
+ if (el == this._rootElements[i]) {
+ this._rootElements.splice(i, 1)
+ return;
+ }
+ }
+ },
+
+ sortByDeepestChild: function (a, b) {
+ // Use jQuery.compare to compare two elements
+ var compare = a.element.compare(b.element);
+ if (compare & 16 || compare & 4) return 1;
+ if (compare & 8 || compare & 2) return -1;
+ return 0;
+ },
+
+ isAffected: function (point, moveable, responder) {
+ return ((responder.element != moveable.element) && (responder.element.within(point[0], point[1], responder._cache).length == 1));
+ },
+
+ deactivate: function (responder, mover, event) {
+ mover.out(event, responder)
+ responder.callHandlers(this.lowerName + 'out', responder.element[0], event, mover)
+ },
+
+ activate: function (responder, mover, event) { //this is where we should call over
+ mover.over(event, responder)
+ responder.callHandlers(this.lowerName + 'over', responder.element[0], event, mover);
+ },
+ move: function (responder, mover, event) {
+ responder.callHandlers(this.lowerName + 'move', responder.element[0], event, mover)
+ },
+
+ compile: function (event, drag) {
+ // if we called compile w/o a current drag
+ if (!this.dragging && !drag) {
+ return;
+ } else if (!this.dragging) {
+ this.dragging = drag;
+ this.last_active = [];
+ }
+ var el, drops, selector, dropResponders, newEls = [],
+ dragging = this.dragging;
+
+ // go to each root element and look for drop elements
+ for (var i = 0; i < this._rootElements.length; i++) { //for each element
+ el = this._rootElements[i]
+
+ // gets something like {"": ["dropinit"], ".foo" : ["dropover","dropmove"] }
+ var drops = $.event.findBySelector(el, eventNames)
+
+ // get drop elements by selector
+ for (selector in drops) {
+ dropResponders = selector ? jQuery(selector, el) : [el];
+
+ // for each drop element
+ for (var e = 0; e < dropResponders.length; e++) {
+
+ // add the callbacks to the element's Data
+ // there already might be data, so we merge it
+ if (this.addCallbacks(dropResponders[e], drops[selector], dragging)) {
+ newEls.push(dropResponders[e])
+ };
+ }
+ }
+ }
+ // once all callbacks are added, call init on everything ...
+ this.add(newEls, event, dragging)
+ },
+
+ // adds the drag callbacks object to the element or merges other callbacks ...
+ // returns true or false if the element is new ...
+ // onlyNew lets only new elements add themselves
+ addCallbacks: function (el, callbacks, onlyNew) {
+ var origData = $.data(el, "_dropData");
+ if (!origData) {
+ $.data(el, "_dropData", new $.Drop(callbacks, el));
+ return true;
+ } else if (!onlyNew) {
+ var origCbs = origData;
+ // merge data
+ for (var eventName in callbacks) {
+ origCbs[eventName] = origCbs[eventName] ? origCbs[eventName].concat(callbacks[eventName]) : callbacks[eventName];
+ }
+ return false;
+ }
+ },
+ // calls init on each element's drags.
+ // if its cancelled it's removed
+ // adds to the current elements ...
+ add: function (newEls, event, drag, dragging) {
+ var i = 0,
+ drop;
+
+ while (i < newEls.length) {
+ drop = $.data(newEls[i], "_dropData");
+ drop.callHandlers(this.lowerName + 'init', newEls[i], event, drag)
+ if (drop._canceled) {
+ newEls.splice(i, 1)
+ } else {
+ i++;
+ }
+ }
+ this._elements.push.apply(this._elements, newEls)
+ },
+ show: function (point, moveable, event) {
+ var element = moveable.element;
+ if (!this._elements.length) return;
+
+ var respondable, affected = [],
+ propagate = true,
+ i = 0,
+ j, la, toBeActivated, aff, oldLastActive = this.last_active,
+ responders = [],
+ self = this,
+ drag;
+
+ // what's still affected ... we can also move element out here
+ while (i < this._elements.length) {
+ drag = $.data(this._elements[i], "_dropData");
+
+ if (!drag) {
+ this._elements.splice(i, 1)
+ }
+ else {
+ i++;
+ if (self.isAffected(point, moveable, drag)) {
+ affected.push(drag);
+ }
+ }
+ }
+
+ // we should only trigger on lowest children
+ affected.sort(this.sortByDeepestChild);
+ event.stopRespondPropagate = function () {
+ propagate = false;
+ }
+
+ toBeActivated = affected.slice();
+
+ // all these will be active
+ this.last_active = affected;
+
+ // deactivate everything in last_active that isn't active
+ for (j = 0; j < oldLastActive.length; j++) {
+ la = oldLastActive[j];
+ i = 0;
+ while ((aff = toBeActivated[i])) {
+ if (la == aff) {
+ toBeActivated.splice(i, 1);
+ break;
+ } else {
+ i++;
+ }
+ }
+ if (!aff) {
+ this.deactivate(la, moveable, event);
+ }
+ if (!propagate) return;
+ }
+ for (var i = 0; i < toBeActivated.length; i++) {
+ this.activate(toBeActivated[i], moveable, event);
+ if (!propagate) return;
+ }
+
+ // activate everything in affected that isn't in last_active
+ for (i = 0; i < affected.length; i++) {
+ this.move(affected[i], moveable, event);
+
+ if (!propagate) return;
+ }
+ },
+ end: function (event, moveable) {
+ var la, endName = this.lowerName + 'end',
+ onEvent = $.Event(this.endName, event),
+ dropData;
+
+ // call dropon
+ // go through the actives ... if you are over one, call dropped on it
+ for (var i = 0; i < this.last_active.length; i++) {
+ la = this.last_active[i]
+ if (this.isAffected(event.vector(), moveable, la) && la[this.endName]) {
+ la.callHandlers(this.endName, null, onEvent, moveable);
+ }
+
+ if (onEvent.isPropagationStopped()) {
+ break;
+ }
+ }
+ // call dropend
+ for (var r = 0; r < this._elements.length; r++) {
+ dropData = $.data(this._elements[r], "_dropData");
+ dropData && dropData.callHandlers(endName, null, event, moveable);
+ }
+
+ this.clear();
+ },
+
+ clear: function () {
+ this._elements.each(function () {
+ // remove temporary drop data
+ $.removeData(this, "_dropData")
+ })
+ this._elements = $();
+ delete this.dragging;
+ }
+ })
+ $.Drag.responder = $.Drop;
+
+ $.extend($.Drop.prototype, {
+
+ callHandlers: function (method, el, ev, drag) {
+ var length = this[method] ? this[method].length : 0
+ for (var i = 0; i < length; i++) {
+ this[method][i].call(el || this.element[0], ev, this, drag)
+ }
+ },
+
+ cache: function (value) {
+ this._cache = value != null ? value : true;
+ },
+
+ cancel: function () {
+ this._canceled = true;
+ }
+ });
+
+ // ## jquery/event/drag/scroll/scroll.js
+ //needs drop to determine if respondable
+ $.Drag.prototype.
+
+ scrolls = function (elements, options) {
+ var elements = $(elements);
+
+ for (var i = 0; i < elements.length; i++) {
+ this.constructor.responder._elements.push(elements.eq(i).data("_dropData", new $.Scrollable(elements[i], options))[0])
+ }
+ },
+
+ $.Scrollable = function (element, options) {
+ this.element = jQuery(element);
+ this.options = $.extend({
+ // when we should start scrolling
+ distance: 30,
+ // how far we should move
+ delta: function (diff, distance) {
+ return (distance - diff) / 2;
+ },
+ direction: "xy"
+ }, options);
+ this.x = this.options.direction.indexOf("x") != -1;
+ this.y = this.options.direction.indexOf("y") != -1;
+ }
+ $.extend($.Scrollable.prototype, {
+ init: function (element) {
+ this.element = jQuery(element);
+ },
+ callHandlers: function (method, el, ev, drag) {
+ this[method](el || this.element[0], ev, this, drag)
+ },
+ dropover: function () {
+
+ },
+ dropon: function () {
+ this.clear_timeout();
+ },
+ dropout: function () {
+ this.clear_timeout();
+ },
+ dropinit: function () {
+
+ },
+ dropend: function () {},
+ clear_timeout: function () {
+ if (this.interval) {
+ clearTimeout(this.interval)
+ this.interval = null;
+ }
+ },
+ distance: function (diff) {
+ return (30 - diff) / 2;
+ },
+ dropmove: function (el, ev, drop, drag) {
+
+ //if we were about to call a move, clear it.
+ this.clear_timeout();
+
+ //position of the mouse
+ var mouse = ev.vector(),
+
+ //get the object we are going to get the boundries of
+ location_object = $(el == document.documentElement ? window : el),
+
+ //get the dimension and location of that object
+ dimensions = location_object.dimensionsv('outer'),
+ position = location_object.offsetv(),
+
+ //how close our mouse is to the boundries
+ bottom = position.y() + dimensions.y() - mouse.y(),
+ top = mouse.y() - position.y(),
+ right = position.x() + dimensions.x() - mouse.x(),
+ left = mouse.x() - position.x(),
+
+ //how far we should scroll
+ dx = 0,
+ dy = 0,
+ distance = this.options.distance;
+
+ //check if we should scroll
+ if (bottom < distance && this.y) {
+ dy = this.options.delta(bottom, distance);
+ } else if (top < distance && this.y) {
+ dy = -this.options.delta(top, distance);
+ }
+
+ if (right < distance && this.options && this.x) {
+ dx = this.options.delta(right, distance);
+ } else if (left < distance && this.x) {
+ dx = -this.options.delta(left, distance);
+ }
+
+ //if we should scroll
+ if (dx || dy) {
+ //set a timeout that will create a mousemove on that object
+ var self = this;
+ this.interval = setTimeout(function () {
+ self.move($(el), drag.movingElement, dx, dy, ev, ev.clientX, ev.clientY, ev.screenX, ev.screenY)
+ }, 15)
+ }
+ },
+
+ move: function (scroll_element, drag_element, dx, dy, ev) {
+ scroll_element.scrollTop(scroll_element.scrollTop() + dy);
+ scroll_element.scrollLeft(scroll_element.scrollLeft() + dx);
+
+ drag_element.trigger(
+ $.event.fix({
+ type: "mousemove",
+ clientX: ev.clientX,
+ clientY: ev.clientY,
+ screenX: ev.screenX,
+ screenY: ev.screenY,
+ pageX: ev.pageX,
+ pageY: ev.pageY
+ }))
+ //drag_element.synthetic('mousemove',{clientX: x, clientY: y, screenX: sx, screenY: sy})
+ }
+ })
+
+ // ## jquery/event/drag/drag.js
+ // ## jquery/event/fastfix/fastfix.js
+ // http://bitovi.com/blog/2012/04/faster-jquery-event-fix.html
+ // https://gist.github.com/2377196
+ // IE 8 has Object.defineProperty but it only defines DOM Nodes. According to
+ // http://kangax.github.com/es5-compat-table/#define-property-ie-note
+ // All browser that have Object.defineProperties also support Object.defineProperty properly
+ if (Object.defineProperties) {
+ var
+ // Use defineProperty on an object to set the value and return it
+ set = function (obj, prop, val) {
+ if (val !== undefined) {
+ Object.defineProperty(obj, prop, {
+ value: val
+ });
+ }
+ return val;
+ },
+ // special converters
+ special = {
+ pageX: function (original) {
+ if (!original) {
+ return;
+ }
+
+ var eventDoc = this.target.ownerDocument || document;
+ doc = eventDoc.documentElement;
+ body = eventDoc.body;
+ return original.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
+ },
+ pageY: function (original) {
+ if (!original) {
+ return;
+ }
+
+ var eventDoc = this.target.ownerDocument || document;
+ doc = eventDoc.documentElement;
+ body = eventDoc.body;
+ return original.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
+ },
+ relatedTarget: function (original) {
+ if (!original) {
+ return;
+ }
+
+ return original.fromElement === this.target ? original.toElement : original.fromElement;
+ },
+ metaKey: function (originalEvent) {
+ if (!originalEvent) {
+ return;
+ }
+ return originalEvent.ctrlKey;
+ },
+ which: function (original) {
+ if (!original) {
+ return;
+ }
+
+ return original.charCode != null ? original.charCode : original.keyCode;
+ }
+ };
+
+ // Get all properties that should be mapped
+ $.each($.event.keyHooks.props.concat($.event.mouseHooks.props).concat($.event.props), function (i, prop) {
+ if (prop !== "target") {
+ (function () {
+ Object.defineProperty($.Event.prototype, prop, {
+ get: function () {
+ // get the original value, undefined when there is no original event
+ var originalValue = this.originalEvent && this.originalEvent[prop];
+ // overwrite getter lookup
+ return this['_' + prop] !== undefined ? this['_' + prop] : set(this, prop,
+ // if we have a special function and no value
+ special[prop] && originalValue === undefined ?
+ // call the special function
+ special[prop].call(this, this.originalEvent) :
+ // use the original value
+ originalValue)
+ },
+ set: function (newValue) {
+ // Set the property with underscore prefix
+ this['_' + prop] = newValue;
+ }
+ });
+ })();
+ }
+ });
+
+ $.event.fix = function (event) {
+ if (event[$.expando]) {
+ return event;
+ }
+ // Create a jQuery event with at minimum a target and type set
+ var originalEvent = event,
+ event = $.Event(originalEvent);
+ event.target = originalEvent.target;
+ // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
+ if (!event.target) {
+ event.target = originalEvent.srcElement || document;
+ }
+
+ // Target should not be a text node (#504, Safari)
+ if (event.target.nodeType === 3) {
+ event.target = event.target.parentNode;
+ }
+
+ return event;
+ }
+ }
+
+ // ## jquery/event/hover/hover.js
+
+ $.Hover = function () {
+ this._delay = $.Hover.delay;
+ this._distance = $.Hover.distance;
+ this._leave = $.Hover.leave
+ };
+
+ $.extend($.Hover, {
+
+ delay: 100,
+
+ distance: 10,
+ leave: 0
+ })
+
+
+ $.extend($.Hover.prototype, {
+
+ delay: function (delay) {
+ this._delay = delay;
+ return this;
+ },
+
+ distance: function (distance) {
+ this._distance = distance;
+ return this;
+ },
+
+ leave: function (leave) {
+ this._leave = leave;
+ return this;
+ }
+ })
+ var event = $.event,
+ handle = event.handle,
+ onmouseenter = function (ev) {
+ // now start checking mousemoves to update location
+ var delegate = ev.delegateTarget || ev.currentTarget;
+ var selector = ev.handleObj.selector;
+ var pending = $.data(delegate, "_hover" + selector);
+ // prevents another mouseenter until current has run its course
+ if (pending) {
+ // Under some circumstances, mouseleave may never fire
+ // (e.g., the element is removed while hovered)
+ // so if we've entered another element, wait the leave time,
+ // then force it to release.
+ if (!pending.forcing) {
+ pending.forcing = true;
+ clearTimeout(pending.leaveTimer);
+ var leaveTime = pending.leaving ? Math.max(0, pending.hover.leave - (new Date() - pending.leaving)) : pending.hover.leave;
+ var self = this;
+
+ setTimeout(function () {
+ pending.callHoverLeave();
+ onmouseenter.call(self, ev);
+ }, leaveTime);
+ }
+ return;
+ }
+ var loc = {
+ pageX: ev.pageX,
+ pageY: ev.pageY
+ },
+ // The current distance
+ dist = 0,
+ // Timer that checks for the distance travelled
+ timer, enteredEl = this,
+ // If we are hovered
+ hovered = false,
+ // The previous event
+ lastEv = ev,
+ // The $.Hover instance passed to events
+ hover = new $.Hover(),
+ // timer if hover.leave has been called
+ leaveTimer,
+ // Callback for triggering hoverleave
+ callHoverLeave = function () {
+ $.each(event.find(delegate, ["hoverleave"], selector), function () {
+ this.call(enteredEl, ev, hover)
+ })
+ cleanUp();
+ },
+ mousemove = function (ev) {
+ clearTimeout(leaveTimer);
+ // Update the distance and location
+ dist += Math.pow(ev.pageX - loc.pageX, 2) + Math.pow(ev.pageY - loc.pageY, 2);
+ loc = {
+ pageX: ev.pageX,
+ pageY: ev.pageY
+ }
+ lastEv = ev
+ },
+ mouseleave = function (ev) {
+ clearTimeout(timer);
+ if (hovered) {
+ // go right away
+ if (hover._leave === 0) {
+ callHoverLeave();
+ } else {
+ clearTimeout(leaveTimer);
+ // leave the hover after the time set in hover.leave(time)
+ pending.leaving = new Date();
+ leaveTimer = pending.leaveTimer = setTimeout(function () {
+ callHoverLeave();
+ }, hover._leave)
+ }
+ } else {
+ cleanUp();
+ }
+ },
+ cleanUp = function () {
+ // Unbind all events and data
+ $(enteredEl).unbind("mouseleave", mouseleave)
+ $(enteredEl).unbind("mousemove", mousemove);
+ $.removeData(delegate, "_hover" + selector)
+ },
+ hoverenter = function () {
+ $.each(event.find(delegate, ["hoverenter"], selector), function () {
+ this.call(enteredEl, lastEv, hover)
+ })
+ hovered = true;
+ };
+ pending = {
+ callHoverLeave: callHoverLeave,
+ hover: hover
+ };
+ $.data(delegate, "_hover" + selector, pending);
+
+ // Bind the mousemove event
+ $(enteredEl).bind("mousemove", mousemove).bind("mouseleave", mouseleave);
+ // call hoverinit for each element with the hover instance
+ $.each(event.find(delegate, ["hoverinit"], selector), function () {
+ this.call(enteredEl, ev, hover)
+ })
+
+ if (hover._delay === 0) {
+ hoverenter();
+ } else {
+ timer = setTimeout(function () {
+ // check that we aren't moving around
+ if (dist < hover._distance && $(enteredEl).queue().length == 0) {
+ hoverenter();
+ return;
+ } else {
+ // Reset distance and timer
+ dist = 0;
+ timer = setTimeout(arguments.callee, hover._delay)
+ }
+ }, hover._delay);
+ }
+ };
+
+
+ // Attach events
+ event.setupHelper([
+
+ "hoverinit",
+
+ "hoverenter",
+
+ "hoverleave",
+
+ "hovermove"], "mouseenter", onmouseenter)
+
+ // ## jquery/event/key/key.js
+
+ // copied from jQuery 1.8.3
+ var uaMatch = function (ua) {
+ ua = ua.toLowerCase();
+
+ var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || /(webkit)[ \/]([\w.]+)/.exec(ua) || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || /(msie) ([\w.]+)/.exec(ua) || ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || [];
+
+ return {
+ browser: match[1] || "",
+ version: match[2] || "0"
+ };
+ }
+
+ var keymap = {},
+ reverseKeyMap = {},
+ currentBrowser = uaMatch(navigator.userAgent).browser;
+
+
+ $.event.key = function (browser, map) {
+ if (browser === undefined) {
+ return keymap;
+ }
+
+ if (map === undefined) {
+ map = browser;
+ browser = currentBrowser;
+ }
+
+ // extend the keymap
+ if (!keymap[browser]) {
+ keymap[browser] = {};
+ }
+ $.extend(keymap[browser], map);
+ // and also update the reverse keymap
+ if (!reverseKeyMap[browser]) {
+ reverseKeyMap[browser] = {};
+ }
+ for (var name in map) {
+ reverseKeyMap[browser][map[name]] = name;
+ }
+ };
+
+ $.event.key({
+ // backspace
+ '\b': '8',
+
+ // tab
+ '\t': '9',
+
+ // enter
+ '\r': '13',
+
+ // special
+ 'shift': '16',
+ 'ctrl': '17',
+ 'alt': '18',
+
+ // others
+ 'pause-break': '19',
+ 'caps': '20',
+ 'escape': '27',
+ 'num-lock': '144',
+ 'scroll-lock': '145',
+ 'print': '44',
+
+ // navigation
+ 'page-up': '33',
+ 'page-down': '34',
+ 'end': '35',
+ 'home': '36',
+ 'left': '37',
+ 'up': '38',
+ 'right': '39',
+ 'down': '40',
+ 'insert': '45',
+ 'delete': '46',
+
+ // normal characters
+ ' ': '32',
+ '0': '48',
+ '1': '49',
+ '2': '50',
+ '3': '51',
+ '4': '52',
+ '5': '53',
+ '6': '54',
+ '7': '55',
+ '8': '56',
+ '9': '57',
+ 'a': '65',
+ 'b': '66',
+ 'c': '67',
+ 'd': '68',
+ 'e': '69',
+ 'f': '70',
+ 'g': '71',
+ 'h': '72',
+ 'i': '73',
+ 'j': '74',
+ 'k': '75',
+ 'l': '76',
+ 'm': '77',
+ 'n': '78',
+ 'o': '79',
+ 'p': '80',
+ 'q': '81',
+ 'r': '82',
+ 's': '83',
+ 't': '84',
+ 'u': '85',
+ 'v': '86',
+ 'w': '87',
+ 'x': '88',
+ 'y': '89',
+ 'z': '90',
+ // normal-characters, numpad
+ 'num0': '96',
+ 'num1': '97',
+ 'num2': '98',
+ 'num3': '99',
+ 'num4': '100',
+ 'num5': '101',
+ 'num6': '102',
+ 'num7': '103',
+ 'num8': '104',
+ 'num9': '105',
+ '*': '106',
+ '+': '107',
+ '-': '109',
+ '.': '110',
+ // normal-characters, others
+ '/': '111',
+ ';': '186',
+ '=': '187',
+ ',': '188',
+ '-': '189',
+ '.': '190',
+ '/': '191',
+ '`': '192',
+ '[': '219',
+ '\\': '220',
+ ']': '221',
+ "'": '222',
+
+ // ignore these, you shouldn't use them
+ 'left window key': '91',
+ 'right window key': '92',
+ 'select key': '93',
+
+
+ 'f1': '112',
+ 'f2': '113',
+ 'f3': '114',
+ 'f4': '115',
+ 'f5': '116',
+ 'f6': '117',
+ 'f7': '118',
+ 'f8': '119',
+ 'f9': '120',
+ 'f10': '121',
+ 'f11': '122',
+ 'f12': '123'
+ });
+
+
+ $.Event.prototype.keyName = function () {
+ var event = this,
+ test = /\w/,
+ // It can be either keyCode or charCode.
+ // Look both cases up in the reverse key map and converted to a string
+ key_Key = reverseKeyMap[currentBrowser][(event.keyCode || event.which) + ""],
+ char_Key = String.fromCharCode(event.keyCode || event.which),
+ key_Char = event.charCode && reverseKeyMap[currentBrowser][event.charCode + ""],
+ char_Char = event.charCode && String.fromCharCode(event.charCode);
+
+ if (char_Char && test.test(char_Char)) {
+ // string representation of event.charCode
+ return char_Char.toLowerCase()
+ }
+ if (key_Char && test.test(key_Char)) {
+ // reverseKeyMap representation of event.charCode
+ return char_Char.toLowerCase()
+ }
+ if (char_Key && test.test(char_Key)) {
+ // string representation of event.keyCode
+ return char_Key.toLowerCase()
+ }
+ if (key_Key && test.test(key_Key)) {
+ // reverseKeyMap representation of event.keyCode
+ return key_Key.toLowerCase()
+ }
+
+ if (event.type == 'keypress') {
+ // keypress doesn't capture everything
+ return event.keyCode ? String.fromCharCode(event.keyCode) : String.fromCharCode(event.which)
+ }
+
+ if (!event.keyCode && event.which) {
+ // event.which
+ return String.fromCharCode(event.which)
+ }
+
+ // default
+ return reverseKeyMap[currentBrowser][event.keyCode + ""]
+ }
+
+ // ## jquery/event/pause/pause.js
+ var current, rnamespaces = /\.(.*)$/,
+ returnFalse = function () {
+ return false
+ },
+ returnTrue = function () {
+ return true
+ };
+
+ $.Event.prototype.isPaused = returnFalse
+
+ $.Event.prototype.pause = function () {
+ // stop the event from continuing temporarily
+ // keep the current state of the event ...
+ this.pausedState = {
+ isDefaultPrevented: this.isDefaultPrevented() ? returnTrue : returnFalse,
+ isPropagationStopped: this.isPropagationStopped() ? returnTrue : returnFalse
+ };
+
+ this.stopImmediatePropagation();
+ this.preventDefault();
+ this.isPaused = returnTrue;
+ };
+
+ $.Event.prototype.resume = function () {
+ // temporarily remove all event handlers of this type
+ var handleObj = this.handleObj,
+ currentTarget = this.currentTarget;
+ // temporarily overwrite special handle
+ var origType = $.event.special[handleObj.origType],
+ origHandle = origType && origType.handle;
+
+ if (!origType) {
+ $.event.special[handleObj.origType] = {};
+ }
+ $.event.special[handleObj.origType].handle = function (ev) {
+ // remove this once we have passed the handleObj
+ if (ev.handleObj === handleObj && ev.currentTarget === currentTarget) {
+ if (!origType) {
+ delete $.event.special[handleObj.origType];
+ } else {
+ $.event.special[handleObj.origType].handle = origHandle;
+ }
+ }
+ }
+ delete this.pausedState;
+ // reset stuff
+ this.isPaused = this.isImmediatePropagationStopped = returnFalse;
+
+ if (!this.isPropagationStopped()) {
+ // fire the event again, no events will get fired until
+ // same currentTarget / handler
+ $.event.trigger(this, [], this.target);
+ }
+
+ };
+
+ // ## jquery/event/resize/resize.js
+ var
+ // bind on the window window resizes to happen
+ win = $(window),
+ windowWidth = 0,
+ windowHeight = 0,
+ timer;
+
+ $(function () {
+ windowWidth = win.width();
+ windowHeight = win.height();
+ });
+
+ $.event.reverse('resize', {
+ handler: function (ev, data) {
+ var isWindow = this === window;
+
+ // if we are the window and a real resize has happened
+ // then we check if the dimensions actually changed
+ // if they did, we will wait a brief timeout and
+ // trigger resize on the window
+ // this is for IE, to prevent window resize 'infinate' loop issues
+ if (isWindow && ev.originalEvent) {
+ var width = win.width(),
+ height = win.height();
+
+ if ((width != windowWidth || height != windowHeight)) {
+ //update the new dimensions
+ windowWidth = width;
+ windowHeight = height;
+ clearTimeout(timer)
+ timer = setTimeout(function () {
+ win.trigger("resize");
+ }, 1);
+
+ }
+ return true;
+ }
+ }
+ });
+
+ // ## jquery/event/swipe/swipe.js
+ var isPhantom = /Phantom/.test(navigator.userAgent),
+ supportTouch = !isPhantom && "ontouchend" in document,
+ scrollEvent = "touchmove scroll",
+ // Use touch events or map it to mouse events
+ touchStartEvent = supportTouch ? "touchstart" : "mousedown",
+ touchStopEvent = supportTouch ? "touchend" : "mouseup",
+ touchMoveEvent = supportTouch ? "touchmove" : "mousemove",
+ data = function (event) {
+ var d = event.originalEvent.touches ? event.originalEvent.touches[0] : event;
+ return {
+ time: (new Date).getTime(),
+ coords: [d.pageX, d.pageY],
+ origin: $(event.target)
+ };
+ };
+
+ var swipe = $.event.swipe = {
+
+ delay: 500,
+
+ max: 320,
+
+ min: 30
+ };
+
+ $.event.setupHelper([
+
+ "swipe",
+
+ 'swipeleft',
+
+ 'swiperight',
+
+ 'swipeup',
+
+ 'swipedown'], touchStartEvent, function (ev) {
+ var
+ // update with data when the event was started
+ start = data(ev),
+ stop, delegate = ev.delegateTarget || ev.currentTarget,
+ selector = ev.handleObj.selector,
+ entered = this;
+
+ function moveHandler(event) {
+ if (!start) {
+ return;
+ }
+ // update stop with the data from the current event
+ stop = data(event);
+
+ // prevent scrolling
+ if (Math.abs(start.coords[0] - stop.coords[0]) > 10) {
+ event.preventDefault();
+ }
+ };
+
+ // Attach to the touch move events
+ $(document.documentElement).bind(touchMoveEvent, moveHandler).one(touchStopEvent, function (event) {
+ $(this).unbind(touchMoveEvent, moveHandler);
+ // if start and stop contain data figure out if we have a swipe event
+ if (start && stop) {
+ // calculate the distance between start and stop data
+ var deltaX = Math.abs(start.coords[0] - stop.coords[0]),
+ deltaY = Math.abs(start.coords[1] - stop.coords[1]),
+ distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
+
+ // check if the delay and distance are matched
+ if (stop.time - start.time < swipe.delay && distance >= swipe.min && distance <= swipe.max) {
+ var events = ['swipe'];
+ // check if we moved horizontally
+ if (deltaX >= swipe.min && deltaY < swipe.min) {
+ // based on the x coordinate check if we moved left or right
+ events.push(start.coords[0] > stop.coords[0] ? "swipeleft" : "swiperight");
+ } else
+ // check if we moved vertically
+ if (deltaY >= swipe.min && deltaX < swipe.min) {
+ // based on the y coordinate check if we moved up or down
+ events.push(start.coords[1] < stop.coords[1] ? "swipedown" : "swipeup");
+ }
+
+ // trigger swipe events on this guy
+ $.each($.event.find(delegate, events, selector), function () {
+ this.call(entered, ev, {
+ start: start,
+ end: stop
+ })
+ })
+
+ }
+ }
+ // reset start and stop
+ start = stop = undefined;
+ })
+ });
+
+})(this, jQuery);
\ No newline at end of file
diff --git a/dist/lang/json.js b/dist/lang/json.js
new file mode 100644
index 00000000..20680c1c
--- /dev/null
+++ b/dist/lang/json.js
@@ -0,0 +1,198 @@
+(function($) {
+ /**
+ * @page jQuery.toJSON jQuery.toJSON
+ * @parent jquerymx.lang
+ *
+ * jQuery.toJSON( json-serializble )
+ *
+ * Converts the given argument into a JSON respresentation.
+ *
+ * If an object has a "toJSON" function, that will
+ * be used to get the representation.
+ * Non-integer/string keys are skipped in the
+ * object, as are keys that point to a function.
+ *
+ * json-serializble:
+ * The *thing* to be converted.
+ */
+ $.toJSON = function(o, replacer, space, recurse)
+ {
+ if (typeof(JSON) == 'object' && JSON.stringify)
+ return JSON.stringify(o, replacer, space);
+
+ if (!recurse && $.isFunction(replacer))
+ o = replacer("", o);
+
+ if (typeof space == "number")
+ space = " ".substring(0, space);
+ space = (typeof space == "string") ? space.substring(0, 10) : "";
+
+ var type = typeof(o);
+
+ if (o === null)
+ return "null";
+
+ if (type == "undefined" || type == "function")
+ return undefined;
+
+ if (type == "number" || type == "boolean")
+ return o + "";
+
+ if (type == "string")
+ return $.quoteString(o);
+
+ if (type == 'object')
+ {
+ if (typeof o.toJSON == "function")
+ return $.toJSON( o.toJSON(), replacer, space, true );
+
+ if (o.constructor === Date)
+ {
+ var month = o.getUTCMonth() + 1;
+ if (month < 10) month = '0' + month;
+
+ var day = o.getUTCDate();
+ if (day < 10) day = '0' + day;
+
+ var year = o.getUTCFullYear();
+
+ var hours = o.getUTCHours();
+ if (hours < 10) hours = '0' + hours;
+
+ var minutes = o.getUTCMinutes();
+ if (minutes < 10) minutes = '0' + minutes;
+
+ var seconds = o.getUTCSeconds();
+ if (seconds < 10) seconds = '0' + seconds;
+
+ var milli = o.getUTCMilliseconds();
+ if (milli < 100) milli = '0' + milli;
+ if (milli < 10) milli = '0' + milli;
+
+ return '"' + year + '-' + month + '-' + day + 'T' +
+ hours + ':' + minutes + ':' + seconds +
+ '.' + milli + 'Z"';
+ }
+
+ var process = ($.isFunction(replacer)) ?
+ function (k, v) { return replacer(k, v); } :
+ function (k, v) { return v; },
+ nl = (space) ? "\n" : "",
+ sp = (space) ? " " : "";
+
+ if (o.constructor === Array)
+ {
+ var ret = [];
+ for (var i = 0; i < o.length; i++)
+ ret.push(( $.toJSON( process(i, o[i]), replacer, space, true ) || "null" ).replace(/^/gm, space));
+
+ return "[" + nl + ret.join("," + nl) + nl + "]";
+ }
+
+ var pairs = [], proplist;
+ if ($.isArray(replacer)) {
+ proplist = $.map(replacer, function (v) {
+ return (typeof v == "string" || typeof v == "number") ?
+ v + "" :
+ null;
+ });
+ }
+ for (var k in o) {
+ var name, val, type = typeof k;
+
+ if (proplist && $.inArray(k + "", proplist) == -1)
+ continue;
+
+ if (type == "number")
+ name = '"' + k + '"';
+ else if (type == "string")
+ name = $.quoteString(k);
+ else
+ continue; //skip non-string or number keys
+
+ val = $.toJSON( process(k, o[k]), replacer, space, true );
+
+ if (typeof val == "undefined")
+ continue; //skip pairs where the value is a function.
+
+ pairs.push((name + ":" + sp + val).replace(/^/gm, space));
+ }
+
+ return "{" + nl + pairs.join("," + nl) + nl + "}";
+ }
+ };
+
+ /**
+ * @function jQuery.evalJSON
+ * Evaluates a given piece of json source.
+ **/
+ $.evalJSON = function(src)
+ {
+ if (typeof(JSON) == 'object' && JSON.parse)
+ return JSON.parse(src);
+ return eval("(" + src + ")");
+ };
+
+ /**
+ * @function jQuery.secureEvalJSON
+ * Evals JSON in a way that is *more* secure.
+ **/
+ $.secureEvalJSON = function(src)
+ {
+ if (typeof(JSON) == 'object' && JSON.parse)
+ return JSON.parse(src);
+
+ var filtered = src;
+ filtered = filtered.replace(/\\["\\\/bfnrtu]/g, '@');
+ filtered = filtered.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
+ filtered = filtered.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
+
+ if (/^[\],:{}\s]*$/.test(filtered))
+ return eval("(" + src + ")");
+ else
+ throw new SyntaxError("Error parsing JSON, source is not valid.");
+ };
+
+ /**
+ * @function jQuery.quoteString
+ *
+ * Returns a string-repr of a string, escaping quotes intelligently.
+ * Mostly a support function for toJSON.
+ *
+ * Examples:
+ *
+ * jQuery.quoteString("apple") //-> "apple"
+ *
+ * jQuery.quoteString('"Where are we going?", she asked.')
+ * // -> "\"Where are we going?\", she asked."
+ **/
+ $.quoteString = function(string)
+ {
+ if (string.match(_escapeable))
+ {
+ return '"' + string.replace(_escapeable, function (a)
+ {
+ var c = _meta[a];
+ if (typeof c === 'string') return c;
+ c = a.charCodeAt();
+ return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
+ }) + '"';
+ }
+ return '"' + string + '"';
+ };
+
+ var _escapeable = /["\\\x00-\x1f\x7f-\x9f]/g;
+
+ var _meta = {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ };
+
+ return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dist/lang/vector.js b/dist/lang/vector.js
new file mode 100644
index 00000000..75787923
--- /dev/null
+++ b/dist/lang/vector.js
@@ -0,0 +1,216 @@
+(function($){
+ var getSetZero = function(v){ return v !== undefined ? (this.array[0] = v) : this.array[0] },
+ getSetOne = function(v){ return v !== undefined ? (this.array[1] = v) : this.array[1]};
+
+/**
+ * @class jQuery.Vector
+ * @parent jquerypp
+ *
+ * `jQuery.Vector` represents a multi dimensional vector with shorthand methods for
+ * working with two dimensions.
+ *
+ * It is mainly used in [jQuery.event.drag drag] & [jQuery.event.drop drop] events.
+ *
+ * @constructor creates a new vector instance from the arguments. Example:
+ *
+ * new jQuery.Vector(1,2)
+ */
+ $.Vector = function(arr) {
+ var array = $.isArray(arr) ? arr : $.makeArray(arguments);
+ this.update(array);
+ };
+ $.Vector.prototype =
+ /* @Prototype*/
+ {
+ /**
+ * Applys the function to every item in the vector and returns a new vector.
+ *
+ * @param {Function} f The function to apply
+ * @return {jQuery.Vector} A new $.Vector instance
+ */
+ app: function( f ) {
+ var i, newArr = [];
+
+ for ( i = 0; i < this.array.length; i++ ) {
+ newArr.push(f(this.array[i], i));
+ }
+ return new $.Vector(newArr);
+ },
+ /**
+ * Adds two vectors together and returns a new instance. Example:
+ *
+ * new $.Vector(1,2).plus(2,3) //-> (3, 5)
+ * new $.Vector(3,5).plus(new Vector(4,5)) //-> (7, 10)
+ *
+ * @return {$.Vector}
+ */
+ plus: function() {
+ var i, args = arguments[0] instanceof $.Vector ? arguments[0].array : $.makeArray(arguments),
+ arr = this.array.slice(0),
+ vec = new $.Vector();
+ for ( i = 0; i < args.length; i++ ) {
+ arr[i] = (arr[i] ? arr[i] : 0) + args[i];
+ }
+ return vec.update(arr);
+ },
+ /**
+ * Subtract one vector from another and returns a new instance. Example:
+ *
+ * new $.Vector(4, 5).minus(2, 1) //-> (2, 4)
+ *
+ * @return {jQuery.Vector}
+ */
+ minus: function() {
+ var i, args = arguments[0] instanceof $.Vector ? arguments[0].array : $.makeArray(arguments),
+ arr = this.array.slice(0),
+ vec = new $.Vector();
+ for ( i = 0; i < args.length; i++ ) {
+ arr[i] = (arr[i] ? arr[i] : 0) - args[i];
+ }
+ return vec.update(arr);
+ },
+ /**
+ * Returns the current vector if it is equal to the vector passed in.
+ *
+ * `null` if otherwise.
+ *
+ * @return {jQuery.Vector}
+ */
+ equals: function() {
+ var i, args = arguments[0] instanceof $.Vector ? arguments[0].array : $.makeArray(arguments),
+ arr = this.array.slice(0),
+ vec = new $.Vector();
+ for ( i = 0; i < args.length; i++ ) {
+ if ( arr[i] != args[i] ) {
+ return null;
+ }
+ }
+ return vec.update(arr);
+ },
+ /**
+ * Returns the first value of the vector.
+ * You can also access the same value through the following aliases the
+ * [jQuery.Vector.prototype.left vector.left()] and [jQuery.Vector.prototype.left vector.width()]
+ * aliases.
+ *
+ * For example:
+ *
+ * var v = new $.Vector(2, 5);
+ * v.x() //-> 2
+ * v.left() //-> 2
+ * v.width() //-> 2
+ *
+ * @return {Number} The first value of the vector
+ */
+ x: getSetZero,
+ /**
+ * @hide
+ * Alias for [jQuery.Vector.prototype.x].
+ *
+ * @return {Number}
+ */
+ left: getSetZero,
+ /**
+ * @hide
+ * Alias for [jQuery.Vector.prototype.x].
+ *
+ * @return {Number}
+ */
+ width: getSetZero,
+ /**
+ * Returns the second value of the vector.
+ * You can also access the same value through the [jQuery.Vector.prototype.top vector.top()]
+ * and [jQuery.Vector.prototype.height vector.height()] aliases.
+ *
+ * For example:
+ *
+ * var v = new $.Vector(2, 5);
+ * v.y() //-> 5
+ * v.top() //-> 5
+ * v.height() //-> 5
+ *
+ * @return {Number} The first value of the vector
+ */
+ y: getSetOne,
+ /**
+ * @hide
+ * Alias for [jQuery.Vector.prototype.y].
+ *
+ * @return {Number}
+ */
+ top: getSetOne,
+ /**
+ * @hide
+ * Alias for [jQuery.Vector.prototype.y].
+ *
+ * @return {Number}
+ */
+ height: getSetOne,
+ /**
+ * Returns a string representation of the vector in the form of (x,y,...)
+ *
+ * var v = new $.Vector(4, 6, 1, 3);
+ * v.toString() //-> (4, 6, 1, 3)
+ *
+ * @return {String}
+ */
+ toString: function() {
+ return "(" + this.array.join(', ') + ")";
+ },
+ /**
+ * Replaces the vectors contents
+ *
+ * var v = new $.Vector(2, 3);
+ *
+ * @param {Object} array
+ */
+ update: function( array ) {
+ var i;
+ if ( this.array ) {
+ for ( i = 0; i < this.array.length; i++ ) {
+ delete this.array[i];
+ }
+ }
+ this.array = array;
+ for ( i = 0; i < array.length; i++ ) {
+ this[i] = this.array[i];
+ }
+ return this;
+ }
+ };
+
+ $.Event.prototype.vector = function() {
+ // Get the first touch element for touch events
+ var touches = "ontouchend" in document && this.originalEvent.touches && this.originalEvent.touches.length
+ ? this.originalEvent.changedTouches[0] : this;
+ if ( this.originalEvent.synthetic ) {
+ var doc = document.documentElement,
+ body = document.body;
+ return new $.Vector(touches.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0),
+ touches.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0));
+ } else {
+ return new $.Vector(touches.pageX, touches.pageY);
+ }
+ };
+
+ $.fn.offsetv = function() {
+ if ( this[0] == window ) {
+ return new $.Vector(window.pageXOffset ? window.pageXOffset : document.documentElement.scrollLeft, window.pageYOffset ? window.pageYOffset : document.documentElement.scrollTop);
+ } else {
+ var offset = this.offset() || {};
+ return new $.Vector(offset.left, offset.top);
+ }
+ };
+
+ $.fn.dimensionsv = function( which ) {
+ if ( this[0] == window || !which ) {
+ return new $.Vector(this.width(), this.height());
+ }
+ else {
+ return new $.Vector(this[which + "Width"](), this[which + "Height"]());
+ }
+ };
+
+ return $;
+}
+)(jQuery);
\ No newline at end of file
diff --git a/dom/form_params/form_params.html b/dom/form_params/form_params.html
index dd64859f..58f2e77d 100644
--- a/dom/form_params/form_params.html
+++ b/dom/form_params/form_params.html
@@ -13,7 +13,7 @@