windex.js (31940B)
1 var Cc = Components.classes, 2 Ci = Components.interfaces, 3 Cu = Components.utils; 4 5 var isNode = function (object) { 6 if (object instanceof Ci.nsIDOMText) return false; 7 return (object instanceof Ci.nsIDOMNode) || 8 (object instanceof Ci.nsIDOMHTMLDocument) || 9 (object instanceof Ci.nsIDOMWindow); 10 }; 11 12 // See: https://bugzilla.mozilla.org/show_bug.cgi?id=550612 13 // See: https://developer.mozilla.org/en/DOM/DOMImplementation.createHTMLDocument 14 // See: http://userscripts.org/guides/9 15 var safeHTMLToDOM = function (document, html) { 16 if (document instanceof Ci.nsIImageDocument) { 17 if (document.implementation.createHTMLDocument) { 18 // FF4 ImageDocuments 19 var safeDoc = document.implementation.createHTMLDocument('safe'); 20 var holder = safeDoc.createElement('div'); 21 holder.innerHTML = html; 22 return document.importNode(holder, true); 23 } 24 else { 25 // FF3.* ImageDocuments 26 var docType = document.implementation.createDocumentType( 27 "html", 28 "-//W3C//DTD HTML 4.01 Transitional//EN", 29 "http://www.w3.org/TR/html4/loose.dtd" 30 ); 31 var holderDoc = document.implementation.createDocument('', '', docType); 32 var holder = holderDoc.createElement('div'); 33 holder.innerHTML = html; 34 return document.importNode(holder, true); 35 } 36 } 37 else { 38 var holder = document.createElement('div'); 39 holder.innerHTML = html; 40 return holder; 41 } 42 }; 43 44 var Windex = exports = function(selector, context) { 45 // no sense in wrapping the nodes twice 46 if (selector instanceof WindexNodes) { return selector; } 47 48 if (isNode(selector)) { return new WindexNodes([selector]); } 49 50 if (typeof selector != "string") { 51 throw new Error("$(" + selector + ", " + context + "): selector is not a Node or a String"); 52 } 53 54 // TODO: return documents, windows 55 if (selector == "body") return Windex(Windex.defaultContext()); 56 if (selector == "!document") { 57 var document = Windex.defaultContext().ownerDocument; 58 return Windex(document); 59 } 60 if (selector == "!window") { 61 return Windex(Windex.defaultContext()); 62 var document = Windex.defaultContext().ownerDocument; 63 var window = document.defaultView; 64 return Windex(window); 65 } 66 67 if (!context) { 68 return Windex(selector, Windex.defaultContext()); 69 } 70 71 // if passed in a list of nodes, meant the first one 72 if (context instanceof WindexNodes) { context = context[0]; } 73 if (!isNode(context)) { 74 throw new Error("$(" + selector + ", " + context + "): context is not a Node or a String"); 75 } 76 context = new XPCNativeWrapper(context); 77 78 return new WindexNodes([context], selector, context).find(selector); 79 }; 80 81 Windex.defaultContext = null; 82 83 // See: http://api.jquery.com/jQuery.extend/ 84 Windex.extend = function () { 85 if (arguments.length == 1) { return this._extendWindex(arguments[0]); }; 86 return this._extendDestructiveMerge.apply(this, arguments); 87 } 88 89 Windex._extendWindex = function (methods) { 90 for (name in methods) { this[name] = methods[name]; } 91 }; 92 93 Windex._extendDestructiveMerge = function () { 94 var target = (arguments[0]) ? arguments[0] : {}; 95 for (var i = 1; i < arguments.length; i++) { 96 var object = arguments[i]; 97 for (key in object) { 98 target[key] = object[key]; 99 } 100 } 101 return target; 102 }; 103 104 var is_array = exports.is_array = function (obj) { 105 return Object.prototype.toString.apply(obj) === "[object Array]"; 106 }; 107 108 Windex.each = function (a, f) { 109 if (is_array(a) || a instanceof WindexNodes) { 110 a.forEach(function (e, i) { f(i, e); }); 111 return; 112 } 113 for (key in a) { f(key, a[key]); } 114 }; 115 Windex.map = function (a, f) { return a.map(f); }; 116 117 Windex.inArray = function (e, a) { 118 return a.some(function (aE) { return e == aE; }); 119 }; 120 121 // See: http://github.com/jeresig/sizzle/blob/master/sizzle.js 122 Windex.contains = function (ancestor, descendant) { 123 return !!(ancestor.compareDocumentPosition(descendant) & 16); 124 }; 125 126 Windex.matchesSelector = function (node, selector) { 127 return Windex(selector, node.parentNode).some(function (match) { 128 return node.wrappedJSObject === match.wrappedJSObject; 129 }); 130 }; 131 132 // See: http://api.jquery.com/jQuery.isEmptyObject/ 133 Windex.isEmptyObject = function (obj) { 134 for(var i in obj) { return false; } 135 return true; 136 }; 137 138 var WindexNodes = function (nodes, selector, context) { 139 for (i = 0; i < nodes.length; i++) { 140 // See: https://developer.mozilla.org/en/XPCNativeWrapper 141 this.push(new XPCNativeWrapper(nodes[i])); 142 } 143 this._selector = selector; 144 this._context = context; 145 }; 146 147 WindexNodes.prototype = new Array(); 148 149 // MozRepl freaks out when trying to print WindexNodes 150 WindexNodes.prototype.toString = function () { return "[object WindexNodes]"; }; 151 152 WindexNodes.prototype.find = function (selector) { 153 var context = this[0]; 154 try { 155 // See: https://developer.mozilla.org/En/DOM/Element.querySelectorAll 156 var matches = context.querySelectorAll(selector); 157 } catch (e if (e.name == "NS_ERROR_DOM_SYNTAX_ERR")) { 158 throw new Error("invalid selector: '" + selector + "'"); 159 } 160 161 // turn the matches NodeList into an array 162 var nodes = []; 163 for (i = 0; i < matches.length; i++) { nodes.push(matches[i]); } 164 165 return new WindexNodes(nodes, selector, context); 166 }; 167 168 WindexNodes.prototype.toArray = function () { return [].concat(this); } 169 170 WindexNodes.prototype.each = function (f) { return Windex.each(this, f); }; 171 172 WindexNodes.prototype.filter = function (f) { 173 return Array.prototype.filter.apply(this,[function (n,i) { return f(i,n); }]); 174 }; 175 176 // See: http://api.jquery.com/children/ 177 WindexNodes.prototype.children = function (selector) { 178 if (selector) { return this._childrenMatching(selector); } 179 var children = []; 180 this.forEach(function (node) { 181 var nodeList = node.childNodes; 182 for (i = 0; i < nodeList.length; i++) { 183 var node = nodeList[i]; 184 if (isNode(node)) { 185 children.push(node); 186 } 187 } 188 }); 189 return new WindexNodes(children); 190 }; 191 192 WindexNodes.prototype._childrenMatching = function (selector) { 193 var children = []; 194 this.forEach(function (node) { 195 var matches = new WindexNodes([node], selector, node).find(selector); 196 matches.forEach(function (windexNode) { children.push(windexNode); }); 197 }); 198 return new WindexNodes(children, selector, this[0]); 199 }; 200 201 // See: http://api.jquery.com/parent/ 202 WindexNodes.prototype.parent = function (selector) { 203 if (selector) { return this._parentMatching(selector); } 204 return new WindexNodes(this.map(function (node) { return node.parentNode; })); 205 }; 206 207 WindexNodes.prototype._parentMatching = function (selector) { 208 var parents = this.map(function (node) { return node.parentNode; }). 209 filter(function (node) {return Windex.matchesSelector(node, selector);}); 210 return new WindexNodes(parents, selector, parents[0]); 211 }; 212 213 // See: http://api.jquery.com/parents/ 214 WindexNodes.prototype.parents = function (selector) { 215 if (selector) { return this._parentsMatching(selector); } 216 var parents = []; 217 this.forEach(function (node) { 218 // See: https://developer.mozilla.org/En/DOM/Node.nodeName 219 while (node.parentNode && node.parentNode.nodeName != '#document') { 220 node = node.parentNode; 221 parents.push(node); 222 } 223 }); 224 return new WindexNodes(parents); 225 }; 226 227 WindexNodes.prototype._parentsMatching = function (selector) { 228 var parents = this.parents().filter(function (i, n) { 229 return Windex.matchesSelector(n, selector); 230 }); 231 return new WindexNodes(parents, selector, parents[0]); 232 }; 233 234 // See: http://api.jquery.com/addClass/ 235 WindexNodes.prototype.addClass = function (className) { 236 var classesToAdd = className.split(" "); 237 this.forEach(function (node) { 238 var classes = (node.className) ? node.className.split(" ") : []; 239 classesToAdd.forEach(function (classToAdd) { 240 if (!classes.some(function (class) { return classToAdd == class; })) { 241 classes.push(classToAdd); 242 } 243 }); 244 node.className = classes.join(" "); 245 }); 246 return this; 247 }; 248 249 // See: http://api.jquery.com/removeClass/ 250 WindexNodes.prototype.removeClass = function (className) { 251 if (!className) { return this._removeAllClasses(); } 252 253 var classesToRemove = className.split(" "); 254 this.forEach(function (node) { 255 var classes = (node.className) ? node.className.split(" ") : []; 256 classes = classes.filter(function (class) { 257 return classesToRemove.every(function (classToRemove) { 258 return class != classToRemove; 259 }); 260 }); 261 node.className = classes.join(" "); 262 }); 263 return this; 264 }; 265 266 WindexNodes.prototype._removeAllClasses = function () { 267 this.forEach(function (node) { node.className = ''; }); 268 return this; 269 }; 270 271 // See: http://api.jquery.com/toggleClass/ 272 WindexNodes.prototype.toggleClass = function (className, add) { 273 if (add === true) { return this.addClass(className); } 274 if (add === false) { return this.removeClass(className); } 275 var classesToToggle = className.split(" "); 276 this.forEach(function (node) { 277 var classes = node.className.split(" "); 278 classesToToggle.forEach(function (classToToggle) { 279 // if node has the class, remove it 280 if (classes.some(function (class) { return class == classToToggle; })) { 281 classes = classes. 282 filter(function (class) {return class != classToToggle; }); 283 } 284 // otherwise, add it 285 else { 286 classes.push(classToToggle); 287 } 288 }); 289 node.className = classes.join(" "); 290 }); 291 return this; 292 }; 293 294 // See: http://api.jquery.com/hasClass/ 295 WindexNodes.prototype.hasClass = function (className) { 296 var classesToFind = className.split(" "); 297 return this.some(function (node) { 298 var classes = node.className.split(" "); 299 return classesToFind.every(function (class) { 300 return Windex.inArray(class, classes); 301 }); 302 }); 303 }; 304 305 // See: http://api.jquery.com/size/ 306 WindexNodes.prototype.size = function () { return this.length; }; 307 308 // See: http://api.jquery.com/empty/ 309 WindexNodes.prototype.empty = function () { 310 this.forEach(function (node) { 311 if (!node.hasChildNodes()) { return; } 312 while (node.childNodes.length >= 1){ 313 node.removeChild(node.firstChild); 314 } 315 }); 316 return this; 317 }; 318 319 // See: http://api.jquery.com/remove/ 320 // TODO: optional selector 321 WindexNodes.prototype.remove = function () { 322 this.forEach(function (node) { 323 // in case a node is already removed 324 if (!node.parentNode) { return; } 325 node.parentNode.removeChild(node); 326 }); 327 return this; 328 }; 329 330 // See: http://api.jquery.com/append/ 331 WindexNodes.prototype.append = function (arg) { 332 if (typeof arg == "string" || typeof arg == "number") { 333 return this._appendHTML(arg); 334 } 335 if (isNode(arg)) { return this._appendNode(arg); } 336 if (arg instanceof WindexNodes) { return this._appendWindexNodes(arg); } 337 throw new Error("'" + arg + "' is not a String, Node, or WindexNodes"); 338 }; 339 340 WindexNodes.prototype._appendHTML = function (html) { 341 this.forEach(function (node) { 342 var holder = safeHTMLToDOM(node.ownerDocument, html); 343 while (holder.firstChild) { 344 node.appendChild(holder.firstChild); 345 } 346 }); 347 return this; 348 }; 349 350 // See: https://developer.mozilla.org/En/DOM/Node.cloneNode 351 WindexNodes.prototype._appendNode = function (nodeToAppend) { 352 this.forEach(function (node) { 353 node.appendChild(nodeToAppend.cloneNode(true)); 354 }); 355 return this; 356 }; 357 358 // See: https://developer.mozilla.org/En/DOM/Node.cloneNode 359 WindexNodes.prototype._appendWindexNodes = function (windexNodes) { 360 this.forEach(function (node) { 361 windexNodes.forEach(function (nodeToAppend) { 362 node.appendChild(nodeToAppend.cloneNode(true)); 363 }); 364 }); 365 return this; 366 }; 367 368 // See: https://developer.mozilla.org/En/DOM/Node.cloneNode 369 WindexNodes.prototype.clone = function () { 370 var node = this[0]; 371 return node.cloneNode(true); 372 }; 373 374 // See: http://api.jquery.com/before/ 375 WindexNodes.prototype.before = function (arg) { 376 if (typeof arg == "string" || typeof arg == "number") { 377 return this._beforeHTML(arg); 378 } 379 if (isNode(arg)) { return this._beforeNode(arg); } 380 if (arg instanceof WindexNodes) { return this._beforeWindexNodes(arg); } 381 throw new Error("'" + arg + "' is not a String, Node, or WindexNodes"); 382 }; 383 384 WindexNodes.prototype._beforeHTML = function (html) { 385 this.forEach(function (node) { 386 var holder = safeHTMLToDOM(node.ownerDocument, html); 387 while (holder.firstChild) { 388 node.parentNode.insertBefore(holder.firstChild, node); 389 } 390 }); 391 return this; 392 }; 393 394 // See: https://developer.mozilla.org/En/DOM/Node.cloneNode 395 WindexNodes.prototype._beforeNode = function (nodeToAppend) { 396 this.forEach(function (node) { 397 node.parentNode.insertBefore(nodeToAppend.cloneNode(true), node); 398 }); 399 return this; 400 }; 401 402 // See: https://developer.mozilla.org/En/DOM/Node.cloneNode 403 WindexNodes.prototype._beforeWindexNodes = function (windexNodes) { 404 this.forEach(function (node) { 405 windexNodes.forEach(function (nodeToAppend) { 406 node.parentNode.insertBefore(nodeToAppend.cloneNode(true), node); 407 }); 408 }); 409 return this; 410 }; 411 412 // See: http://api.jquery.com/after/ 413 WindexNodes.prototype.after = function (arg) { 414 if (typeof arg == "string" || typeof arg == "number") { 415 return this._afterHTML(arg); 416 } 417 if (isNode(arg)) { return this._afterNode(arg); } 418 if (arg instanceof WindexNodes) { return this._afterWindexNodes(arg); } 419 throw new Error("'" + arg + "' is not a String, Node, or WindexNodes"); 420 }; 421 422 // See: http://snipplr.com/view/2107/insertafter-function-for-the-dom/ 423 var after = function (node, toAdd) { 424 if (!node.nextSibling) { return node.parentNode.append(toAdd); } 425 node.parentNode.insertBefore(toAdd, node.nextSibling); 426 }; 427 428 WindexNodes.prototype._afterHTML = function (html) { 429 this.forEach(function (node) { 430 var holder = safeHTMLToDOM(node.ownerDocument, html); 431 while (holder.lastChild) { 432 after(node, holder.lastChild); 433 } 434 }); 435 return this; 436 }; 437 438 // See: https://developer.mozilla.org/En/DOM/Node.cloneNode 439 WindexNodes.prototype._afterNode = function (nodeToAppend) { 440 this.forEach(function (node) { after(node, nodeToAppend.cloneNode(true)); }); 441 return this; 442 }; 443 444 // See: https://developer.mozilla.org/En/DOM/Node.cloneNode 445 WindexNodes.prototype._afterWindexNodes = function (windexNodes) { 446 this.forEach(function (node) { 447 windexNodes.forEach(function (nodeToAppend) { 448 after(node, nodeToAppend.cloneNode(true)); 449 }); 450 }); 451 return this; 452 }; 453 454 // See: http://api.jquery.com/html/ 455 WindexNodes.prototype.html = function (replacement) { 456 if (!replacement) { return this._htmlGet(); }; 457 return this._htmlSet(replacement); 458 }; 459 460 // FIXME: innerHTML does work on ImageDocuments in FF < 4 461 // See: https://bugzilla.mozilla.org/show_bug.cgi?id=550612 462 WindexNodes.prototype._htmlGet = function () { 463 var html = ""; 464 this.forEach(function (node) { 465 if (node.ownerDocument instanceof Components.interfaces.nsIImageDocument) { 466 throw new Error("html() does not work on ImageDocuments. See: https://bugzilla.mozilla.org/show_bug.cgi?id=550612"); 467 } 468 html += node.innerHTML; 469 }); 470 return html; 471 }; 472 473 WindexNodes.prototype._htmlSet = function (replacement) { 474 this.forEach(function (node) { Windex(node).empty().append(replacement); }); 475 return this; 476 }; 477 478 // See: http://api.jquery.com/replaceWith/ 479 WindexNodes.prototype.replaceWith = function (html) { 480 this.after(html).remove(); 481 return this; 482 }; 483 484 // See: http://api.jquery.com/css/ 485 WindexNodes.prototype.css = function (name, value) { 486 if (value === undefined && typeof name == "string") { 487 return this._cssGet(name); 488 }; 489 if (typeof name != "object") { return this._cssSet(name, value); }; 490 491 for (var key in name) { this._cssSet(key, name[key]); }; 492 return this; 493 }; 494 495 // See: https://developer.mozilla.org/en/DOM/window.getComputedStyle 496 WindexNodes.prototype._cssGet = function (name) { 497 var node = this[0]; 498 var window = node.ownerDocument.defaultView; 499 var computedStyle = window.getComputedStyle(node, null); 500 return computedStyle.getPropertyValue(name); 501 }; 502 503 WindexNodes.prototype._cssSet = function (name, value) { 504 var dims = ["height", "width", "top", "left", "right", "bottom"]; 505 if (typeof(value) == "number" && 506 dims.some(function (dim) { return name == dim; })) { 507 value = parseInt(value) + "px"; 508 } 509 var translations = { 510 "z-index": "zIndex", 511 "overflow-y": "overflowY", 512 "overflow-x": "overflowX", 513 "margin-top": "marginTop" 514 }; 515 if (translations[name]) { name = translations[name]; } 516 this.forEach(function (node) { node.style[name] = value; }); 517 return this; 518 }; 519 520 // See: http://api.jquery.com/height/ 521 // See: http://api.jquery.com/width/ 522 // TODO: convert everything to px instead of pretending em == pt == px 523 ["height", "width"].forEach(function (dim) { 524 var capitalized = dim.replace( 525 /^(.)(.+)/ , 526 function(m, p1, p2) { return p1.toUpperCase() + p2; } 527 ); 528 529 WindexNodes.prototype[dim] = function (replacement) { 530 if (!replacement) { return this["_" + dim + "Get"](); }; 531 return this["_" + dim + "Set"](replacement); 532 }; 533 534 WindexNodes.prototype["_" + dim + "Get"] = function () { 535 var node = this[0]; 536 if (!node) { return 0; } 537 if (node instanceof Components.interfaces.nsIDOMHTMLDocument) { 538 return this["_" + dim + "GetForDocument"](); 539 } 540 if (node instanceof Components.interfaces.nsIDOMWindow) { 541 return this["_" + dim + "GetForWindow"](); 542 } 543 return this["_" + dim + "GetForNode"](); 544 }; 545 546 WindexNodes.prototype["_" + dim + "GetForNode"] = function () { 547 var node = this[0]; 548 var value = parseInt(Windex(node)._cssGet(dim)); 549 return (value) ? value : 0; 550 }; 551 552 WindexNodes.prototype["_" + dim + "GetForDocument"] = function () { 553 var node = this[0]; 554 return node.documentElement["client" + capitalized]; 555 }; 556 557 WindexNodes.prototype["_" + dim + "GetForWindow"] = function () { 558 var node = this[0]; 559 return node["inner" + capitalized]; 560 }; 561 562 WindexNodes.prototype["_" + dim + "Set"] = function (replacement) { 563 this.forEach(function (node) { 564 if (node instanceof Components.interfaces.nsIDOMHTMLDocument) { 565 return setters[dim + "ForDocument"](node, replacement); 566 } 567 if (node instanceof Components.interfaces.nsIDOMWindow) { 568 return setters[dim + "ForWindow"](node, replacement); 569 } 570 setters[dim + "ForNode"](node, replacement); 571 }); 572 return this; 573 }; 574 575 // work on a node at a time 576 var setters = {}; 577 setters[dim + "ForNode"] = function (node, replacement) { 578 return Windex(node)._cssSet(dim, (replacement) ? replacement + "px" : 0); 579 }; 580 581 setters[dim + "ForDocument"] = function (node, replacement) { 582 return node[dim] = replacement; 583 }; 584 585 setters[dim + "ForWindow"] = function (node, replacement) { 586 return node["inner" + capitalized] = replacement; 587 }; 588 }); 589 590 // See: http://api.jquery.com/outerWidth/ 591 WindexNodes.prototype.outerWidth = function (includeMargin) { 592 var dims = { 593 padding: 0, 594 margin: 0, 595 border: 0 596 }; 597 var node = this; 598 for (key in dims) { 599 ["left", "right"].forEach(function (side) { 600 if (includeMargin) { 601 var attribute = parseInt(node.css(key + "-" + side + "-width")); 602 dims[key] += (isNaN(attribute)) ? 0 : attribute; 603 } 604 }); 605 } 606 var val = this[0].offsetWidth; 607 if (includeMargin) { return val + dims.margin; } 608 return val - dims.padding - dims.border; 609 }; 610 611 // See: http://api.jquery.com/outerHeight/ 612 WindexNodes.prototype.outerHeight = function (includeMargin) { 613 if (this.size() == 0) return 0; 614 var node = this; 615 var dims = { 616 padding: 0, 617 margin: 0, 618 border: 0 619 }; 620 for (key in dims) { 621 ["top", "bottom"].forEach(function (side) { 622 if (includeMargin) { 623 var attribute = parseInt(node.css(key + "-" + side + "-height")); 624 dims[key] += (isNaN(attribute)) ? 0 : attribute; 625 } 626 }); 627 } 628 var val = this[0].offsetHeight; 629 if (includeMargin) { return val + dims.margin; } 630 return val - dims.padding - dims.border; 631 }; 632 633 // See: http://api.jquery.com/trigger/ 634 // See: https://developer.mozilla.org/en/Code_snippets/Interaction_between_privileged_and_non-privileged_pages 635 WindexNodes.prototype.trigger = function (name) { 636 this.forEach(function (node) { 637 if (typeof node[name] == "function") { 638 node[name](); 639 } else { 640 var event = node.ownerDocument.createEvent("Events"); 641 event.initEvent(name, true, false); 642 node.dispatchEvent(event); 643 } 644 }); 645 return this; 646 }; 647 648 WindexNodes.prototype.triggerHandler = function (name) { 649 this.trigger(name); 650 return this; 651 }; 652 653 654 var events = []; 655 656 // See: http://api.jquery.com/bind/ 657 WindexNodes.prototype.bind = function (name, handler) { 658 this.forEach(function (node) { 659 var wrapped = function (event) { handler.apply(node, [event]); }; 660 if (!events[node]) { events[node] = {}; } 661 if (!events[node][name]) { events[node][name] = []; } 662 events[node][name].push({ wrapped: wrapped, handler: handler }); 663 node.addEventListener(name, wrapped, false, false); 664 }); 665 return this; 666 }; 667 668 // See: http://api.jquery.com/unbind 669 WindexNodes.prototype.unbind = function (name, handler) { 670 if (!name) { return this._unbindAll(); } 671 if (!handler) { return this._unbindEventAll(name); } 672 return this._unbindEvent(name, handler); 673 } 674 675 WindexNodes.prototype._unbindAll = function () { 676 this.forEach(function (node) { 677 for (name in events[node]) { 678 events[node][name].forEach(function (bridge) { 679 node.removeEventListener(name, bridge.wrapped, false); 680 }); 681 } 682 delete events[node]; 683 }); 684 return this; 685 }; 686 687 WindexNodes.prototype._unbindEventAll = function (name) { 688 this.forEach(function (node) { 689 if (!events[node] || !events[node][name]) return; 690 events[node][name].forEach(function (bridge) { 691 node.removeEventListener(name, bridge.wrapped, false); 692 }); 693 delete events[node][name]; 694 }); 695 return this; 696 }; 697 698 WindexNodes.prototype._unbindEvent = function (name, handler) { 699 this.forEach(function (node) { 700 if (!events[node] || !events[node][name]) return; 701 events[node][name] = events[node][name].filter(function (bridge) { 702 if (bridge.handler !== handler) return true; 703 node.removeEventListener(name, bridge.wrapped, false); 704 return false; 705 }); 706 }); 707 return this; 708 }; 709 710 [ 711 "click", // See: http://api.jquery.com/click/ 712 "mousedown", 713 "mouseup", 714 "mousemove", 715 "mouseenter", 716 "mouseleave", 717 "keypress", // See: http://api.jquery.com/keypress/ 718 "keydown", 719 "keyup", 720 "resize", // See: http://api.jquery.com/resize/ 721 "submit", // See: http://api.jquery.com/submit/ 722 "focus", // See: http://api.jquery.com/focus/ 723 "blur", // See: http://api.jquery.com/blur/ 724 "change", // See: http://api.jquery.com/change/ 725 "command" 726 ].forEach(function (event) { 727 WindexNodes.prototype[event] = function (handler) { 728 if (!handler) { return this.trigger(event); } 729 return this.bind(event, handler); 730 }; 731 }); 732 733 var delegates = []; 734 735 // See: http://api.jquery.com/live 736 WindexNodes.prototype.live = function (name, handler) { 737 var selector = this._selector; 738 var context = this._context; 739 var delegater = function (event) { 740 Windex(selector, context).forEach(function (node) { 741 var isTarget = (node == event.originalTarget); 742 var containsTarget = Windex.contains(node, event.originalTarget); 743 if (isTarget || containsTarget) { handler.apply(node, [event]); } 744 }); 745 }; 746 if (!delegates[context]) { delegates[context] = {}; } 747 if (!delegates[context][name]) { delegates[context][name] = []; } 748 delegates[context][name].push({ wrapped: delegater, handler: handler }); 749 context.addEventListener(name, delegater, false); 750 }; 751 752 // See: http://api.jquery.com/die 753 WindexNodes.prototype.die = function (name, handler) { 754 if (!name) { return this._dieAll(); } 755 return this._dieEvent(name, handler); 756 } 757 758 WindexNodes.prototype._dieAll = function () { 759 var node = this._context; 760 for (name in delegates[node]) { 761 delegates[node][name].forEach(function (bridge) { 762 node.removeEventListener(name, bridge.wrapped, false); 763 }); 764 } 765 delete delegates[node]; 766 return this; 767 }; 768 769 WindexNodes.prototype._dieEvent = function (name, handler) { 770 var node = this._context; 771 if (!delegates[node][name]) return; 772 delegates[node][name] = delegates[node][name].filter(function (bridge) { 773 if (bridge.handler !== handler) return true; 774 node.removeEventListener(name, bridge.wrapped, false); 775 return false; 776 }); 777 return this; 778 }; 779 780 781 // See: http://api.jquery.com/attr/ 782 WindexNodes.prototype.attr = function (name, value) { 783 if (typeof name == "object") { return this._attrSetObj(name); } 784 if (value === undefined) { return this._attrGet(name); } 785 return this._attrSet(name, value); 786 }; 787 788 WindexNodes.prototype._attrGet = function (name) { 789 var node = this[0]; 790 return node.getAttribute(name); 791 }; 792 793 WindexNodes.prototype._attrSet = function (name, value) { 794 if (name == "class") { 795 this.forEach(function (node) { node.className = value; }); 796 return this; 797 } 798 this.forEach(function (node) { 799 node.setAttribute(name, value); 800 if(name === 'checked' && (node.type === 'checkbox' || node.type === 'radio')) { 801 node.checked = value; 802 } 803 }); 804 return this; 805 }; 806 807 WindexNodes.prototype._attrSetObj = function (obj) { 808 for (key in obj) { this._attrSet(key, obj[key]); } 809 return this; 810 }; 811 812 // See: http://api.jquery.com/removeAttr/ 813 WindexNodes.prototype.removeAttr = function (name) { 814 this.forEach(function (node) { 815 node.removeAttribute(name); 816 if(name === 'checked' && node.type === 'checkbox') { 817 node.checked = false; 818 } 819 }); 820 return this; 821 }; 822 823 // See: http://api.jquery.com/val/ 824 WindexNodes.prototype.val = function (replacement) { 825 if (replacement === undefined) { return this._valGet(); } 826 return this._valSet(replacement); 827 }; 828 829 WindexNodes.prototype._valGet = function () { 830 var node = this[0]; 831 return node.value; 832 }; 833 834 WindexNodes.prototype._valSet = function (replacement) { 835 this.forEach(function (node) { node.value = replacement; }); 836 return this; 837 }; 838 839 // See: http://api.jquery.com/hide/ 840 WindexNodes.prototype.hide = function () { 841 this.forEach(function (node) { 842 if (Windex(node).css("display") == "none") return; 843 Windex(node).attr("olddisplay", Windex(node).css("display")); 844 Windex(node).css("display", "none"); 845 }); 846 return this; 847 }; 848 849 // See: http://api.jquery.com/show/ 850 WindexNodes.prototype.show = function () { 851 this.forEach(function (node) { 852 node = Windex(node); 853 if (node.css("display") != "none") return; 854 var display = (node.attr("olddisplay")) ? node.attr("olddisplay") : "block"; 855 node.css("display", display).removeAttr("olddisplay"); 856 }); 857 return this; 858 }; 859 860 WindexNodes.prototype.toggle = function () { 861 this.forEach(function (node) { 862 node = Windex(node); 863 if (node.css("display") == "none") { node.show(); } else { node.hide(); } 864 }); 865 return this; 866 }; 867 868 // See: http://api.jquery.com/text/ 869 WindexNodes.prototype.text = function (replacement) { 870 if (replacement === undefined) { return this._textGet(); } 871 return this._textSet(replacement); 872 }; 873 874 WindexNodes.prototype._textGet = function () { 875 return this.map(function (node) { return node.textContent; }).join(" "); 876 }; 877 878 WindexNodes.prototype._textSet = function (replacement) { 879 this.forEach(function (node) { node.textContent = replacement; }); 880 return this; 881 }; 882 883 // See: http://api.jquery.com/position/ 884 WindexNodes.prototype.position = function () { 885 var node = this[0]; 886 return { top: node.offsetTop, left: node.offsetLeft }; 887 }; 888 889 // See: http://api.jquery.com/scrollTop/ 890 // See: http://api.jquery.com/scrollLeft/ 891 [ 892 "Top", 893 "Left" 894 ].forEach(function (direction) { 895 var axis = (direction == "Top") ? "Y" : "X"; 896 897 WindexNodes.prototype["scroll" + direction] = function (replacement) { 898 if (!replacement) { return this["_scroll" + direction + "Get"](); } 899 return this["_scroll" + direction + "Set"](replacement); 900 }; 901 902 WindexNodes.prototype["_scroll" + direction + "Get"] = function () { 903 var node = this[0]; 904 if (node instanceof Components.interfaces.nsIDOMHTMLDocument) { 905 return this["_scroll" + direction + "GetForDocument"](); 906 } 907 if (node instanceof Components.interfaces.nsIDOMWindow) { 908 return this["_scroll" + direction + "GetForWindow"](); 909 } 910 return this["_scroll" + direction + "GetForNode"](); 911 }; 912 913 WindexNodes.prototype["_scroll" + direction + "GetForDocument"] = function() { 914 var node = this[0]; 915 return node.defaultView["scroll" + axis]; 916 }; 917 918 WindexNodes.prototype["_scroll" + direction + "GetForWindow"] = function () { 919 var node = this[0]; 920 return node["scroll" + axis]; 921 }; 922 923 WindexNodes.prototype["_scroll" + direction + "GetForNode"] = function () { 924 var node = this[0]; 925 return node["scroll" + direction]; 926 }; 927 928 var setters = { 929 document: function (n, r) { n.defaultView["scroll" + axis] = r; }, 930 window: function (n, r) { n["scroll" + axis] = r; }, 931 node: function (n, r) { n["scroll" + direction] = r; } 932 }; 933 934 WindexNodes.prototype["_scroll" + direction + "Set"] = function (replacement){ 935 this.forEach(function (node) { 936 if (node instanceof Components.interfaces.nsIDOMHTMLDocument) { 937 return setters.document(replacement); 938 } 939 if (node instanceof Components.interfaces.nsIDOMWindow) { 940 return setters.window(replacement); 941 } 942 setters.node(replacement); 943 }); 944 return this; 945 }; 946 }); 947 948 WindexNodes.prototype.first = function () { 949 var node = this[0]; 950 return new WindexNodes([node], this._selector, this._context); 951 }; 952 953 WindexNodes.prototype.last = function () { 954 var node = this[this.length - 1]; 955 return new WindexNodes([node], this._selector, this._context); 956 }; 957 958 WindexNodes.prototype.next = function (selector) { 959 if (selector) { return this._nextMatching(selector); } 960 var nodes = []; 961 this.forEach(function (node) { 962 if (!node.nextSibling) { return; } 963 node = node.nextSibling; // adjacent text node 964 if (!node.nextSibling) { return; } 965 nodes.push(node.nextSibling); 966 }); 967 return new WindexNodes(nodes, this._selector, this._context); 968 }; 969 970 WindexNodes.prototype._nextMatching = function (selector) { 971 var nodes = []; 972 this.forEach(function (node) { 973 if (!node.nextSibling) { return; } 974 node = node.nextSibling; // adjacent text node 975 if (!node.nextSibling) { return; } 976 node = node.nextSibling; 977 if (!Windex.matchesSelector(node, selector)) { return; } 978 nodes.push(node); 979 }); 980 return new WindexNodes(nodes, this._selector, this._context); 981 }; 982 983 WindexNodes.prototype.prev = function () { 984 var selector = this._selector; 985 var context = this._context; 986 var node = this[0]; 987 if (!node.previousSibling) { return new WindexNodes([], selector, context); } 988 node = node.previousSibling; // adjacent text node 989 if (!node.previousSibling) { return new WindexNodes([], selector, context); } 990 return new WindexNodes([node.previousSibling], selector, context); 991 }; 992 993 // See: http://api.jquery.com/is/ 994 WindexNodes.prototype.is = function (selector) { 995 return this.some(function (node) { 996 return Windex.matchesSelector(node, selector); 997 }); 998 }; 999 1000 // See: http://api.jquery.com/not/ 1001 WindexNodes.prototype.not = function (selector) { 1002 var remains = this.filter(function (node) { 1003 return !Windex.matchesSelector(node, selector); 1004 }); 1005 return new WindexNodes(remains, this._selector, this._context); 1006 }; 1007 1008 Windex.url = { setUrl: function (url) { return new WindexUrl(url); } }; 1009 1010 var WindexUrl = function (url) { 1011 this._url = { 1012 spec: "", 1013 host: "", 1014 path: "" 1015 }; 1016 try { 1017 var ioService = Components.classes["@mozilla.org/network/io-service;1"]. 1018 getService(Components.interfaces.nsIIOService); 1019 var nsiUri = ioService.newURI(url, null, null); 1020 for (var key in this._url) { 1021 this._url[key] = nsiUri[key]; 1022 } 1023 } catch (e) {} 1024 return this; 1025 }; 1026 1027 // See: https://developer.mozilla.org/en/nsIURI 1028 // See: https://developer.mozilla.org/en/nsIIOService#newURI.28.29 1029 WindexUrl.prototype.attr = function (name) { return this._url[name]; }; 1030 1031 Windex.browser = {};