Bug 582023: iQ changes based on comments from gavin
authorMichael Yoshitaka Erlewine <mitcho@mitcho.com>
Wed, 28 Jul 2010 21:33:43 -0600
changeset 50224 15a0872846d449763607e8c161a23f6cf3e8a58c
parent 50223 5bcd57aa7869757995106cc1f5969ee4d3c97eda
child 50225 d1bf7067c78d5c9e4c343262a111f986035d2623
push id15039
push useredward.lee@engineering.uiuc.edu
push dateThu, 12 Aug 2010 19:47:36 +0000
treeherdermozilla-central@5da28c582cc7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs582023
milestone2.0b3pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 582023: iQ changes based on comments from gavin - let is the new var, no multiple declarations on the same line. - renamed iQ.fn.init to be iQClass, iQ.fn to be iQClass.prototype, to look much more normal. iQ now just creates a new iQClass. No more brain hurt. - no more (function(){...}) closure - Array.prototype.xxx.call replaced by Array.xxx - a couple other changes recommended by gavin. - Utils: rm some comments, but haven't touched JSM, isDOMElement, or merge.
browser/base/content/tabcandy/app/groups.js
browser/base/content/tabcandy/core/iq.js
browser/base/content/tabcandy/core/utils.js
--- a/browser/base/content/tabcandy/app/groups.js
+++ b/browser/base/content/tabcandy/app/groups.js
@@ -569,17 +569,17 @@ window.Group.prototype = Utils.extend(ne
   },
 
   // ----------
   // Function: closeAll
   // Closes the group and all of its children.
   closeAll: function() {
     var self = this;
     if (this._children.length) {
-      var toClose = Utils.merge([], this._children);
+      var toClose = this._children.concat();
       toClose.forEach(function(child) {
         child.removeSubscriber(self, "close");
         child.close();
       });
     }
 
     if (!this.locked.close)
       this.close();
@@ -754,17 +754,17 @@ window.Group.prototype = Utils.extend(ne
     }
   },
 
   // ----------
   // Function: removeAll
   // Removes all of the group's children.
   removeAll: function() {
     var self = this;
-    var toRemove = Utils.merge([], this._children);
+    var toRemove = this._children.concat();
     toRemove.forEach(function(child) {
       self.remove(child, {dontArrange: true});
     });
   },
 
   // ----------
   // Function: setNewTabButtonBounds
   // Used for positioning the "new tab" button in the "new tabs" group.
@@ -1666,17 +1666,17 @@ window.Groups = {
       }
     });
   },
 
   // ----------
   // Function: removeAll
   // Removes all tabs from all groups (which automatically closes all unnamed groups).
   removeAll: function() {
-    var toRemove = Utils.merge([], this.groups);
+    var toRemove = this.groups.concat();
     toRemove.forEach(function(group) {
       group.removeAll();
     });
   },
 
   // ----------
   // Function: newTab
   // Given a <TabItem>, files it in the appropriate group.
--- a/browser/base/content/tabcandy/core/iq.js
+++ b/browser/base/content/tabcandy/core/iq.js
@@ -42,281 +42,266 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 // **********
 // Title: iq.js
 // Various helper functions, in the vein of jQuery.
 
