browser/components/tabview/iq.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 // **********
michael@0 6 // Title: iq.js
michael@0 7 // Various helper functions, in the vein of jQuery.
michael@0 8
michael@0 9 // ----------
michael@0 10 // Function: iQ
michael@0 11 // Returns an iQClass object which represents an individual element or a group
michael@0 12 // of elements. It works pretty much like jQuery(), with a few exceptions,
michael@0 13 // most notably that you can't use strings with complex html,
michael@0 14 // just simple tags like '<div>'.
michael@0 15 function iQ(selector, context) {
michael@0 16 // The iQ object is actually just the init constructor 'enhanced'
michael@0 17 return new iQClass(selector, context);
michael@0 18 };
michael@0 19
michael@0 20 // A simple way to check for HTML strings or ID strings
michael@0 21 // (both of which we optimize for)
michael@0 22 let quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/;
michael@0 23
michael@0 24 // Match a standalone tag
michael@0 25 let rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/;
michael@0 26
michael@0 27 // ##########
michael@0 28 // Class: iQClass
michael@0 29 // The actual class of iQ result objects, representing an individual element
michael@0 30 // or a group of elements.
michael@0 31 //
michael@0 32 // ----------
michael@0 33 // Function: iQClass
michael@0 34 // You don't call this directly; this is what's called by iQ().
michael@0 35 function iQClass(selector, context) {
michael@0 36
michael@0 37 // Handle $(""), $(null), or $(undefined)
michael@0 38 if (!selector) {
michael@0 39 return this;
michael@0 40 }
michael@0 41
michael@0 42 // Handle $(DOMElement)
michael@0 43 if (selector.nodeType) {
michael@0 44 this.context = selector;
michael@0 45 this[0] = selector;
michael@0 46 this.length = 1;
michael@0 47 return this;
michael@0 48 }
michael@0 49
michael@0 50 // The body element only exists once, optimize finding it
michael@0 51 if (selector === "body" && !context) {
michael@0 52 this.context = document;
michael@0 53 this[0] = document.body;
michael@0 54 this.selector = "body";
michael@0 55 this.length = 1;
michael@0 56 return this;
michael@0 57 }
michael@0 58
michael@0 59 // Handle HTML strings
michael@0 60 if (typeof selector === "string") {
michael@0 61 // Are we dealing with HTML string or an ID?
michael@0 62
michael@0 63 let match = quickExpr.exec(selector);
michael@0 64
michael@0 65 // Verify a match, and that no context was specified for #id
michael@0 66 if (match && (match[1] || !context)) {
michael@0 67
michael@0 68 // HANDLE $(html) -> $(array)
michael@0 69 if (match[1]) {
michael@0 70 let doc = (context ? context.ownerDocument || context : document);
michael@0 71
michael@0 72 // If a single string is passed in and it's a single tag
michael@0 73 // just do a createElement and skip the rest
michael@0 74 let ret = rsingleTag.exec(selector);
michael@0 75
michael@0 76 if (ret) {
michael@0 77 if (Utils.isPlainObject(context)) {
michael@0 78 Utils.assert(false, 'does not support HTML creation with context');
michael@0 79 } else {
michael@0 80 selector = [doc.createElement(ret[1])];
michael@0 81 }
michael@0 82
michael@0 83 } else {
michael@0 84 Utils.assert(false, 'does not support complex HTML creation');
michael@0 85 }
michael@0 86
michael@0 87 return Utils.merge(this, selector);
michael@0 88
michael@0 89 // HANDLE $("#id")
michael@0 90 } else {
michael@0 91 let elem = document.getElementById(match[2]);
michael@0 92
michael@0 93 if (elem) {
michael@0 94 this.length = 1;
michael@0 95 this[0] = elem;
michael@0 96 }
michael@0 97
michael@0 98 this.context = document;
michael@0 99 this.selector = selector;
michael@0 100 return this;
michael@0 101 }
michael@0 102
michael@0 103 // HANDLE $("TAG")
michael@0 104 } else if (!context && /^\w+$/.test(selector)) {
michael@0 105 this.selector = selector;
michael@0 106 this.context = document;
michael@0 107 selector = document.getElementsByTagName(selector);
michael@0 108 return Utils.merge(this, selector);
michael@0 109
michael@0 110 // HANDLE $(expr, $(...))
michael@0 111 } else if (!context || context.iq) {
michael@0 112 return (context || iQ(document)).find(selector);
michael@0 113
michael@0 114 // HANDLE $(expr, context)
michael@0 115 // (which is just equivalent to: $(context).find(expr)
michael@0 116 } else {
michael@0 117 return iQ(context).find(selector);
michael@0 118 }
michael@0 119
michael@0 120 // HANDLE $(function)
michael@0 121 // Shortcut for document ready
michael@0 122 } else if (typeof selector == "function") {
michael@0 123 Utils.log('iQ does not support ready functions');
michael@0 124 return null;
michael@0 125 }
michael@0 126
michael@0 127 if ("selector" in selector) {
michael@0 128 this.selector = selector.selector;
michael@0 129 this.context = selector.context;
michael@0 130 }
michael@0 131
michael@0 132 let ret = this || [];
michael@0 133 if (selector != null) {
michael@0 134 // The window, strings (and functions) also have 'length'
michael@0 135 if (selector.length == null || typeof selector == "string" || selector.setInterval) {
michael@0 136 Array.push(ret, selector);
michael@0 137 } else {
michael@0 138 Utils.merge(ret, selector);
michael@0 139 }
michael@0 140 }
michael@0 141 return ret;
michael@0 142 };
michael@0 143
michael@0 144 iQClass.prototype = {
michael@0 145
michael@0 146 // ----------
michael@0 147 // Function: toString
michael@0 148 // Prints [iQ...] for debug use
michael@0 149 toString: function iQClass_toString() {
michael@0 150 if (this.length > 1) {
michael@0 151 if (this.selector)
michael@0 152 return "[iQ (" + this.selector + ")]";
michael@0 153 else
michael@0 154 return "[iQ multi-object]";
michael@0 155 }
michael@0 156
michael@0 157 if (this.length == 1)
michael@0 158 return "[iQ (" + this[0].toString() + ")]";
michael@0 159
michael@0 160 return "[iQ non-object]";
michael@0 161 },
michael@0 162
michael@0 163 // Start with an empty selector
michael@0 164 selector: "",
michael@0 165
michael@0 166 // The default length of a iQ object is 0
michael@0 167 length: 0,
michael@0 168
michael@0 169 // ----------
michael@0 170 // Function: each
michael@0 171 // Execute a callback for every element in the matched set.
michael@0 172 each: function iQClass_each(callback) {
michael@0 173 if (typeof callback != "function") {
michael@0 174 Utils.assert(false, "each's argument must be a function");
michael@0 175 return null;
michael@0 176 }
michael@0 177 for (let i = 0; this[i] != null && callback(this[i]) !== false; i++) {}
michael@0 178 return this;
michael@0 179 },
michael@0 180
michael@0 181 // ----------
michael@0 182 // Function: addClass
michael@0 183 // Adds the given class(es) to the receiver.
michael@0 184 addClass: function iQClass_addClass(value) {
michael@0 185 Utils.assertThrow(typeof value == "string" && value,
michael@0 186 'requires a valid string argument');
michael@0 187
michael@0 188 let length = this.length;
michael@0 189 for (let i = 0; i < length; i++) {
michael@0 190 let elem = this[i];
michael@0 191 if (elem.nodeType === 1) {
michael@0 192 value.split(/\s+/).forEach(function(className) {
michael@0 193 elem.classList.add(className);
michael@0 194 });
michael@0 195 }
michael@0 196 }
michael@0 197
michael@0 198 return this;
michael@0 199 },
michael@0 200
michael@0 201 // ----------
michael@0 202 // Function: removeClass
michael@0 203 // Removes the given class(es) from the receiver.
michael@0 204 removeClass: function iQClass_removeClass(value) {
michael@0 205 if (typeof value != "string" || !value) {
michael@0 206 Utils.assert(false, 'does not support function argument');
michael@0 207 return null;
michael@0 208 }
michael@0 209
michael@0 210 let length = this.length;
michael@0 211 for (let i = 0; i < length; i++) {
michael@0 212 let elem = this[i];
michael@0 213 if (elem.nodeType === 1 && elem.className) {
michael@0 214 value.split(/\s+/).forEach(function(className) {
michael@0 215 elem.classList.remove(className);
michael@0 216 });
michael@0 217 }
michael@0 218 }
michael@0 219
michael@0 220 return this;
michael@0 221 },
michael@0 222
michael@0 223 // ----------
michael@0 224 // Function: hasClass
michael@0 225 // Returns true is the receiver has the given css class.
michael@0 226 hasClass: function iQClass_hasClass(singleClassName) {
michael@0 227 let length = this.length;
michael@0 228 for (let i = 0; i < length; i++) {
michael@0 229 if (this[i].classList.contains(singleClassName)) {
michael@0 230 return true;
michael@0 231 }
michael@0 232 }
michael@0 233 return false;
michael@0 234 },
michael@0 235
michael@0 236 // ----------
michael@0 237 // Function: find
michael@0 238 // Searches the receiver and its children, returning a new iQ object with
michael@0 239 // elements that match the given selector.
michael@0 240 find: function iQClass_find(selector) {
michael@0 241 let ret = [];
michael@0 242 let length = 0;
michael@0 243
michael@0 244 let l = this.length;
michael@0 245 for (let i = 0; i < l; i++) {
michael@0 246 length = ret.length;
michael@0 247 try {
michael@0 248 Utils.merge(ret, this[i].querySelectorAll(selector));
michael@0 249 } catch(e) {
michael@0 250 Utils.log('iQ.find error (bad selector)', e);
michael@0 251 }
michael@0 252
michael@0 253 if (i > 0) {
michael@0 254 // Make sure that the results are unique
michael@0 255 for (let n = length; n < ret.length; n++) {
michael@0 256 for (let r = 0; r < length; r++) {
michael@0 257 if (ret[r] === ret[n]) {
michael@0 258 ret.splice(n--, 1);
michael@0 259 break;
michael@0 260 }
michael@0 261 }
michael@0 262 }
michael@0 263 }
michael@0 264 }
michael@0 265
michael@0 266 return iQ(ret);
michael@0 267 },
michael@0 268
michael@0 269 // ----------
michael@0 270 // Function: contains
michael@0 271 // Check to see if a given DOM node descends from the receiver.
michael@0 272 contains: function iQClass_contains(selector) {
michael@0 273 Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
michael@0 274
michael@0 275 // fast path when querySelector() can be used
michael@0 276 if ('string' == typeof selector)
michael@0 277 return null != this[0].querySelector(selector);
michael@0 278
michael@0 279 let object = iQ(selector);
michael@0 280 Utils.assert(object.length <= 1, 'does not yet support multi-objects');
michael@0 281
michael@0 282 let elem = object[0];
michael@0 283 if (!elem || !elem.parentNode)
michael@0 284 return false;
michael@0 285
michael@0 286 do {
michael@0 287 elem = elem.parentNode;
michael@0 288 } while (elem && this[0] != elem);
michael@0 289
michael@0 290 return this[0] == elem;
michael@0 291 },
michael@0 292
michael@0 293 // ----------
michael@0 294 // Function: remove
michael@0 295 // Removes the receiver from the DOM.
michael@0 296 remove: function iQClass_remove(options) {
michael@0 297 if (!options || !options.preserveEventHandlers)
michael@0 298 this.unbindAll();
michael@0 299 for (let i = 0; this[i] != null; i++) {
michael@0 300 let elem = this[i];
michael@0 301 if (elem.parentNode) {
michael@0 302 elem.parentNode.removeChild(elem);
michael@0 303 }
michael@0 304 }
michael@0 305 return this;
michael@0 306 },
michael@0 307
michael@0 308 // ----------
michael@0 309 // Function: empty
michael@0 310 // Removes all of the reciever's children and HTML content from the DOM.
michael@0 311 empty: function iQClass_empty() {
michael@0 312 for (let i = 0; this[i] != null; i++) {
michael@0 313 let elem = this[i];
michael@0 314 while (elem.firstChild) {
michael@0 315 iQ(elem.firstChild).unbindAll();
michael@0 316 elem.removeChild(elem.firstChild);
michael@0 317 }
michael@0 318 }
michael@0 319 return this;
michael@0 320 },
michael@0 321
michael@0 322 // ----------
michael@0 323 // Function: width
michael@0 324 // Returns the width of the receiver, including padding and border.
michael@0 325 width: function iQClass_width() {
michael@0 326 return Math.floor(this[0].offsetWidth);
michael@0 327 },
michael@0 328
michael@0 329 // ----------
michael@0 330 // Function: height
michael@0 331 // Returns the height of the receiver, including padding and border.
michael@0 332 height: function iQClass_height() {
michael@0 333 return Math.floor(this[0].offsetHeight);
michael@0 334 },
michael@0 335
michael@0 336 // ----------
michael@0 337 // Function: position
michael@0 338 // Returns an object with the receiver's position in left and top
michael@0 339 // properties.
michael@0 340 position: function iQClass_position() {
michael@0 341 let bounds = this.bounds();
michael@0 342 return new Point(bounds.left, bounds.top);
michael@0 343 },
michael@0 344
michael@0 345 // ----------
michael@0 346 // Function: bounds
michael@0 347 // Returns a <Rect> with the receiver's bounds.
michael@0 348 bounds: function iQClass_bounds() {
michael@0 349 Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
michael@0 350 let rect = this[0].getBoundingClientRect();
michael@0 351 return new Rect(Math.floor(rect.left), Math.floor(rect.top),
michael@0 352 Math.floor(rect.width), Math.floor(rect.height));
michael@0 353 },
michael@0 354
michael@0 355 // ----------
michael@0 356 // Function: data
michael@0 357 // Pass in both key and value to attach some data to the receiver;
michael@0 358 // pass in just key to retrieve it.
michael@0 359 data: function iQClass_data(key, value) {
michael@0 360 let data = null;
michael@0 361 if (value === undefined) {
michael@0 362 Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
michael@0 363 data = this[0].iQData;
michael@0 364 if (data)
michael@0 365 return data[key];
michael@0 366 else
michael@0 367 return null;
michael@0 368 }
michael@0 369
michael@0 370 for (let i = 0; this[i] != null; i++) {
michael@0 371 let elem = this[i];
michael@0 372 data = elem.iQData;
michael@0 373
michael@0 374 if (!data)
michael@0 375 data = elem.iQData = {};
michael@0 376
michael@0 377 data[key] = value;
michael@0 378 }
michael@0 379
michael@0 380 return this;
michael@0 381 },
michael@0 382
michael@0 383 // ----------
michael@0 384 // Function: html
michael@0 385 // Given a value, sets the receiver's innerHTML to it; otherwise returns
michael@0 386 // what's already there.
michael@0 387 html: function iQClass_html(value) {
michael@0 388 Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
michael@0 389 if (value === undefined)
michael@0 390 return this[0].innerHTML;
michael@0 391
michael@0 392 this[0].innerHTML = value;
michael@0 393 return this;
michael@0 394 },
michael@0 395
michael@0 396 // ----------
michael@0 397 // Function: text
michael@0 398 // Given a value, sets the receiver's textContent to it; otherwise returns
michael@0 399 // what's already there.
michael@0 400 text: function iQClass_text(value) {
michael@0 401 Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
michael@0 402 if (value === undefined) {
michael@0 403 return this[0].textContent;
michael@0 404 }
michael@0 405
michael@0 406 return this.empty().append((this[0] && this[0].ownerDocument || document).createTextNode(value));
michael@0 407 },
michael@0 408
michael@0 409 // ----------
michael@0 410 // Function: val
michael@0 411 // Given a value, sets the receiver's value to it; otherwise returns what's already there.
michael@0 412 val: function iQClass_val(value) {
michael@0 413 Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
michael@0 414 if (value === undefined) {
michael@0 415 return this[0].value;
michael@0 416 }
michael@0 417
michael@0 418 this[0].value = value;
michael@0 419 return this;
michael@0 420 },
michael@0 421
michael@0 422 // ----------
michael@0 423 // Function: appendTo
michael@0 424 // Appends the receiver to the result of iQ(selector).
michael@0 425 appendTo: function iQClass_appendTo(selector) {
michael@0 426 Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
michael@0 427 iQ(selector).append(this);
michael@0 428 return this;
michael@0 429 },
michael@0 430
michael@0 431 // ----------
michael@0 432 // Function: append
michael@0 433 // Appends the result of iQ(selector) to the receiver.
michael@0 434 append: function iQClass_append(selector) {
michael@0 435 let object = iQ(selector);
michael@0 436 Utils.assert(object.length == 1 && this.length == 1,
michael@0 437 'does not yet support multi-objects (or null objects)');
michael@0 438 this[0].appendChild(object[0]);
michael@0 439 return this;
michael@0 440 },
michael@0 441
michael@0 442 // ----------
michael@0 443 // Function: attr
michael@0 444 // Sets or gets an attribute on the element(s).
michael@0 445 attr: function iQClass_attr(key, value) {
michael@0 446 Utils.assert(typeof key === 'string', 'string key');
michael@0 447 if (value === undefined) {
michael@0 448 Utils.assert(this.length == 1, 'retrieval does not support multi-objects (or null objects)');
michael@0 449 return this[0].getAttribute(key);
michael@0 450 }
michael@0 451
michael@0 452 for (let i = 0; this[i] != null; i++)
michael@0 453 this[i].setAttribute(key, value);
michael@0 454
michael@0 455 return this;
michael@0 456 },
michael@0 457
michael@0 458 // ----------
michael@0 459 // Function: css
michael@0 460 // Sets or gets CSS properties on the receiver. When setting certain numerical properties,
michael@0 461 // will automatically add "px". A property can be removed by setting it to null.
michael@0 462 //
michael@0 463 // Possible call patterns:
michael@0 464 // a: object, b: undefined - sets with properties from a
michael@0 465 // a: string, b: undefined - gets property specified by a
michael@0 466 // a: string, b: string/number - sets property specified by a to b
michael@0 467 css: function iQClass_css(a, b) {
michael@0 468 let properties = null;
michael@0 469
michael@0 470 if (typeof a === 'string') {
michael@0 471 let key = a;
michael@0 472 if (b === undefined) {
michael@0 473 Utils.assert(this.length == 1, 'retrieval does not support multi-objects (or null objects)');
michael@0 474
michael@0 475 return window.getComputedStyle(this[0], null).getPropertyValue(key);
michael@0 476 }
michael@0 477 properties = {};
michael@0 478 properties[key] = b;
michael@0 479 } else if (a instanceof Rect) {
michael@0 480 properties = {
michael@0 481 left: a.left,
michael@0 482 top: a.top,
michael@0 483 width: a.width,
michael@0 484 height: a.height
michael@0 485 };
michael@0 486 } else {
michael@0 487 properties = a;
michael@0 488 }
michael@0 489
michael@0 490 let pixels = {
michael@0 491 'left': true,
michael@0 492 'top': true,
michael@0 493 'right': true,
michael@0 494 'bottom': true,
michael@0 495 'width': true,
michael@0 496 'height': true
michael@0 497 };
michael@0 498
michael@0 499 for (let i = 0; this[i] != null; i++) {
michael@0 500 let elem = this[i];
michael@0 501 for (let key in properties) {
michael@0 502 let value = properties[key];
michael@0 503
michael@0 504 if (pixels[key] && typeof value != 'string')
michael@0 505 value += 'px';
michael@0 506
michael@0 507 if (value == null) {
michael@0 508 elem.style.removeProperty(key);
michael@0 509 } else if (key.indexOf('-') != -1)
michael@0 510 elem.style.setProperty(key, value, '');
michael@0 511 else
michael@0 512 elem.style[key] = value;
michael@0 513 }
michael@0 514 }
michael@0 515
michael@0 516 return this;
michael@0 517 },
michael@0 518
michael@0 519 // ----------
michael@0 520 // Function: animate
michael@0 521 // Uses CSS transitions to animate the element.
michael@0 522 //
michael@0 523 // Parameters:
michael@0 524 // css - an object map of the CSS properties to change
michael@0 525 // options - an object with various properites (see below)
michael@0 526 //
michael@0 527 // Possible "options" properties:
michael@0 528 // duration - how long to animate, in milliseconds
michael@0 529 // easing - easing function to use. Possibilities include
michael@0 530 // "tabviewBounce", "easeInQuad". Default is "ease".
michael@0 531 // complete - function to call once the animation is done, takes nothing
michael@0 532 // in, but "this" is set to the element that was animated.
michael@0 533 animate: function iQClass_animate(css, options) {
michael@0 534 Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
michael@0 535
michael@0 536 if (!options)
michael@0 537 options = {};
michael@0 538
michael@0 539 let easings = {
michael@0 540 tabviewBounce: "cubic-bezier(0.0, 0.63, .6, 1.29)",
michael@0 541 easeInQuad: 'ease-in', // TODO: make it a real easeInQuad, or decide we don't care
michael@0 542 fast: 'cubic-bezier(0.7,0,1,1)'
michael@0 543 };
michael@0 544
michael@0 545 let duration = (options.duration || 400);
michael@0 546 let easing = (easings[options.easing] || 'ease');
michael@0 547
michael@0 548 if (css instanceof Rect) {
michael@0 549 css = {
michael@0 550 left: css.left,
michael@0 551 top: css.top,
michael@0 552 width: css.width,
michael@0 553 height: css.height
michael@0 554 };
michael@0 555 }
michael@0 556
michael@0 557
michael@0 558 // The latest versions of Firefox do not animate from a non-explicitly
michael@0 559 // set css properties. So for each element to be animated, go through
michael@0 560 // and explicitly define 'em.
michael@0 561 let rupper = /([A-Z])/g;
michael@0 562 this.each(function(elem) {
michael@0 563 let cStyle = window.getComputedStyle(elem, null);
michael@0 564 for (let prop in css) {
michael@0 565 prop = prop.replace(rupper, "-$1").toLowerCase();
michael@0 566 iQ(elem).css(prop, cStyle.getPropertyValue(prop));
michael@0 567 }
michael@0 568 });
michael@0 569
michael@0 570 this.css({
michael@0 571 'transition-property': Object.keys(css).join(", "),
michael@0 572 'transition-duration': (duration / 1000) + 's',
michael@0 573 'transition-timing-function': easing
michael@0 574 });
michael@0 575
michael@0 576 this.css(css);
michael@0 577
michael@0 578 let self = this;
michael@0 579 setTimeout(function() {
michael@0 580 self.css({
michael@0 581 'transition-property': 'none',
michael@0 582 'transition-duration': '',
michael@0 583 'transition-timing-function': ''
michael@0 584 });
michael@0 585
michael@0 586 if (typeof options.complete == "function")
michael@0 587 options.complete.apply(self);
michael@0 588 }, duration);
michael@0 589
michael@0 590 return this;
michael@0 591 },
michael@0 592
michael@0 593 // ----------
michael@0 594 // Function: fadeOut
michael@0 595 // Animates the receiver to full transparency. Calls callback on completion.
michael@0 596 fadeOut: function iQClass_fadeOut(callback) {
michael@0 597 Utils.assert(typeof callback == "function" || callback === undefined,
michael@0 598 'does not yet support duration');
michael@0 599
michael@0 600 this.animate({
michael@0 601 opacity: 0
michael@0 602 }, {
michael@0 603 duration: 400,
michael@0 604 complete: function() {
michael@0 605 iQ(this).css({display: 'none'});
michael@0 606 if (typeof callback == "function")
michael@0 607 callback.apply(this);
michael@0 608 }
michael@0 609 });
michael@0 610
michael@0 611 return this;
michael@0 612 },
michael@0 613
michael@0 614 // ----------
michael@0 615 // Function: fadeIn
michael@0 616 // Animates the receiver to full opacity.
michael@0 617 fadeIn: function iQClass_fadeIn() {
michael@0 618 this.css({display: ''});
michael@0 619 this.animate({
michael@0 620 opacity: 1
michael@0 621 }, {
michael@0 622 duration: 400
michael@0 623 });
michael@0 624
michael@0 625 return this;
michael@0 626 },
michael@0 627
michael@0 628 // ----------
michael@0 629 // Function: hide
michael@0 630 // Hides the receiver.
michael@0 631 hide: function iQClass_hide() {
michael@0 632 this.css({display: 'none', opacity: 0});
michael@0 633 return this;
michael@0 634 },
michael@0 635
michael@0 636 // ----------
michael@0 637 // Function: show
michael@0 638 // Shows the receiver.
michael@0 639 show: function iQClass_show() {
michael@0 640 this.css({display: '', opacity: 1});
michael@0 641 return this;
michael@0 642 },
michael@0 643
michael@0 644 // ----------
michael@0 645 // Function: bind
michael@0 646 // Binds the given function to the given event type. Also wraps the function
michael@0 647 // in a try/catch block that does a Utils.log on any errors.
michael@0 648 bind: function iQClass_bind(type, func) {
michael@0 649 let handler = function(event) func.apply(this, [event]);
michael@0 650
michael@0 651 for (let i = 0; this[i] != null; i++) {
michael@0 652 let elem = this[i];
michael@0 653 if (!elem.iQEventData)
michael@0 654 elem.iQEventData = {};
michael@0 655
michael@0 656 if (!elem.iQEventData[type])
michael@0 657 elem.iQEventData[type] = [];
michael@0 658
michael@0 659 elem.iQEventData[type].push({
michael@0 660 original: func,
michael@0 661 modified: handler
michael@0 662 });
michael@0 663
michael@0 664 elem.addEventListener(type, handler, false);
michael@0 665 }
michael@0 666
michael@0 667 return this;
michael@0 668 },
michael@0 669
michael@0 670 // ----------
michael@0 671 // Function: one
michael@0 672 // Binds the given function to the given event type, but only for one call;
michael@0 673 // automatically unbinds after the event fires once.
michael@0 674 one: function iQClass_one(type, func) {
michael@0 675 Utils.assert(typeof func == "function", 'does not support eventData argument');
michael@0 676
michael@0 677 let handler = function(e) {
michael@0 678 iQ(this).unbind(type, handler);
michael@0 679 return func.apply(this, [e]);
michael@0 680 };
michael@0 681
michael@0 682 return this.bind(type, handler);
michael@0 683 },
michael@0 684
michael@0 685 // ----------
michael@0 686 // Function: unbind
michael@0 687 // Unbinds the given function from the given event type.
michael@0 688 unbind: function iQClass_unbind(type, func) {
michael@0 689 Utils.assert(typeof func == "function", 'Must provide a function');
michael@0 690
michael@0 691 for (let i = 0; this[i] != null; i++) {
michael@0 692 let elem = this[i];
michael@0 693 let handler = func;
michael@0 694 if (elem.iQEventData && elem.iQEventData[type]) {
michael@0 695 let count = elem.iQEventData[type].length;
michael@0 696 for (let a = 0; a < count; a++) {
michael@0 697 let pair = elem.iQEventData[type][a];
michael@0 698 if (pair.original == func) {
michael@0 699 handler = pair.modified;
michael@0 700 elem.iQEventData[type].splice(a, 1);
michael@0 701 if (!elem.iQEventData[type].length) {
michael@0 702 delete elem.iQEventData[type];
michael@0 703 if (!Object.keys(elem.iQEventData).length)
michael@0 704 delete elem.iQEventData;
michael@0 705 }
michael@0 706 break;
michael@0 707 }
michael@0 708 }
michael@0 709 }
michael@0 710
michael@0 711 elem.removeEventListener(type, handler, false);
michael@0 712 }
michael@0 713
michael@0 714 return this;
michael@0 715 },
michael@0 716
michael@0 717 // ----------
michael@0 718 // Function: unbindAll
michael@0 719 // Unbinds all event handlers.
michael@0 720 unbindAll: function iQClass_unbindAll() {
michael@0 721 for (let i = 0; this[i] != null; i++) {
michael@0 722 let elem = this[i];
michael@0 723
michael@0 724 for (let j = 0; j < elem.childElementCount; j++)
michael@0 725 iQ(elem.children[j]).unbindAll();
michael@0 726
michael@0 727 if (!elem.iQEventData)
michael@0 728 continue;
michael@0 729
michael@0 730 Object.keys(elem.iQEventData).forEach(function (type) {
michael@0 731 while (elem.iQEventData && elem.iQEventData[type])
michael@0 732 this.unbind(type, elem.iQEventData[type][0].original);
michael@0 733 }, this);
michael@0 734 }
michael@0 735
michael@0 736 return this;
michael@0 737 }
michael@0 738 };
michael@0 739
michael@0 740 // ----------
michael@0 741 // Create various event aliases
michael@0 742 let events = [
michael@0 743 'keyup',
michael@0 744 'keydown',
michael@0 745 'keypress',
michael@0 746 'mouseup',
michael@0 747 'mousedown',
michael@0 748 'mouseover',
michael@0 749 'mouseout',
michael@0 750 'mousemove',
michael@0 751 'click',
michael@0 752 'dblclick',
michael@0 753 'resize',
michael@0 754 'change',
michael@0 755 'blur',
michael@0 756 'focus'
michael@0 757 ];
michael@0 758
michael@0 759 events.forEach(function(event) {
michael@0 760 iQClass.prototype[event] = function(func) {
michael@0 761 return this.bind(event, func);
michael@0 762 };
michael@0 763 });

mercurial