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: });