michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0:
michael@0: // **********
michael@0: // Title: iq.js
michael@0: // Various helper functions, in the vein of jQuery.
michael@0:
michael@0: // ----------
michael@0: // Function: iQ
michael@0: // Returns an iQClass object which represents an individual element or a group
michael@0: // of elements. It works pretty much like jQuery(), with a few exceptions,
michael@0: // most notably that you can't use strings with complex html,
michael@0: // just simple tags like '
'.
michael@0: function iQ(selector, context) {
michael@0: // The iQ object is actually just the init constructor 'enhanced'
michael@0: return new iQClass(selector, context);
michael@0: };
michael@0:
michael@0: // A simple way to check for HTML strings or ID strings
michael@0: // (both of which we optimize for)
michael@0: let quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/;
michael@0:
michael@0: // Match a standalone tag
michael@0: let rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/;
michael@0:
michael@0: // ##########
michael@0: // Class: iQClass
michael@0: // The actual class of iQ result objects, representing an individual element
michael@0: // or a group of elements.
michael@0: //
michael@0: // ----------
michael@0: // Function: iQClass
michael@0: // You don't call this directly; this is what's called by iQ().
michael@0: function iQClass(selector, context) {
michael@0:
michael@0: // Handle $(""), $(null), or $(undefined)
michael@0: if (!selector) {
michael@0: return this;
michael@0: }
michael@0:
michael@0: // Handle $(DOMElement)
michael@0: if (selector.nodeType) {
michael@0: this.context = selector;
michael@0: this[0] = selector;
michael@0: this.length = 1;
michael@0: return this;
michael@0: }
michael@0:
michael@0: // The body element only exists once, optimize finding it
michael@0: if (selector === "body" && !context) {
michael@0: this.context = document;
michael@0: this[0] = document.body;
michael@0: this.selector = "body";
michael@0: this.length = 1;
michael@0: return this;
michael@0: }
michael@0:
michael@0: // Handle HTML strings
michael@0: if (typeof selector === "string") {
michael@0: // Are we dealing with HTML string or an ID?
michael@0:
michael@0: let match = quickExpr.exec(selector);
michael@0:
michael@0: // Verify a match, and that no context was specified for #id
michael@0: if (match && (match[1] || !context)) {
michael@0:
michael@0: // HANDLE $(html) -> $(array)
michael@0: if (match[1]) {
michael@0: let doc = (context ? context.ownerDocument || context : document);
michael@0:
michael@0: // If a single string is passed in and it's a single tag
michael@0: // just do a createElement and skip the rest
michael@0: let ret = rsingleTag.exec(selector);
michael@0:
michael@0: if (ret) {
michael@0: if (Utils.isPlainObject(context)) {
michael@0: Utils.assert(false, 'does not support HTML creation with context');
michael@0: } else {
michael@0: selector = [doc.createElement(ret[1])];
michael@0: }
michael@0:
michael@0: } else {
michael@0: Utils.assert(false, 'does not support complex HTML creation');
michael@0: }
michael@0:
michael@0: return Utils.merge(this, selector);
michael@0:
michael@0: // HANDLE $("#id")
michael@0: } else {
michael@0: let elem = document.getElementById(match[2]);
michael@0:
michael@0: if (elem) {
michael@0: this.length = 1;
michael@0: this[0] = elem;
michael@0: }
michael@0:
michael@0: this.context = document;
michael@0: this.selector = selector;
michael@0: return this;
michael@0: }
michael@0:
michael@0: // HANDLE $("TAG")
michael@0: } else if (!context && /^\w+$/.test(selector)) {
michael@0: this.selector = selector;
michael@0: this.context = document;
michael@0: selector = document.getElementsByTagName(selector);
michael@0: return Utils.merge(this, selector);
michael@0:
michael@0: // HANDLE $(expr, $(...))
michael@0: } else if (!context || context.iq) {
michael@0: return (context || iQ(document)).find(selector);
michael@0:
michael@0: // HANDLE $(expr, context)
michael@0: // (which is just equivalent to: $(context).find(expr)
michael@0: } else {
michael@0: return iQ(context).find(selector);
michael@0: }
michael@0:
michael@0: // HANDLE $(function)
michael@0: // Shortcut for document ready
michael@0: } else if (typeof selector == "function") {
michael@0: Utils.log('iQ does not support ready functions');
michael@0: return null;
michael@0: }
michael@0:
michael@0: if ("selector" in selector) {
michael@0: this.selector = selector.selector;
michael@0: this.context = selector.context;
michael@0: }
michael@0:
michael@0: let ret = this || [];
michael@0: if (selector != null) {
michael@0: // The window, strings (and functions) also have 'length'
michael@0: if (selector.length == null || typeof selector == "string" || selector.setInterval) {
michael@0: Array.push(ret, selector);
michael@0: } else {
michael@0: Utils.merge(ret, selector);
michael@0: }
michael@0: }
michael@0: return ret;
michael@0: };
michael@0:
michael@0: iQClass.prototype = {
michael@0:
michael@0: // ----------
michael@0: // Function: toString
michael@0: // Prints [iQ...] for debug use
michael@0: toString: function iQClass_toString() {
michael@0: if (this.length > 1) {
michael@0: if (this.selector)
michael@0: return "[iQ (" + this.selector + ")]";
michael@0: else
michael@0: return "[iQ multi-object]";
michael@0: }
michael@0:
michael@0: if (this.length == 1)
michael@0: return "[iQ (" + this[0].toString() + ")]";
michael@0:
michael@0: return "[iQ non-object]";
michael@0: },
michael@0:
michael@0: // Start with an empty selector
michael@0: selector: "",
michael@0:
michael@0: // The default length of a iQ object is 0
michael@0: length: 0,
michael@0:
michael@0: // ----------
michael@0: // Function: each
michael@0: // Execute a callback for every element in the matched set.
michael@0: each: function iQClass_each(callback) {
michael@0: if (typeof callback != "function") {
michael@0: Utils.assert(false, "each's argument must be a function");
michael@0: return null;
michael@0: }
michael@0: for (let i = 0; this[i] != null && callback(this[i]) !== false; i++) {}
michael@0: return this;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: addClass
michael@0: // Adds the given class(es) to the receiver.
michael@0: addClass: function iQClass_addClass(value) {
michael@0: Utils.assertThrow(typeof value == "string" && value,
michael@0: 'requires a valid string argument');
michael@0:
michael@0: let length = this.length;
michael@0: for (let i = 0; i < length; i++) {
michael@0: let elem = this[i];
michael@0: if (elem.nodeType === 1) {
michael@0: value.split(/\s+/).forEach(function(className) {
michael@0: elem.classList.add(className);
michael@0: });
michael@0: }
michael@0: }
michael@0:
michael@0: return this;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: removeClass
michael@0: // Removes the given class(es) from the receiver.
michael@0: removeClass: function iQClass_removeClass(value) {
michael@0: if (typeof value != "string" || !value) {
michael@0: Utils.assert(false, 'does not support function argument');
michael@0: return null;
michael@0: }
michael@0:
michael@0: let length = this.length;
michael@0: for (let i = 0; i < length; i++) {
michael@0: let elem = this[i];
michael@0: if (elem.nodeType === 1 && elem.className) {
michael@0: value.split(/\s+/).forEach(function(className) {
michael@0: elem.classList.remove(className);
michael@0: });
michael@0: }
michael@0: }
michael@0:
michael@0: return this;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: hasClass
michael@0: // Returns true is the receiver has the given css class.
michael@0: hasClass: function iQClass_hasClass(singleClassName) {
michael@0: let length = this.length;
michael@0: for (let i = 0; i < length; i++) {
michael@0: if (this[i].classList.contains(singleClassName)) {
michael@0: return true;
michael@0: }
michael@0: }
michael@0: return false;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: find
michael@0: // Searches the receiver and its children, returning a new iQ object with
michael@0: // elements that match the given selector.
michael@0: find: function iQClass_find(selector) {
michael@0: let ret = [];
michael@0: let length = 0;
michael@0:
michael@0: let l = this.length;
michael@0: for (let i = 0; i < l; i++) {
michael@0: length = ret.length;
michael@0: try {
michael@0: Utils.merge(ret, this[i].querySelectorAll(selector));
michael@0: } catch(e) {
michael@0: Utils.log('iQ.find error (bad selector)', e);
michael@0: }
michael@0:
michael@0: if (i > 0) {
michael@0: // Make sure that the results are unique
michael@0: for (let n = length; n < ret.length; n++) {
michael@0: for (let r = 0; r < length; r++) {
michael@0: if (ret[r] === ret[n]) {
michael@0: ret.splice(n--, 1);
michael@0: break;
michael@0: }
michael@0: }
michael@0: }
michael@0: }
michael@0: }
michael@0:
michael@0: return iQ(ret);
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: contains
michael@0: // Check to see if a given DOM node descends from the receiver.
michael@0: contains: function iQClass_contains(selector) {
michael@0: Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
michael@0:
michael@0: // fast path when querySelector() can be used
michael@0: if ('string' == typeof selector)
michael@0: return null != this[0].querySelector(selector);
michael@0:
michael@0: let object = iQ(selector);
michael@0: Utils.assert(object.length <= 1, 'does not yet support multi-objects');
michael@0:
michael@0: let elem = object[0];
michael@0: if (!elem || !elem.parentNode)
michael@0: return false;
michael@0:
michael@0: do {
michael@0: elem = elem.parentNode;
michael@0: } while (elem && this[0] != elem);
michael@0:
michael@0: return this[0] == elem;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: remove
michael@0: // Removes the receiver from the DOM.
michael@0: remove: function iQClass_remove(options) {
michael@0: if (!options || !options.preserveEventHandlers)
michael@0: this.unbindAll();
michael@0: for (let i = 0; this[i] != null; i++) {
michael@0: let elem = this[i];
michael@0: if (elem.parentNode) {
michael@0: elem.parentNode.removeChild(elem);
michael@0: }
michael@0: }
michael@0: return this;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: empty
michael@0: // Removes all of the reciever's children and HTML content from the DOM.
michael@0: empty: function iQClass_empty() {
michael@0: for (let i = 0; this[i] != null; i++) {
michael@0: let elem = this[i];
michael@0: while (elem.firstChild) {
michael@0: iQ(elem.firstChild).unbindAll();
michael@0: elem.removeChild(elem.firstChild);
michael@0: }
michael@0: }
michael@0: return this;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: width
michael@0: // Returns the width of the receiver, including padding and border.
michael@0: width: function iQClass_width() {
michael@0: return Math.floor(this[0].offsetWidth);
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: height
michael@0: // Returns the height of the receiver, including padding and border.
michael@0: height: function iQClass_height() {
michael@0: return Math.floor(this[0].offsetHeight);
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: position
michael@0: // Returns an object with the receiver's position in left and top
michael@0: // properties.
michael@0: position: function iQClass_position() {
michael@0: let bounds = this.bounds();
michael@0: return new Point(bounds.left, bounds.top);
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: bounds
michael@0: // Returns a with the receiver's bounds.
michael@0: bounds: function iQClass_bounds() {
michael@0: Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
michael@0: let rect = this[0].getBoundingClientRect();
michael@0: return new Rect(Math.floor(rect.left), Math.floor(rect.top),
michael@0: Math.floor(rect.width), Math.floor(rect.height));
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: data
michael@0: // Pass in both key and value to attach some data to the receiver;
michael@0: // pass in just key to retrieve it.
michael@0: data: function iQClass_data(key, value) {
michael@0: let data = null;
michael@0: if (value === undefined) {
michael@0: Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
michael@0: data = this[0].iQData;
michael@0: if (data)
michael@0: return data[key];
michael@0: else
michael@0: return null;
michael@0: }
michael@0:
michael@0: for (let i = 0; this[i] != null; i++) {
michael@0: let elem = this[i];
michael@0: data = elem.iQData;
michael@0:
michael@0: if (!data)
michael@0: data = elem.iQData = {};
michael@0:
michael@0: data[key] = value;
michael@0: }
michael@0:
michael@0: return this;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: html
michael@0: // Given a value, sets the receiver's innerHTML to it; otherwise returns
michael@0: // what's already there.
michael@0: html: function iQClass_html(value) {
michael@0: Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
michael@0: if (value === undefined)
michael@0: return this[0].innerHTML;
michael@0:
michael@0: this[0].innerHTML = value;
michael@0: return this;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: text
michael@0: // Given a value, sets the receiver's textContent to it; otherwise returns
michael@0: // what's already there.
michael@0: text: function iQClass_text(value) {
michael@0: Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
michael@0: if (value === undefined) {
michael@0: return this[0].textContent;
michael@0: }
michael@0:
michael@0: return this.empty().append((this[0] && this[0].ownerDocument || document).createTextNode(value));
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: val
michael@0: // Given a value, sets the receiver's value to it; otherwise returns what's already there.
michael@0: val: function iQClass_val(value) {
michael@0: Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
michael@0: if (value === undefined) {
michael@0: return this[0].value;
michael@0: }
michael@0:
michael@0: this[0].value = value;
michael@0: return this;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: appendTo
michael@0: // Appends the receiver to the result of iQ(selector).
michael@0: appendTo: function iQClass_appendTo(selector) {
michael@0: Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
michael@0: iQ(selector).append(this);
michael@0: return this;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: append
michael@0: // Appends the result of iQ(selector) to the receiver.
michael@0: append: function iQClass_append(selector) {
michael@0: let object = iQ(selector);
michael@0: Utils.assert(object.length == 1 && this.length == 1,
michael@0: 'does not yet support multi-objects (or null objects)');
michael@0: this[0].appendChild(object[0]);
michael@0: return this;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: attr
michael@0: // Sets or gets an attribute on the element(s).
michael@0: attr: function iQClass_attr(key, value) {
michael@0: Utils.assert(typeof key === 'string', 'string key');
michael@0: if (value === undefined) {
michael@0: Utils.assert(this.length == 1, 'retrieval does not support multi-objects (or null objects)');
michael@0: return this[0].getAttribute(key);
michael@0: }
michael@0:
michael@0: for (let i = 0; this[i] != null; i++)
michael@0: this[i].setAttribute(key, value);
michael@0:
michael@0: return this;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: css
michael@0: // Sets or gets CSS properties on the receiver. When setting certain numerical properties,
michael@0: // will automatically add "px". A property can be removed by setting it to null.
michael@0: //
michael@0: // Possible call patterns:
michael@0: // a: object, b: undefined - sets with properties from a
michael@0: // a: string, b: undefined - gets property specified by a
michael@0: // a: string, b: string/number - sets property specified by a to b
michael@0: css: function iQClass_css(a, b) {
michael@0: let properties = null;
michael@0:
michael@0: if (typeof a === 'string') {
michael@0: let key = a;
michael@0: if (b === undefined) {
michael@0: Utils.assert(this.length == 1, 'retrieval does not support multi-objects (or null objects)');
michael@0:
michael@0: return window.getComputedStyle(this[0], null).getPropertyValue(key);
michael@0: }
michael@0: properties = {};
michael@0: properties[key] = b;
michael@0: } else if (a instanceof Rect) {
michael@0: properties = {
michael@0: left: a.left,
michael@0: top: a.top,
michael@0: width: a.width,
michael@0: height: a.height
michael@0: };
michael@0: } else {
michael@0: properties = a;
michael@0: }
michael@0:
michael@0: let pixels = {
michael@0: 'left': true,
michael@0: 'top': true,
michael@0: 'right': true,
michael@0: 'bottom': true,
michael@0: 'width': true,
michael@0: 'height': true
michael@0: };
michael@0:
michael@0: for (let i = 0; this[i] != null; i++) {
michael@0: let elem = this[i];
michael@0: for (let key in properties) {
michael@0: let value = properties[key];
michael@0:
michael@0: if (pixels[key] && typeof value != 'string')
michael@0: value += 'px';
michael@0:
michael@0: if (value == null) {
michael@0: elem.style.removeProperty(key);
michael@0: } else if (key.indexOf('-') != -1)
michael@0: elem.style.setProperty(key, value, '');
michael@0: else
michael@0: elem.style[key] = value;
michael@0: }
michael@0: }
michael@0:
michael@0: return this;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: animate
michael@0: // Uses CSS transitions to animate the element.
michael@0: //
michael@0: // Parameters:
michael@0: // css - an object map of the CSS properties to change
michael@0: // options - an object with various properites (see below)
michael@0: //
michael@0: // Possible "options" properties:
michael@0: // duration - how long to animate, in milliseconds
michael@0: // easing - easing function to use. Possibilities include
michael@0: // "tabviewBounce", "easeInQuad". Default is "ease".
michael@0: // complete - function to call once the animation is done, takes nothing
michael@0: // in, but "this" is set to the element that was animated.
michael@0: animate: function iQClass_animate(css, options) {
michael@0: Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
michael@0:
michael@0: if (!options)
michael@0: options = {};
michael@0:
michael@0: let easings = {
michael@0: tabviewBounce: "cubic-bezier(0.0, 0.63, .6, 1.29)",
michael@0: easeInQuad: 'ease-in', // TODO: make it a real easeInQuad, or decide we don't care
michael@0: fast: 'cubic-bezier(0.7,0,1,1)'
michael@0: };
michael@0:
michael@0: let duration = (options.duration || 400);
michael@0: let easing = (easings[options.easing] || 'ease');
michael@0:
michael@0: if (css instanceof Rect) {
michael@0: css = {
michael@0: left: css.left,
michael@0: top: css.top,
michael@0: width: css.width,
michael@0: height: css.height
michael@0: };
michael@0: }
michael@0:
michael@0:
michael@0: // The latest versions of Firefox do not animate from a non-explicitly
michael@0: // set css properties. So for each element to be animated, go through
michael@0: // and explicitly define 'em.
michael@0: let rupper = /([A-Z])/g;
michael@0: this.each(function(elem) {
michael@0: let cStyle = window.getComputedStyle(elem, null);
michael@0: for (let prop in css) {
michael@0: prop = prop.replace(rupper, "-$1").toLowerCase();
michael@0: iQ(elem).css(prop, cStyle.getPropertyValue(prop));
michael@0: }
michael@0: });
michael@0:
michael@0: this.css({
michael@0: 'transition-property': Object.keys(css).join(", "),
michael@0: 'transition-duration': (duration / 1000) + 's',
michael@0: 'transition-timing-function': easing
michael@0: });
michael@0:
michael@0: this.css(css);
michael@0:
michael@0: let self = this;
michael@0: setTimeout(function() {
michael@0: self.css({
michael@0: 'transition-property': 'none',
michael@0: 'transition-duration': '',
michael@0: 'transition-timing-function': ''
michael@0: });
michael@0:
michael@0: if (typeof options.complete == "function")
michael@0: options.complete.apply(self);
michael@0: }, duration);
michael@0:
michael@0: return this;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: fadeOut
michael@0: // Animates the receiver to full transparency. Calls callback on completion.
michael@0: fadeOut: function iQClass_fadeOut(callback) {
michael@0: Utils.assert(typeof callback == "function" || callback === undefined,
michael@0: 'does not yet support duration');
michael@0:
michael@0: this.animate({
michael@0: opacity: 0
michael@0: }, {
michael@0: duration: 400,
michael@0: complete: function() {
michael@0: iQ(this).css({display: 'none'});
michael@0: if (typeof callback == "function")
michael@0: callback.apply(this);
michael@0: }
michael@0: });
michael@0:
michael@0: return this;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: fadeIn
michael@0: // Animates the receiver to full opacity.
michael@0: fadeIn: function iQClass_fadeIn() {
michael@0: this.css({display: ''});
michael@0: this.animate({
michael@0: opacity: 1
michael@0: }, {
michael@0: duration: 400
michael@0: });
michael@0:
michael@0: return this;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: hide
michael@0: // Hides the receiver.
michael@0: hide: function iQClass_hide() {
michael@0: this.css({display: 'none', opacity: 0});
michael@0: return this;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: show
michael@0: // Shows the receiver.
michael@0: show: function iQClass_show() {
michael@0: this.css({display: '', opacity: 1});
michael@0: return this;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: bind
michael@0: // Binds the given function to the given event type. Also wraps the function
michael@0: // in a try/catch block that does a Utils.log on any errors.
michael@0: bind: function iQClass_bind(type, func) {
michael@0: let handler = function(event) func.apply(this, [event]);
michael@0:
michael@0: for (let i = 0; this[i] != null; i++) {
michael@0: let elem = this[i];
michael@0: if (!elem.iQEventData)
michael@0: elem.iQEventData = {};
michael@0:
michael@0: if (!elem.iQEventData[type])
michael@0: elem.iQEventData[type] = [];
michael@0:
michael@0: elem.iQEventData[type].push({
michael@0: original: func,
michael@0: modified: handler
michael@0: });
michael@0:
michael@0: elem.addEventListener(type, handler, false);
michael@0: }
michael@0:
michael@0: return this;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: one
michael@0: // Binds the given function to the given event type, but only for one call;
michael@0: // automatically unbinds after the event fires once.
michael@0: one: function iQClass_one(type, func) {
michael@0: Utils.assert(typeof func == "function", 'does not support eventData argument');
michael@0:
michael@0: let handler = function(e) {
michael@0: iQ(this).unbind(type, handler);
michael@0: return func.apply(this, [e]);
michael@0: };
michael@0:
michael@0: return this.bind(type, handler);
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: unbind
michael@0: // Unbinds the given function from the given event type.
michael@0: unbind: function iQClass_unbind(type, func) {
michael@0: Utils.assert(typeof func == "function", 'Must provide a function');
michael@0:
michael@0: for (let i = 0; this[i] != null; i++) {
michael@0: let elem = this[i];
michael@0: let handler = func;
michael@0: if (elem.iQEventData && elem.iQEventData[type]) {
michael@0: let count = elem.iQEventData[type].length;
michael@0: for (let a = 0; a < count; a++) {
michael@0: let pair = elem.iQEventData[type][a];
michael@0: if (pair.original == func) {
michael@0: handler = pair.modified;
michael@0: elem.iQEventData[type].splice(a, 1);
michael@0: if (!elem.iQEventData[type].length) {
michael@0: delete elem.iQEventData[type];
michael@0: if (!Object.keys(elem.iQEventData).length)
michael@0: delete elem.iQEventData;
michael@0: }
michael@0: break;
michael@0: }
michael@0: }
michael@0: }
michael@0:
michael@0: elem.removeEventListener(type, handler, false);
michael@0: }
michael@0:
michael@0: return this;
michael@0: },
michael@0:
michael@0: // ----------
michael@0: // Function: unbindAll
michael@0: // Unbinds all event handlers.
michael@0: unbindAll: function iQClass_unbindAll() {
michael@0: for (let i = 0; this[i] != null; i++) {
michael@0: let elem = this[i];
michael@0:
michael@0: for (let j = 0; j < elem.childElementCount; j++)
michael@0: iQ(elem.children[j]).unbindAll();
michael@0:
michael@0: if (!elem.iQEventData)
michael@0: continue;
michael@0:
michael@0: Object.keys(elem.iQEventData).forEach(function (type) {
michael@0: while (elem.iQEventData && elem.iQEventData[type])
michael@0: this.unbind(type, elem.iQEventData[type][0].original);
michael@0: }, this);
michael@0: }
michael@0:
michael@0: return this;
michael@0: }
michael@0: };
michael@0:
michael@0: // ----------
michael@0: // Create various event aliases
michael@0: let events = [
michael@0: 'keyup',
michael@0: 'keydown',
michael@0: 'keypress',
michael@0: 'mouseup',
michael@0: 'mousedown',
michael@0: 'mouseover',
michael@0: 'mouseout',
michael@0: 'mousemove',
michael@0: 'click',
michael@0: 'dblclick',
michael@0: 'resize',
michael@0: 'change',
michael@0: 'blur',
michael@0: 'focus'
michael@0: ];
michael@0:
michael@0: events.forEach(function(event) {
michael@0: iQClass.prototype[event] = function(func) {
michael@0: return this.bind(event, func);
michael@0: };
michael@0: });