-(function( window, undefined ) {
-
-var iQ = function(selector, context) {
-    // The iQ object is actually just the init constructor 'enhanced'
-    return new iQ.fn.init( selector, context );
-  },
-
-  // Map over iQ in case of overwrite
-  _iQ = window.iQ,
-
-  // Use the correct document accordingly with window argument (sandbox)
-  document = window.document,
+// ----------
+// Function: iQ
+// Returns an iQClass object which represents an individual element or a group
+// of elements. It works pretty much like jQuery(), with a few exceptions,
+// most notably that you can't use strings with complex html,
+// just simple tags like '<div>'.
+function iQ(selector, context) {
+  // The iQ object is actually just the init constructor 'enhanced'
+  return new iQClass( selector, context );
+};
 
-  // A central reference to the root iQ(document)
-  rootiQ,
+// A simple way to check for HTML strings or ID strings
+// (both of which we optimize for)
+let quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/;
 
-  // A simple way to check for HTML strings or ID strings
-  // (both of which we optimize for)
-  quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,
-
-  // Match a standalone tag
-  rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
-
-  rclass = /[\n\t]/g,
-  rspace = /\s+/;
+// Match a standalone tag
+let rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/;
 
 // ##########
-// Class: iQ.fn
-// An individual element or group of elements.
-iQ.fn = iQ.prototype = {
-  // ----------
-  // Function: init
-  // You don't call this directly; this is what's called by iQ().
-  // It works pretty much like jQuery(), with a few exceptions,
-  // most notably that you can't use strings with complex html,
-  // just simple tags like '<div>'.
-  init: function( selector, context ) {
-    var match, elem, ret, doc;
+// Class: iQClass
+// The actual class of iQ result objects, representing an individual element
+// or a group of elements.
+//
+// ----------
+// Function: iQClass
+// You don't call this directly; this is what's called by iQ().
+let iQClass = function(selector, context) {
+
+  // Handle $(""), $(null), or $(undefined)
+  if ( !selector ) {
+    return this;
+  }
+
+  // Handle $(DOMElement)
+  if ( selector.nodeType ) {
+    this.context = selector;
+    this[0] = selector;
+    this.length = 1;
+    return this;
+  }
+
+  // The body element only exists once, optimize finding it
+  if ( selector === "body" && !context ) {
+    this.context = document;
+    this[0] = document.body;
+    this.selector = "body";
+    this.length = 1;
+    return this;
+  }
+
+  // Handle HTML strings
+  if ( typeof selector === "string" ) {
+    // Are we dealing with HTML string or an ID?
+
+    let match = quickExpr.exec( selector );
+
+    // Verify a match, and that no context was specified for #id
+    if ( match && (match[1] || !context) ) {
+
+      // HANDLE $(html) -> $(array)
+      if ( match[1] ) {
+        let doc = (context ? context.ownerDocument || context : document);
 
-    // Handle $(""), $(null), or $(undefined)
-    if ( !selector ) {
-      return this;
-    }
+        // If a single string is passed in and it's a single tag
+        // just do a createElement and skip the rest
+        let ret = rsingleTag.exec( selector );
+
+        if ( ret ) {
+          if ( Utils.isPlainObject( context ) ) {
+            Utils.assert('does not support HTML creation with context', false);
+          } else {
+            selector = [ doc.createElement( ret[1] ) ];
+          }
+
+        } else {
+            Utils.assert('does not support complex HTML creation', false);
+        }
+
+        return Utils.merge( this, selector );
+
+      // HANDLE $("#id")
+      } else {
+        let elem = document.getElementById( match[2] );
 
-    // Handle $(DOMElement)
-    if ( selector.nodeType ) {
-      this.context = this[0] = selector;
-      this.length = 1;
-      return this;
-    }
+        if ( elem ) {
+          this.length = 1;
+          this[0] = elem;
+        }
+
+        this.context = document;
+        this.selector = selector;
+        return this;
+      }
 
-    // The body element only exists once, optimize finding it
-    if ( selector === "body" && !context ) {
+    // HANDLE $("TAG")
+    } else if ( !context && /^\w+$/.test( selector ) ) {
+      this.selector = selector;
       this.context = document;
-      this[0] = document.body;
-      this.selector = "body";
-      this.length = 1;
-      return this;
+      selector = document.getElementsByTagName( selector );
+      return Utils.merge( this, selector );
+
+    // HANDLE $(expr, $(...))
+    } else if ( !context || context.iq ) {
+      return (context || iQ(document)).find( selector );
+
+    // HANDLE $(expr, context)
+    // (which is just equivalent to: $(context).find(expr)
+    } else {
+      return iQ( context ).find( selector );
     }
 
-    // Handle HTML strings
-    if ( typeof selector === "string" ) {
-      // Are we dealing with HTML string or an ID?
-      match = quickExpr.exec( selector );
-
-      // Verify a match, and that no context was specified for #id
-      if ( match && (match[1] || !context) ) {
-
-        // HANDLE $(html) -> $(array)
-        if ( match[1] ) {
-          doc = (context ? context.ownerDocument || context : document);
-
-          // If a single string is passed in and it's a single tag
-          // just do a createElement and skip the rest
-          ret = rsingleTag.exec( selector );
+  // HANDLE $(function)
+  // Shortcut for document ready
+  } else if (typeof selector == "function") {
+    Utils.log('iQ does not support ready functions');
+    return null;
+  }
 
-          if ( ret ) {
-            if ( Utils.isPlainObject( context ) ) {
-              Utils.assert('does not support HTML creation with context', false);
-            } else {
-              selector = [ doc.createElement( ret[1] ) ];
-            }
-
-          } else {
-              Utils.assert('does not support complex HTML creation', false);
-          }
-
-          return Utils.merge( this, selector );
-
-        // HANDLE $("#id")
-        } else {
-          elem = document.getElementById( match[2] );
-
-          if ( elem ) {
-            this.length = 1;
-            this[0] = elem;
-          }
-
-          this.context = document;
-          this.selector = selector;
-          return this;
-        }
+  if (selector.selector !== undefined) {
+    this.selector = selector.selector;
+    this.context = selector.context;
+  }
 
-      // HANDLE $("TAG")
-      } else if ( !context && /^\w+$/.test( selector ) ) {
-        this.selector = selector;
-        this.context = document;
-        selector = document.getElementsByTagName( selector );
-        return Utils.merge( this, selector );
-
-      // HANDLE $(expr, $(...))
-      } else if ( !context || context.iq ) {
-        return (context || rootiQ).find( selector );
-
-      // HANDLE $(expr, context)
-      // (which is just equivalent to: $(context).find(expr)
-      } else {
-        return iQ( context ).find( selector );
-      }
-
-    // HANDLE $(function)
-    // Shortcut for document ready
-    } else if (typeof selector == "function") {
-      Utils.log('iQ does not support ready functions');
-      return null;
+  let ret = this || [];
+  if ( selector != null ) {
+    // The window, strings (and functions) also have 'length'
+    if (selector.length == null || typeof selector == "string" || selector.setInterval) {
+      Array.push( ret, selector );
+    } else {
+      Utils.merge( ret, selector );
     }
-
-    if (selector.selector !== undefined) {
-      this.selector = selector.selector;
-      this.context = selector.context;
-    }
-
-    // this used to be makeArray:
-    var ret = this || [];
-    if ( selector != null ) {
-      // The window, strings (and functions) also have 'length'
-      if (selector.length == null || typeof selector == "string" || typeof selector == "function" || selector.setInterval) {
-        Array.prototype.push.call( ret, selector );
-      } else {
-        Utils.merge( ret, selector );
-      }
-    }
-    return ret;
-
-  },
+  }
+  return ret;
+}
+iQClass.prototype = {
 
   // Start with an empty selector
   selector: "",
 
   // The current version of iQ being used
   iq: "1.4.2",
 
   // The default length of a iQ object is 0
   length: 0,
 
   // ----------
   // Function: get
   // Get the Nth element in the matched element set OR
   // Get the whole matched element set as a clean array
   get: function( num ) {
-    return num == null ?
+    if (num == null) // Return a 'clean' array
+      return Array.slice( this, 0 );
 
-      // Return a 'clean' array
-      // was toArray
-      Array.prototype.slice.call( this, 0 ) :
-
-      // Return just the object
-      ( num < 0 ? this[ num + this.length ] : this[ num ] );
+    // Return just the Nth object
+    let index = num < 0 ? num + this.length : num;
+    return this[index];
   },
 
   // ----------
   // Function: each
   // Execute a callback for every element in the matched set.
   each: function( callback ) {
     if (typeof callback != "function") {
       Utils.assert("each's argument must be a function", false);
       return null;
     }
-    for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
-      callback(elem);
+    for ( let i = 0; this[i] != null; i++ ) {
+      callback(this[i]);
     }
     return this;
   },
 
   // ----------
   // Function: addClass
   // Adds the given class(es) to the receiver.
   addClass: function( value ) {
-    if (typeof value == "function") {
-      Utils.assert('does not support function argument', false);
+    if ( typeof value != "string" || !value ) {
+      Utils.assert('requires a valid string argument', false);
       return null;
     }
 
-    if ( value && typeof value === "string" ) {
-      for ( var i = 0, l = this.length; i < l; i++ ) {
-        var elem = this[i];
-        if ( elem.nodeType === 1 ) {
-          (value || "").split( rspace ).forEach(function(className) {
-            elem.classList.add(className);
-          });
-        }
+    let length = this.length;
+    for ( let i = 0; i < length; i++ ) {
+      let elem = this[i];
+      if ( elem.nodeType === 1 ) {
+        value.split( /\s+/ ).forEach(function(className) {
+          elem.classList.add(className);
+        });
       }
     }
 
     return this;
   },
 
   // ----------
   // Function: removeClass
   // Removes the given class(es) from the receiver.
   removeClass: function( value ) {
-    if (typeof value == "function") {
+    if ( typeof value != "string" || !value ) {
       Utils.assert('does not support function argument', false);
       return null;
     }
 
-    if ( (value && typeof value === "string") || value === undefined ) {
-      for ( var i = 0, l = this.length; i < l; i++ ) {
-        var elem = this[i];
-        if ( elem.nodeType === 1 && elem.className ) {
-          if ( value ) {
-            (value || "").split(rspace).forEach(function(className) {
-              elem.classList.remove(className);
-            });
-          } else {
-            elem.className = "";
-          }
-        }
+    let length = this.length;
+    for ( let i = 0; i < length; i++ ) {
+      let elem = this[i];
+      if ( elem.nodeType === 1 && elem.className ) {
+        value.split( /\s+/ ).forEach(function(className) {
+          elem.classList.remove(className);
+        });
       }
     }
 
     return this;
   },
 
   // ----------
   // Function: hasClass
   // Returns true is the receiver has the given css class.
-  hasClass: function( selector ) {
-    for ( var i = 0, l = this.length; i < l; i++ ) {
-      if ( this[i].classList.contains( selector ) ) {
+  hasClass: function( singleClassName ) {
+    let length = this.length;
+    for ( let i = 0; i < length; i++ ) {
+      if ( this[i].classList.contains( singleClassName ) ) {
         return true;
       }
     }
     return false;
   },
 
   // ----------
   // Function: find
   // Searches the receiver and its children, returning a new iQ object with
   // elements that match the given selector.
   find: function( selector ) {
-    var ret = [], length = 0;
+    let ret = [];
+    let length = 0;
 
-    for ( var i = 0, l = this.length; i < l; i++ ) {
+    let l = this.length;
+    for ( let i = 0; i < l; i++ ) {
       length = ret.length;
       try {
         Utils.merge(ret, this[i].querySelectorAll( selector ) );
       } catch(e) {
         Utils.log('iQ.find error (bad selector)', e);
       }
 
       if ( i > 0 ) {
         // Make sure that the results are unique
-        for ( var n = length; n < ret.length; n++ ) {
-          for ( var r = 0; r < length; r++ ) {
+        for ( let n = length; n < ret.length; n++ ) {
+          for ( let r = 0; r < length; r++ ) {
             if ( ret[r] === ret[n] ) {
               ret.splice(n--, 1);
               break;
             }
           }
         }
       }
     }
@@ -324,35 +309,35 @@ iQ.fn = iQ.prototype = {
     return iQ(ret);
   },
 
   // ----------
   // Function: remove
   // Removes the receiver from the DOM.
   remove: function(unused) {
     Utils.assert('does not accept a selector', unused === undefined);
-    for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+    for ( let i = 0; this[i] != null; i++ ) {
+      let elem = this[i];
       if ( elem.parentNode ) {
-         elem.parentNode.removeChild( elem );
+        elem.parentNode.removeChild( elem );
       }
     }
-
     return this;
   },
 
   // ----------
   // Function: empty
   // Removes all of the reciever's children and HTML content from the DOM.
   empty: function() {
-    for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+    for ( let i = 0; this[i] != null; i++ ) {
+      let elem = this[i];
       while ( elem.firstChild ) {
         elem.removeChild( elem.firstChild );
       }
     }
-
     return this;
   },
 
   // ----------
   // Function: width
   // Returns the width of the receiver.
   width: function(unused) {
     Utils.assert('does not yet support setting', unused === undefined);
@@ -364,74 +349,79 @@ iQ.fn = iQ.prototype = {
   // Returns the height of the receiver.
   height: function(unused) {
     Utils.assert('does not yet support setting', unused === undefined);
     return parseInt(this.css('height'));
   },
 
   // ----------
   // Function: position
-  // Returns an object with the receiver's position in left and top properties.
+  // Returns an object with the receiver's position in left and top 
+  // properties.
   position: function(unused) {
     Utils.assert('does not yet support setting', unused === undefined);
     return {
       left: parseInt(this.css('left')),
       top: parseInt(this.css('top'))
     };
   },
 
   // ----------
   // Function: bounds
   // Returns a <Rect> with the receiver's bounds.
-  bounds: function(unused) {
-    Utils.assert('does not yet support setting', unused === undefined);
-    var p = this.position();
+  bounds: function() {
+    let p = this.position();
     return new Rect(p.left, p.top, this.width(), this.height());
   },
 
   // ----------
   // Function: data
   // Pass in both key and value to attach some data to the receiver;
   // pass in just key to retrieve it.
   data: function(key, value) {
-    var data = null;
+    let data = null;
     if (value === undefined) {
       Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1);
       data = this[0].iQData;
-      return (data ? data[key] : null);
+      if (data)
+        return data[key];
+      else
+        return null;
     }
 
-    for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+    for ( let i = 0; this[i] != null; i++ ) {
+      let elem = this[i];
       data = elem.iQData;
 
       if (!data)
         data = elem.iQData = {};
 
       data[key] = value;
     }
 
     return this;
   },
 
   // ----------
   // Function: html
-  // Given a value, sets the receiver's innerHTML to it; otherwise returns what's already there.
-  // TODO: security
+  // Given a value, sets the receiver's innerHTML to it; otherwise returns
+  // what's already there.
   html: function(value) {
     Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1);
     if (value === undefined)
       return this[0].innerHTML;
 
     this[0].innerHTML = value;
     return this;
   },
 
   // ----------
   // Function: text
-  // Given a value, sets the receiver's textContent to it; otherwise returns what's already there.
+  // Given a value, sets the receiver's textContent to it; otherwise returns
+  // what's already there.
   text: function(value) {
     Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1);
     if (value === undefined) {
       return this[0].textContent;
     }
 
     return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode(value));
   },
@@ -457,35 +447,34 @@ iQ.fn = iQ.prototype = {
     iQ(selector).append(this);
     return this;
   },
 
   // ----------
   // Function: append
   // Appends the result of iQ(selector) to the receiver.
   append: function(selector) {
-    Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1);
-    var object = iQ(selector);
-    Utils.assert('does not yet support multi-objects (or null objects)', object.length == 1);
+    let object = iQ(selector);
+    Utils.assert('does not yet support multi-objects (or null objects)', object.length == 1 && this.length == 1);
     this[0].appendChild(object[0]);
     return this;
   },
 
   // ----------
   // Function: attr
   // Sets or gets an attribute on the element(s).
   attr: function(key, value) {
     try {
       Utils.assert('string key', typeof key === 'string');
       if (value === undefined) {
         Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1);
         return this[0].getAttribute(key);
       }
-      for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
-        elem.setAttribute(key, value);
+      for ( let i = 0; this[i] != null; i++ ) {
+        this[i].setAttribute(key, value);
       }
     } catch(e) {
       Utils.log(e);
     }
 
     return this;
   },
 
@@ -494,48 +483,49 @@ iQ.fn = iQ.prototype = {
   // Sets or gets CSS properties on the receiver. When setting certain numerical properties,
   // will automatically add "px".
   //
   // Possible call patterns:
   //   a: object, b: undefined - sets with properties from a
   //   a: string, b: undefined - gets property specified by a
   //   a: string, b: string/number - sets property specified by a to b
   css: function(a, b) {
-    var properties = null;
+    let properties = null;
 
     if (typeof a === 'string') {
-      var key = a;
+      let key = a;
       if (b === undefined) {
         Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1);
 
-        var substitutions = {
+        let substitutions = {
           'MozTransform': '-moz-transform',
           'zIndex': 'z-index'
         };
 
         return window.getComputedStyle(this[0], null).getPropertyValue(substitutions[key] || key);
       }
       properties = {};
       properties[key] = b;
     } else {
       properties = a;
     }
 
-    var pixels = {
+    let pixels = {
       'left': true,
       'top': true,
       'right': true,
       'bottom': true,
       'width': true,
       'height': true
     };
 
-    for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
-      for (var key in properties) {
-        var value = properties[key];
+    for ( let i = 0; this[i] != null; i++ ) {
+      let elem = this[i];
+      for (let key in properties) {
+        let value = properties[key];
         if (pixels[key] && typeof(value) != 'string')
           value += 'px';
 
         if (key.indexOf('-') != -1)
           elem.style.setProperty(key, value, '');
         else
           elem.style[key] = value;
       }
@@ -549,58 +539,57 @@ iQ.fn = iQ.prototype = {
   // Uses CSS transitions to animate the element.
   //
   // Parameters:
   //   css - an object map of the CSS properties to change
   //   options - an object with various properites (see below)
   //
   // Possible "options" properties:
   //   duration - how long to animate, in milliseconds
-  //   easing - easing function to use. Possibilities include 'tabcandyBounce', 'easeInQuad'.
-  //     Default is 'ease'.
-  //   complete - function to call once the animation is done, takes nothing in, but "this"
-  //     is set to the element that was animated.
+  //   easing - easing function to use. Possibilities include 
+  //     'tabcandyBounce', 'easeInQuad'. Default is 'ease'.
+  //   complete - function to call once the animation is done, takes nothing 
+  //     in, but "this" is set to the element that was animated.
   animate: function(css, options) {
     try {
       Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1);
 
       if (!options)
         options = {};
 
-      var easings = {
+      let easings = {
         tabcandyBounce: 'cubic-bezier(0.0, 0.63, .6, 1.29)',
         easeInQuad: 'ease-in', // TODO: make it a real easeInQuad, or decide we don't care
         fast: 'cubic-bezier(0.7,0,1,1)'
       };
 
-      var duration = (options.duration || 400);
-      var easing = (easings[options.easing] || 'ease');
+      let duration = (options.duration || 400);
+      let easing = (easings[options.easing] || 'ease');
 
-      // The latest versions of Firefox do not animate from a non-explicitly set
-      // css properties. So for each element to be animated, go through and
-      // explicitly define 'em.
-      var rupper = /([A-Z])/g;
+      // The latest versions of Firefox do not animate from a non-explicitly 
+      // set css properties. So for each element to be animated, go through 
+      // and explicitly define 'em.
+      let rupper = /([A-Z])/g;
       this.each(function(elem){
-        var cStyle = window.getComputedStyle(elem, null);
-        for (var prop in css){
+        let cStyle = window.getComputedStyle(elem, null);
+        for (let prop in css){
           prop = prop.replace( rupper, "-$1" ).toLowerCase();
           iQ(elem).css(prop, cStyle.getPropertyValue(prop));
         }
       });
 
-
       this.css({
         '-moz-transition-property': 'all', // TODO: just animate the properties we're changing
         '-moz-transition-duration': (duration / 1000) + 's',
         '-moz-transition-timing-function': easing
       });
 
       this.css(css);
 
-      var self = this;
+      let self = this;
       Utils.timeout(function() {
         self.css({
           '-moz-transition-property': 'none',
           '-moz-transition-duration': '',
           '-moz-transition-timing-function': ''
         });
 
         if (typeof options.complete == "function")
@@ -612,31 +601,27 @@ iQ.fn = iQ.prototype = {
 
     return this;
   },
 
   // ----------
   // Function: fadeOut
   // Animates the receiver to full transparency. Calls callback on completion.
   fadeOut: function(callback) {
-    try {
-      Utils.assert('does not yet support duration', typeof callback == "function" || callback === undefined);
-      this.animate({
-        opacity: 0
-      }, {
-        duration: 400,
-        complete: function() {
-          iQ(this).css({display: 'none'});
-          if (typeof callback == "function")
-            callback.apply(this);
-        }
-      });
-    } catch(e) {
-      Utils.log(e);
-    }
+    Utils.assert('does not yet support duration', typeof callback == "function" || callback === undefined);
+    this.animate({
+      opacity: 0
+    }, {
+      duration: 400,
+      complete: function() {
+        iQ(this).css({display: 'none'});
+        if (typeof callback == "function")
+          callback.apply(this);
+      }
+    });
 
     return this;
   },
 
   // ----------
   // Function: fadeIn
   // Animates the receiver to full opacity.
   fadeIn: function() {
@@ -682,25 +667,26 @@ iQ.fn = iQ.prototype = {
 
   // ----------
   // Function: bind
   // Binds the given function to the given event type. Also wraps the function
   // in a try/catch block that does a Utils.log on any errors.
   bind: function(type, func) {
     Utils.assert('does not support eventData argument', typeof func == "function");
 
-    var handler = function(event) {
+    let handler = function(event) {
       try {
         return func.apply(this, [event]);
       } catch(e) {
         Utils.log(e);
       }
     };
 
-    for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+    for ( let i = 0; this[i] != null; i++ ) {
+      let elem = this[i];
       if (!elem.iQEventData)
         elem.iQEventData = {};
 
       if (!elem.iQEventData[type])
         elem.iQEventData[type] = [];
 
       elem.iQEventData[type].push({
         original: func,
@@ -715,80 +701,66 @@ iQ.fn = iQ.prototype = {
 
   // ----------
   // Function: one
   // Binds the given function to the given event type, but only for one call;
   // automatically unbinds after the event fires once.
   one: function(type, func) {
     Utils.assert('does not support eventData argument', typeof func == "function");
 
-    var handler = function(e) {
+    let handler = function(e) {
       iQ(this).unbind(type, handler);
       return func.apply(this, [e]);
     };
 
     return this.bind(type, handler);
   },
 
   // ----------
   // Function: unbind
   // Unbinds the given function from the given event type.
   unbind: function(type, func) {
     Utils.assert('Must provide a function', typeof func == "function");
 
-    for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
-      var handler = func;
+    for ( let i = 0; this[i] != null; i++ ) {
+      let elem = this[i];
+      let handler = func;
       if (elem.iQEventData && elem.iQEventData[type]) {
-        for (var a = 0, count = elem.iQEventData[type].length; a < count; a++) {
-          var pair = elem.iQEventData[type][a];
+        let count = elem.iQEventData[type].length;
+        for (let a = 0; a < count; a++) {
+          let pair = elem.iQEventData[type][a];
           if (pair.original == func) {
             handler = pair.modified;
             elem.iQEventData[type].splice(a, 1);
             break;
           }
         }
       }
 
       elem.removeEventListener(type, handler, false);
     }
 
     return this;
   }
 };
 
 // ----------
-// Give the init function the iQ prototype for later instantiation
-iQ.fn.init.prototype = iQ.fn;
+// Create various event aliases
+let events = [
+  'keyup',
+  'keydown',
+  'mouseup',
+  'mousedown',
+  'mouseover',
+  'mouseout',
+  'mousemove',
+  'click',
+  'resize',
+  'change',
+  'blur',
+  'focus'
+];
 
-// ----------
-// Create various event aliases
-(function() {
-  var events = [
-    'keyup',
-    'keydown',
-    'mouseup',
-    'mousedown',
-    'mouseover',
-    'mouseout',
-    'mousemove',
-    'click',
-    'resize',
-    'change',
-    'blur',
-    'focus'
-  ];
-
-  events.forEach(function(event) {
-    iQ.fn[event] = function(func) {
-      return this.bind(event, func);
-    };
-  });
-})();
-
-// ----------
-// All iQ objects should point back to these
-rootiQ = iQ(document);
-
-// ----------
-// Expose iQ to the global object
-window.iQ = iQ;
-
-})(window);
+events.forEach(function(event) {
+  iQClass.prototype[event] = function(func) {
+    return this.bind(event, func);
+  };
+});
--- a/browser/base/content/tabcandy/core/utils.js
+++ b/browser/base/content/tabcandy/core/utils.js
@@ -82,19 +82,17 @@ XPCOMUtils.defineLazyGetter(this, "gTabV
 // A simple point.
 //
 // Constructor: Point
 // If a is a Point, creates a copy of it. Otherwise, expects a to be x,
 // and creates a Point with it along with y. If either a or y are omitted,
 // 0 is used in their place.
 window.Point = function(a, y) {
   if (Utils.isPoint(a)) {
-    // Variable: x
     this.x = a.x;
-    // Variable: y
     this.y = a.y;
   } else {
     this.x = (Utils.isNumber(a) ? a : 0);
     this.y = (Utils.isNumber(y) ? y : 0);
   }
 };
 
 window.Point.prototype = {
@@ -115,71 +113,49 @@ window.Point.prototype = {
 // vertical properties.
 //
 // Constructor: Rect
 // If a is a Rect, creates a copy of it. Otherwise, expects a to be left,
 // and creates a Rect with it along with top, width, and height.
 window.Rect = function(a, top, width, height) {
   // Note: perhaps 'a' should really be called 'rectOrLeft'
   if (Utils.isRect(a)) {
-    // Variable: left
     this.left = a.left;
-
-    // Variable: top
     this.top = a.top;
-
-    // Variable: width
     this.width = a.width;
-
-    // Variable: height
     this.height = a.height;
   } else {
     this.left = a;
     this.top = top;
     this.width = width;
     this.height = height;
   }
 };
 
 window.Rect.prototype = {
-  // ----------
-  // Variable: right
-  get right() {
-    return this.left + this.width;
-  },
 
-  // ----------
+  get right() this.left + this.width,
   set right(value) {
     this.width = value - this.left;
   },
 
-  // ----------
-  // Variable: bottom
-  get bottom() {
-    return this.top + this.height;
-  },
-
-  // ----------
+  get bottom() this.top + this.height,
   set bottom(value) {
     this.height = value - this.top;
   },
 
   // ----------
   // Variable: xRange
   // Gives you a new <Range> for the horizontal dimension.
-  get xRange() {
-    return new Range(this.left,this.right);
-  },
+  get xRange() new Range(this.left,this.right),
 
   // ----------
   // Variable: yRange
   // Gives you a new <Range> for the vertical dimension.
-  get yRange() {
-    return new Range(this.top,this.bottom);
-  },
+  get yRange() new Range(this.top,this.bottom),
 
   // ----------
   // Function: intersects
   // Returns true if this rectangle intersects the given <Rect>.
   intersects: function(rect) {
     return (rect.right > this.left
         && rect.left < this.right
         && rect.bottom > this.top
@@ -307,19 +283,21 @@ window.Rect.prototype = {
     this.left = a.left;
     this.top = a.top;
     this.width = a.width;
     this.height = a.height;
   },
 
   // ----------
   // Function: css
-  // Returns an object with the dimensions of this rectangle, suitable for passing
-  // into iQ.fn.css.
-  // You could of course just pass the rectangle straight in, but this is cleaner.
+  // Returns an object with the dimensions of this rectangle, suitable for
+  // passing into iQ's css method. You could of course just pass the rectangle 
+  // straight in, but this is cleaner, as it removes all the extraneous
+  // properties. If you give a <Rect> to <iQClass.css> without this, it will
+  // ignore the extraneous properties, but result in CSS warnings.
   css: function() {
     return {
       left: this.left,
       top: this.top,
       width: this.width,
       height: this.height
     };
   }
@@ -483,17 +461,17 @@ window.Subscribable.prototype = {
     try {
       Utils.assertThrow("eventName must be a non-empty string",
           eventName && typeof(eventName) == "string");
 
       if (!this.subscribers || !this.subscribers[eventName])
         return;
 
       var self = this;
-      var subsCopy = Utils.merge([], this.subscribers[eventName]);
+      var subsCopy = this.subscribers[eventName].concat();
       subsCopy.forEach(function(object) {
         object.callback(self, eventInfo);
       });
     } catch(e) {
       Utils.log(e);
     }
   }
 };