browser/components/tabview/modules/utils.jsm

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 "use strict";
michael@0 6
michael@0 7 // **********
michael@0 8 // Title: utils.js
michael@0 9
michael@0 10 this.EXPORTED_SYMBOLS = ["Point", "Rect", "Range", "Subscribable", "Utils", "MRUList"];
michael@0 11
michael@0 12 // #########
michael@0 13 const Ci = Components.interfaces;
michael@0 14 const Cu = Components.utils;
michael@0 15
michael@0 16 Cu.import("resource://gre/modules/Services.jsm");
michael@0 17
michael@0 18 // ##########
michael@0 19 // Class: Point
michael@0 20 // A simple point.
michael@0 21 //
michael@0 22 // Constructor: Point
michael@0 23 // If a is a Point, creates a copy of it. Otherwise, expects a to be x,
michael@0 24 // and creates a Point with it along with y. If either a or y are omitted,
michael@0 25 // 0 is used in their place.
michael@0 26 this.Point = function Point(a, y) {
michael@0 27 if (Utils.isPoint(a)) {
michael@0 28 this.x = a.x;
michael@0 29 this.y = a.y;
michael@0 30 } else {
michael@0 31 this.x = (Utils.isNumber(a) ? a : 0);
michael@0 32 this.y = (Utils.isNumber(y) ? y : 0);
michael@0 33 }
michael@0 34 };
michael@0 35
michael@0 36 Point.prototype = {
michael@0 37 // ----------
michael@0 38 // Function: toString
michael@0 39 // Prints [Point (x,y)] for debug use
michael@0 40 toString: function Point_toString() {
michael@0 41 return "[Point (" + this.x + "," + this.y + ")]";
michael@0 42 },
michael@0 43
michael@0 44 // ----------
michael@0 45 // Function: distance
michael@0 46 // Returns the distance from this point to the given <Point>.
michael@0 47 distance: function Point_distance(point) {
michael@0 48 var ax = this.x - point.x;
michael@0 49 var ay = this.y - point.y;
michael@0 50 return Math.sqrt((ax * ax) + (ay * ay));
michael@0 51 }
michael@0 52 };
michael@0 53
michael@0 54 // ##########
michael@0 55 // Class: Rect
michael@0 56 // A simple rectangle. Note that in addition to the left and width, it also has
michael@0 57 // a right property; changing one affects the others appropriately. Same for the
michael@0 58 // vertical properties.
michael@0 59 //
michael@0 60 // Constructor: Rect
michael@0 61 // If a is a Rect, creates a copy of it. Otherwise, expects a to be left,
michael@0 62 // and creates a Rect with it along with top, width, and height.
michael@0 63 this.Rect = function Rect(a, top, width, height) {
michael@0 64 // Note: perhaps 'a' should really be called 'rectOrLeft'
michael@0 65 if (Utils.isRect(a)) {
michael@0 66 this.left = a.left;
michael@0 67 this.top = a.top;
michael@0 68 this.width = a.width;
michael@0 69 this.height = a.height;
michael@0 70 } else {
michael@0 71 this.left = a;
michael@0 72 this.top = top;
michael@0 73 this.width = width;
michael@0 74 this.height = height;
michael@0 75 }
michael@0 76 };
michael@0 77
michael@0 78 Rect.prototype = {
michael@0 79 // ----------
michael@0 80 // Function: toString
michael@0 81 // Prints [Rect (left,top,width,height)] for debug use
michael@0 82 toString: function Rect_toString() {
michael@0 83 return "[Rect (" + this.left + "," + this.top + "," +
michael@0 84 this.width + "," + this.height + ")]";
michael@0 85 },
michael@0 86
michael@0 87 get right() this.left + this.width,
michael@0 88 set right(value) {
michael@0 89 this.width = value - this.left;
michael@0 90 },
michael@0 91
michael@0 92 get bottom() this.top + this.height,
michael@0 93 set bottom(value) {
michael@0 94 this.height = value - this.top;
michael@0 95 },
michael@0 96
michael@0 97 // ----------
michael@0 98 // Variable: xRange
michael@0 99 // Gives you a new <Range> for the horizontal dimension.
michael@0 100 get xRange() new Range(this.left, this.right),
michael@0 101
michael@0 102 // ----------
michael@0 103 // Variable: yRange
michael@0 104 // Gives you a new <Range> for the vertical dimension.
michael@0 105 get yRange() new Range(this.top, this.bottom),
michael@0 106
michael@0 107 // ----------
michael@0 108 // Function: intersects
michael@0 109 // Returns true if this rectangle intersects the given <Rect>.
michael@0 110 intersects: function Rect_intersects(rect) {
michael@0 111 return (rect.right > this.left &&
michael@0 112 rect.left < this.right &&
michael@0 113 rect.bottom > this.top &&
michael@0 114 rect.top < this.bottom);
michael@0 115 },
michael@0 116
michael@0 117 // ----------
michael@0 118 // Function: intersection
michael@0 119 // Returns a new <Rect> with the intersection of this rectangle and the give <Rect>,
michael@0 120 // or null if they don't intersect.
michael@0 121 intersection: function Rect_intersection(rect) {
michael@0 122 var box = new Rect(Math.max(rect.left, this.left), Math.max(rect.top, this.top), 0, 0);
michael@0 123 box.right = Math.min(rect.right, this.right);
michael@0 124 box.bottom = Math.min(rect.bottom, this.bottom);
michael@0 125 if (box.width > 0 && box.height > 0)
michael@0 126 return box;
michael@0 127
michael@0 128 return null;
michael@0 129 },
michael@0 130
michael@0 131 // ----------
michael@0 132 // Function: contains
michael@0 133 // Returns a boolean denoting if the <Rect> or <Point> is contained inside
michael@0 134 // this rectangle.
michael@0 135 //
michael@0 136 // Parameters
michael@0 137 // - A <Rect> or a <Point>
michael@0 138 contains: function Rect_contains(a) {
michael@0 139 if (Utils.isPoint(a))
michael@0 140 return (a.x > this.left &&
michael@0 141 a.x < this.right &&
michael@0 142 a.y > this.top &&
michael@0 143 a.y < this.bottom);
michael@0 144
michael@0 145 return (a.left >= this.left &&
michael@0 146 a.right <= this.right &&
michael@0 147 a.top >= this.top &&
michael@0 148 a.bottom <= this.bottom);
michael@0 149 },
michael@0 150
michael@0 151 // ----------
michael@0 152 // Function: center
michael@0 153 // Returns a new <Point> with the center location of this rectangle.
michael@0 154 center: function Rect_center() {
michael@0 155 return new Point(this.left + (this.width / 2), this.top + (this.height / 2));
michael@0 156 },
michael@0 157
michael@0 158 // ----------
michael@0 159 // Function: size
michael@0 160 // Returns a new <Point> with the dimensions of this rectangle.
michael@0 161 size: function Rect_size() {
michael@0 162 return new Point(this.width, this.height);
michael@0 163 },
michael@0 164
michael@0 165 // ----------
michael@0 166 // Function: position
michael@0 167 // Returns a new <Point> with the top left of this rectangle.
michael@0 168 position: function Rect_position() {
michael@0 169 return new Point(this.left, this.top);
michael@0 170 },
michael@0 171
michael@0 172 // ----------
michael@0 173 // Function: area
michael@0 174 // Returns the area of this rectangle.
michael@0 175 area: function Rect_area() {
michael@0 176 return this.width * this.height;
michael@0 177 },
michael@0 178
michael@0 179 // ----------
michael@0 180 // Function: inset
michael@0 181 // Makes the rect smaller (if the arguments are positive) as if a margin is added all around
michael@0 182 // the initial rect, with the margin widths (symmetric) being specified by the arguments.
michael@0 183 //
michael@0 184 // Paramaters
michael@0 185 // - A <Point> or two arguments: x and y
michael@0 186 inset: function Rect_inset(a, b) {
michael@0 187 if (Utils.isPoint(a)) {
michael@0 188 b = a.y;
michael@0 189 a = a.x;
michael@0 190 }
michael@0 191
michael@0 192 this.left += a;
michael@0 193 this.width -= a * 2;
michael@0 194 this.top += b;
michael@0 195 this.height -= b * 2;
michael@0 196 },
michael@0 197
michael@0 198 // ----------
michael@0 199 // Function: offset
michael@0 200 // Moves (translates) the rect by the given vector.
michael@0 201 //
michael@0 202 // Paramaters
michael@0 203 // - A <Point> or two arguments: x and y
michael@0 204 offset: function Rect_offset(a, b) {
michael@0 205 if (Utils.isPoint(a)) {
michael@0 206 this.left += a.x;
michael@0 207 this.top += a.y;
michael@0 208 } else {
michael@0 209 this.left += a;
michael@0 210 this.top += b;
michael@0 211 }
michael@0 212 },
michael@0 213
michael@0 214 // ----------
michael@0 215 // Function: equals
michael@0 216 // Returns true if this rectangle is identical to the given <Rect>.
michael@0 217 equals: function Rect_equals(rect) {
michael@0 218 return (rect.left == this.left &&
michael@0 219 rect.top == this.top &&
michael@0 220 rect.width == this.width &&
michael@0 221 rect.height == this.height);
michael@0 222 },
michael@0 223
michael@0 224 // ----------
michael@0 225 // Function: union
michael@0 226 // Returns a new <Rect> with the union of this rectangle and the given <Rect>.
michael@0 227 union: function Rect_union(a) {
michael@0 228 var newLeft = Math.min(a.left, this.left);
michael@0 229 var newTop = Math.min(a.top, this.top);
michael@0 230 var newWidth = Math.max(a.right, this.right) - newLeft;
michael@0 231 var newHeight = Math.max(a.bottom, this.bottom) - newTop;
michael@0 232 var newRect = new Rect(newLeft, newTop, newWidth, newHeight);
michael@0 233
michael@0 234 return newRect;
michael@0 235 },
michael@0 236
michael@0 237 // ----------
michael@0 238 // Function: copy
michael@0 239 // Copies the values of the given <Rect> into this rectangle.
michael@0 240 copy: function Rect_copy(a) {
michael@0 241 this.left = a.left;
michael@0 242 this.top = a.top;
michael@0 243 this.width = a.width;
michael@0 244 this.height = a.height;
michael@0 245 }
michael@0 246 };
michael@0 247
michael@0 248 // ##########
michael@0 249 // Class: Range
michael@0 250 // A physical interval, with a min and max.
michael@0 251 //
michael@0 252 // Constructor: Range
michael@0 253 // Creates a Range with the given min and max
michael@0 254 this.Range = function Range(min, max) {
michael@0 255 if (Utils.isRange(min) && !max) { // if the one variable given is a range, copy it.
michael@0 256 this.min = min.min;
michael@0 257 this.max = min.max;
michael@0 258 } else {
michael@0 259 this.min = min || 0;
michael@0 260 this.max = max || 0;
michael@0 261 }
michael@0 262 };
michael@0 263
michael@0 264 Range.prototype = {
michael@0 265 // ----------
michael@0 266 // Function: toString
michael@0 267 // Prints [Range (min,max)] for debug use
michael@0 268 toString: function Range_toString() {
michael@0 269 return "[Range (" + this.min + "," + this.max + ")]";
michael@0 270 },
michael@0 271
michael@0 272 // Variable: extent
michael@0 273 // Equivalent to max-min
michael@0 274 get extent() {
michael@0 275 return (this.max - this.min);
michael@0 276 },
michael@0 277
michael@0 278 set extent(extent) {
michael@0 279 this.max = extent - this.min;
michael@0 280 },
michael@0 281
michael@0 282 // ----------
michael@0 283 // Function: contains
michael@0 284 // Whether the <Range> contains the given <Range> or value or not.
michael@0 285 //
michael@0 286 // Parameters
michael@0 287 // - a number or <Range>
michael@0 288 contains: function Range_contains(value) {
michael@0 289 if (Utils.isNumber(value))
michael@0 290 return value >= this.min && value <= this.max;
michael@0 291 if (Utils.isRange(value))
michael@0 292 return value.min >= this.min && value.max <= this.max;
michael@0 293 return false;
michael@0 294 },
michael@0 295
michael@0 296 // ----------
michael@0 297 // Function: overlaps
michael@0 298 // Whether the <Range> overlaps with the given <Range> value or not.
michael@0 299 //
michael@0 300 // Parameters
michael@0 301 // - a number or <Range>
michael@0 302 overlaps: function Range_overlaps(value) {
michael@0 303 if (Utils.isNumber(value))
michael@0 304 return this.contains(value);
michael@0 305 if (Utils.isRange(value))
michael@0 306 return !(value.max < this.min || this.max < value.min);
michael@0 307 return false;
michael@0 308 },
michael@0 309
michael@0 310 // ----------
michael@0 311 // Function: proportion
michael@0 312 // Maps the given value to the range [0,1], so that it returns 0 if the value is <= the min,
michael@0 313 // returns 1 if the value >= the max, and returns an interpolated "proportion" in (min, max).
michael@0 314 //
michael@0 315 // Parameters
michael@0 316 // - a number
michael@0 317 // - (bool) smooth? If true, a smooth tanh-based function will be used instead of the linear.
michael@0 318 proportion: function Range_proportion(value, smooth) {
michael@0 319 if (value <= this.min)
michael@0 320 return 0;
michael@0 321 if (this.max <= value)
michael@0 322 return 1;
michael@0 323
michael@0 324 var proportion = (value - this.min) / this.extent;
michael@0 325
michael@0 326 if (smooth) {
michael@0 327 // The ease function ".5+.5*Math.tanh(4*x-2)" is a pretty
michael@0 328 // little graph. It goes from near 0 at x=0 to near 1 at x=1
michael@0 329 // smoothly and beautifully.
michael@0 330 // http://www.wolframalpha.com/input/?i=.5+%2B+.5+*+tanh%28%284+*+x%29+-+2%29
michael@0 331 let tanh = function tanh(x) {
michael@0 332 var e = Math.exp(x);
michael@0 333 return (e - 1/e) / (e + 1/e);
michael@0 334 };
michael@0 335
michael@0 336 return .5 - .5 * tanh(2 - 4 * proportion);
michael@0 337 }
michael@0 338
michael@0 339 return proportion;
michael@0 340 },
michael@0 341
michael@0 342 // ----------
michael@0 343 // Function: scale
michael@0 344 // Takes the given value in [0,1] and maps it to the associated value on the Range.
michael@0 345 //
michael@0 346 // Parameters
michael@0 347 // - a number in [0,1]
michael@0 348 scale: function Range_scale(value) {
michael@0 349 if (value > 1)
michael@0 350 value = 1;
michael@0 351 if (value < 0)
michael@0 352 value = 0;
michael@0 353 return this.min + this.extent * value;
michael@0 354 }
michael@0 355 };
michael@0 356
michael@0 357 // ##########
michael@0 358 // Class: Subscribable
michael@0 359 // A mix-in for allowing objects to collect subscribers for custom events.
michael@0 360 this.Subscribable = function Subscribable() {
michael@0 361 this.subscribers = null;
michael@0 362 };
michael@0 363
michael@0 364 Subscribable.prototype = {
michael@0 365 // ----------
michael@0 366 // Function: addSubscriber
michael@0 367 // The given callback will be called when the Subscribable fires the given event.
michael@0 368 addSubscriber: function Subscribable_addSubscriber(eventName, callback) {
michael@0 369 try {
michael@0 370 Utils.assertThrow(typeof callback == "function", "callback must be a function");
michael@0 371 Utils.assertThrow(eventName && typeof eventName == "string",
michael@0 372 "eventName must be a non-empty string");
michael@0 373 } catch(e) {
michael@0 374 Utils.log(e);
michael@0 375 return;
michael@0 376 }
michael@0 377
michael@0 378 if (!this.subscribers)
michael@0 379 this.subscribers = {};
michael@0 380
michael@0 381 if (!this.subscribers[eventName])
michael@0 382 this.subscribers[eventName] = [];
michael@0 383
michael@0 384 let subscribers = this.subscribers[eventName];
michael@0 385 if (subscribers.indexOf(callback) == -1)
michael@0 386 subscribers.push(callback);
michael@0 387 },
michael@0 388
michael@0 389 // ----------
michael@0 390 // Function: removeSubscriber
michael@0 391 // Removes the subscriber associated with the event for the given callback.
michael@0 392 removeSubscriber: function Subscribable_removeSubscriber(eventName, callback) {
michael@0 393 try {
michael@0 394 Utils.assertThrow(typeof callback == "function", "callback must be a function");
michael@0 395 Utils.assertThrow(eventName && typeof eventName == "string",
michael@0 396 "eventName must be a non-empty string");
michael@0 397 } catch(e) {
michael@0 398 Utils.log(e);
michael@0 399 return;
michael@0 400 }
michael@0 401
michael@0 402 if (!this.subscribers || !this.subscribers[eventName])
michael@0 403 return;
michael@0 404
michael@0 405 let subscribers = this.subscribers[eventName];
michael@0 406 let index = subscribers.indexOf(callback);
michael@0 407
michael@0 408 if (index > -1)
michael@0 409 subscribers.splice(index, 1);
michael@0 410 },
michael@0 411
michael@0 412 // ----------
michael@0 413 // Function: _sendToSubscribers
michael@0 414 // Internal routine. Used by the Subscribable to fire events.
michael@0 415 _sendToSubscribers: function Subscribable__sendToSubscribers(eventName, eventInfo) {
michael@0 416 try {
michael@0 417 Utils.assertThrow(eventName && typeof eventName == "string",
michael@0 418 "eventName must be a non-empty string");
michael@0 419 } catch(e) {
michael@0 420 Utils.log(e);
michael@0 421 return;
michael@0 422 }
michael@0 423
michael@0 424 if (!this.subscribers || !this.subscribers[eventName])
michael@0 425 return;
michael@0 426
michael@0 427 let subsCopy = this.subscribers[eventName].concat();
michael@0 428 subsCopy.forEach(function (callback) {
michael@0 429 try {
michael@0 430 callback(this, eventInfo);
michael@0 431 } catch(e) {
michael@0 432 Utils.log(e);
michael@0 433 }
michael@0 434 }, this);
michael@0 435 }
michael@0 436 };
michael@0 437
michael@0 438 // ##########
michael@0 439 // Class: Utils
michael@0 440 // Singelton with common utility functions.
michael@0 441 this.Utils = {
michael@0 442 // ----------
michael@0 443 // Function: toString
michael@0 444 // Prints [Utils] for debug use
michael@0 445 toString: function Utils_toString() {
michael@0 446 return "[Utils]";
michael@0 447 },
michael@0 448
michael@0 449 // ___ Logging
michael@0 450 useConsole: true, // as opposed to dump
michael@0 451 showTime: false,
michael@0 452
michael@0 453 // ----------
michael@0 454 // Function: log
michael@0 455 // Prints the given arguments to the JavaScript error console as a message.
michael@0 456 // Pass as many arguments as you want, it'll print them all.
michael@0 457 log: function Utils_log() {
michael@0 458 var text = this.expandArgumentsForLog(arguments);
michael@0 459 var prefix = this.showTime ? Date.now() + ': ' : '';
michael@0 460 if (this.useConsole)
michael@0 461 Services.console.logStringMessage(prefix + text);
michael@0 462 else
michael@0 463 dump(prefix + text + '\n');
michael@0 464 },
michael@0 465
michael@0 466 // ----------
michael@0 467 // Function: error
michael@0 468 // Prints the given arguments to the JavaScript error console as an error.
michael@0 469 // Pass as many arguments as you want, it'll print them all.
michael@0 470 error: function Utils_error() {
michael@0 471 var text = this.expandArgumentsForLog(arguments);
michael@0 472 var prefix = this.showTime ? Date.now() + ': ' : '';
michael@0 473 if (this.useConsole)
michael@0 474 Cu.reportError(prefix + "tabview error: " + text);
michael@0 475 else
michael@0 476 dump(prefix + "TABVIEW ERROR: " + text + '\n');
michael@0 477 },
michael@0 478
michael@0 479 // ----------
michael@0 480 // Function: trace
michael@0 481 // Prints the given arguments to the JavaScript error console as a message,
michael@0 482 // along with a full stack trace.
michael@0 483 // Pass as many arguments as you want, it'll print them all.
michael@0 484 trace: function Utils_trace() {
michael@0 485 var text = this.expandArgumentsForLog(arguments);
michael@0 486
michael@0 487 // cut off the first line of the stack trace, because that's just this function.
michael@0 488 let stack = Error().stack.split("\n").slice(1);
michael@0 489
michael@0 490 // if the caller was assert, cut out the line for the assert function as well.
michael@0 491 if (stack[0].indexOf("Utils_assert(") == 0)
michael@0 492 stack.splice(0, 1);
michael@0 493
michael@0 494 this.log('trace: ' + text + '\n' + stack.join("\n"));
michael@0 495 },
michael@0 496
michael@0 497 // ----------
michael@0 498 // Function: assert
michael@0 499 // Prints a stack trace along with label (as a console message) if condition is false.
michael@0 500 assert: function Utils_assert(condition, label) {
michael@0 501 if (!condition) {
michael@0 502 let text;
michael@0 503 if (typeof label != 'string')
michael@0 504 text = 'badly formed assert';
michael@0 505 else
michael@0 506 text = "tabview assert: " + label;
michael@0 507
michael@0 508 this.trace(text);
michael@0 509 }
michael@0 510 },
michael@0 511
michael@0 512 // ----------
michael@0 513 // Function: assertThrow
michael@0 514 // Throws label as an exception if condition is false.
michael@0 515 assertThrow: function Utils_assertThrow(condition, label) {
michael@0 516 if (!condition) {
michael@0 517 let text;
michael@0 518 if (typeof label != 'string')
michael@0 519 text = 'badly formed assert';
michael@0 520 else
michael@0 521 text = "tabview assert: " + label;
michael@0 522
michael@0 523 // cut off the first line of the stack trace, because that's just this function.
michael@0 524 let stack = Error().stack.split("\n").slice(1);
michael@0 525
michael@0 526 throw text + "\n" + stack.join("\n");
michael@0 527 }
michael@0 528 },
michael@0 529
michael@0 530 // ----------
michael@0 531 // Function: expandObject
michael@0 532 // Prints the given object to a string, including all of its properties.
michael@0 533 expandObject: function Utils_expandObject(obj) {
michael@0 534 var s = obj + ' = {';
michael@0 535 for (let prop in obj) {
michael@0 536 let value;
michael@0 537 try {
michael@0 538 value = obj[prop];
michael@0 539 } catch(e) {
michael@0 540 value = '[!!error retrieving property]';
michael@0 541 }
michael@0 542
michael@0 543 s += prop + ': ';
michael@0 544 if (typeof value == 'string')
michael@0 545 s += '\'' + value + '\'';
michael@0 546 else if (typeof value == 'function')
michael@0 547 s += 'function';
michael@0 548 else
michael@0 549 s += value;
michael@0 550
michael@0 551 s += ', ';
michael@0 552 }
michael@0 553 return s + '}';
michael@0 554 },
michael@0 555
michael@0 556 // ----------
michael@0 557 // Function: expandArgumentsForLog
michael@0 558 // Expands all of the given args (an array) into a single string.
michael@0 559 expandArgumentsForLog: function Utils_expandArgumentsForLog(args) {
michael@0 560 var that = this;
michael@0 561 return Array.map(args, function(arg) {
michael@0 562 return typeof arg == 'object' ? that.expandObject(arg) : arg;
michael@0 563 }).join('; ');
michael@0 564 },
michael@0 565
michael@0 566 // ___ Misc
michael@0 567
michael@0 568 // ----------
michael@0 569 // Function: isLeftClick
michael@0 570 // Given a DOM mouse event, returns true if it was for the left mouse button.
michael@0 571 isLeftClick: function Utils_isLeftClick(event) {
michael@0 572 return event.button == 0;
michael@0 573 },
michael@0 574
michael@0 575 // ----------
michael@0 576 // Function: isMiddleClick
michael@0 577 // Given a DOM mouse event, returns true if it was for the middle mouse button.
michael@0 578 isMiddleClick: function Utils_isMiddleClick(event) {
michael@0 579 return event.button == 1;
michael@0 580 },
michael@0 581
michael@0 582 // ----------
michael@0 583 // Function: isRightClick
michael@0 584 // Given a DOM mouse event, returns true if it was for the right mouse button.
michael@0 585 isRightClick: function Utils_isRightClick(event) {
michael@0 586 return event.button == 2;
michael@0 587 },
michael@0 588
michael@0 589 // ----------
michael@0 590 // Function: isDOMElement
michael@0 591 // Returns true if the given object is a DOM element.
michael@0 592 isDOMElement: function Utils_isDOMElement(object) {
michael@0 593 return object instanceof Ci.nsIDOMElement;
michael@0 594 },
michael@0 595
michael@0 596 // ----------
michael@0 597 // Function: isValidXULTab
michael@0 598 // A xulTab is valid if it has not been closed,
michael@0 599 // and it has not been removed from the DOM
michael@0 600 // Returns true if the tab is valid.
michael@0 601 isValidXULTab: function Utils_isValidXULTab(xulTab) {
michael@0 602 return !xulTab.closing && xulTab.parentNode;
michael@0 603 },
michael@0 604
michael@0 605 // ----------
michael@0 606 // Function: isNumber
michael@0 607 // Returns true if the argument is a valid number.
michael@0 608 isNumber: function Utils_isNumber(n) {
michael@0 609 return typeof n == 'number' && !isNaN(n);
michael@0 610 },
michael@0 611
michael@0 612 // ----------
michael@0 613 // Function: isRect
michael@0 614 // Returns true if the given object (r) looks like a <Rect>.
michael@0 615 isRect: function Utils_isRect(r) {
michael@0 616 return (r &&
michael@0 617 this.isNumber(r.left) &&
michael@0 618 this.isNumber(r.top) &&
michael@0 619 this.isNumber(r.width) &&
michael@0 620 this.isNumber(r.height));
michael@0 621 },
michael@0 622
michael@0 623 // ----------
michael@0 624 // Function: isRange
michael@0 625 // Returns true if the given object (r) looks like a <Range>.
michael@0 626 isRange: function Utils_isRange(r) {
michael@0 627 return (r &&
michael@0 628 this.isNumber(r.min) &&
michael@0 629 this.isNumber(r.max));
michael@0 630 },
michael@0 631
michael@0 632 // ----------
michael@0 633 // Function: isPoint
michael@0 634 // Returns true if the given object (p) looks like a <Point>.
michael@0 635 isPoint: function Utils_isPoint(p) {
michael@0 636 return (p && this.isNumber(p.x) && this.isNumber(p.y));
michael@0 637 },
michael@0 638
michael@0 639 // ----------
michael@0 640 // Function: isPlainObject
michael@0 641 // Check to see if an object is a plain object (created using "{}" or "new Object").
michael@0 642 isPlainObject: function Utils_isPlainObject(obj) {
michael@0 643 // Must be an Object.
michael@0 644 // Make sure that DOM nodes and window objects don't pass through, as well
michael@0 645 if (!obj || Object.prototype.toString.call(obj) !== "[object Object]" ||
michael@0 646 obj.nodeType || obj.setInterval) {
michael@0 647 return false;
michael@0 648 }
michael@0 649
michael@0 650 // Not own constructor property must be Object
michael@0 651 const hasOwnProperty = Object.prototype.hasOwnProperty;
michael@0 652
michael@0 653 if (obj.constructor &&
michael@0 654 !hasOwnProperty.call(obj, "constructor") &&
michael@0 655 !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf")) {
michael@0 656 return false;
michael@0 657 }
michael@0 658
michael@0 659 // Own properties are enumerated firstly, so to speed up,
michael@0 660 // if last one is own, then all properties are own.
michael@0 661
michael@0 662 var key;
michael@0 663 for (key in obj) {}
michael@0 664
michael@0 665 return key === undefined || hasOwnProperty.call(obj, key);
michael@0 666 },
michael@0 667
michael@0 668 // ----------
michael@0 669 // Function: isEmptyObject
michael@0 670 // Returns true if the given object has no members.
michael@0 671 isEmptyObject: function Utils_isEmptyObject(obj) {
michael@0 672 for (let name in obj)
michael@0 673 return false;
michael@0 674 return true;
michael@0 675 },
michael@0 676
michael@0 677 // ----------
michael@0 678 // Function: copy
michael@0 679 // Returns a copy of the argument. Note that this is a shallow copy; if the argument
michael@0 680 // has properties that are themselves objects, those properties will be copied by reference.
michael@0 681 copy: function Utils_copy(value) {
michael@0 682 if (value && typeof value == 'object') {
michael@0 683 if (Array.isArray(value))
michael@0 684 return this.extend([], value);
michael@0 685 return this.extend({}, value);
michael@0 686 }
michael@0 687 return value;
michael@0 688 },
michael@0 689
michael@0 690 // ----------
michael@0 691 // Function: merge
michael@0 692 // Merge two array-like objects into the first and return it.
michael@0 693 merge: function Utils_merge(first, second) {
michael@0 694 Array.forEach(second, function(el) Array.push(first, el));
michael@0 695 return first;
michael@0 696 },
michael@0 697
michael@0 698 // ----------
michael@0 699 // Function: extend
michael@0 700 // Pass several objects in and it will combine them all into the first object and return it.
michael@0 701 extend: function Utils_extend() {
michael@0 702
michael@0 703 // copy reference to target object
michael@0 704 let target = arguments[0] || {};
michael@0 705 // Deep copy is not supported
michael@0 706 if (typeof target === "boolean") {
michael@0 707 this.assert(false, "The first argument of extend cannot be a boolean." +
michael@0 708 "Deep copy is not supported.");
michael@0 709 return target;
michael@0 710 }
michael@0 711
michael@0 712 // Back when this was in iQ + iQ.fn, so you could extend iQ objects with it.
michael@0 713 // This is no longer supported.
michael@0 714 let length = arguments.length;
michael@0 715 if (length === 1) {
michael@0 716 this.assert(false, "Extending the iQ prototype using extend is not supported.");
michael@0 717 return target;
michael@0 718 }
michael@0 719
michael@0 720 // Handle case when target is a string or something
michael@0 721 if (typeof target != "object" && typeof target != "function") {
michael@0 722 target = {};
michael@0 723 }
michael@0 724
michael@0 725 for (let i = 1; i < length; i++) {
michael@0 726 // Only deal with non-null/undefined values
michael@0 727 let options = arguments[i];
michael@0 728 if (options != null) {
michael@0 729 // Extend the base object
michael@0 730 for (let name in options) {
michael@0 731 let copy = options[name];
michael@0 732
michael@0 733 // Prevent never-ending loop
michael@0 734 if (target === copy)
michael@0 735 continue;
michael@0 736
michael@0 737 if (copy !== undefined)
michael@0 738 target[name] = copy;
michael@0 739 }
michael@0 740 }
michael@0 741 }
michael@0 742
michael@0 743 // Return the modified object
michael@0 744 return target;
michael@0 745 },
michael@0 746
michael@0 747 // ----------
michael@0 748 // Function: attempt
michael@0 749 // Tries to execute a number of functions. Returns immediately the return
michael@0 750 // value of the first non-failed function without executing successive
michael@0 751 // functions, or null.
michael@0 752 attempt: function Utils_attempt() {
michael@0 753 let args = arguments;
michael@0 754
michael@0 755 for (let i = 0; i < args.length; i++) {
michael@0 756 try {
michael@0 757 return args[i]();
michael@0 758 } catch (e) {}
michael@0 759 }
michael@0 760
michael@0 761 return null;
michael@0 762 }
michael@0 763 };
michael@0 764
michael@0 765 // ##########
michael@0 766 // Class: MRUList
michael@0 767 // A most recently used list.
michael@0 768 //
michael@0 769 // Constructor: MRUList
michael@0 770 // If a is an array of entries, creates a copy of it.
michael@0 771 this.MRUList = function MRUList(a) {
michael@0 772 if (Array.isArray(a))
michael@0 773 this._list = a.concat();
michael@0 774 else
michael@0 775 this._list = [];
michael@0 776 };
michael@0 777
michael@0 778 MRUList.prototype = {
michael@0 779 // ----------
michael@0 780 // Function: toString
michael@0 781 // Prints [List (entry1, entry2, ...)] for debug use
michael@0 782 toString: function MRUList_toString() {
michael@0 783 return "[List (" + this._list.join(", ") + ")]";
michael@0 784 },
michael@0 785
michael@0 786 // ----------
michael@0 787 // Function: update
michael@0 788 // Updates/inserts the given entry as the most recently used one in the list.
michael@0 789 update: function MRUList_update(entry) {
michael@0 790 this.remove(entry);
michael@0 791 this._list.unshift(entry);
michael@0 792 },
michael@0 793
michael@0 794 // ----------
michael@0 795 // Function: remove
michael@0 796 // Removes the given entry from the list.
michael@0 797 remove: function MRUList_remove(entry) {
michael@0 798 let index = this._list.indexOf(entry);
michael@0 799 if (index > -1)
michael@0 800 this._list.splice(index, 1);
michael@0 801 },
michael@0 802
michael@0 803 // ----------
michael@0 804 // Function: peek
michael@0 805 // Returns the most recently used entry. If a filter exists, gets the most
michael@0 806 // recently used entry which matches the filter.
michael@0 807 peek: function MRUList_peek(filter) {
michael@0 808 let match = null;
michael@0 809 if (filter && typeof filter == "function")
michael@0 810 this._list.some(function MRUList_peek_getEntry(entry) {
michael@0 811 if (filter(entry)) {
michael@0 812 match = entry
michael@0 813 return true;
michael@0 814 }
michael@0 815 return false;
michael@0 816 });
michael@0 817 else
michael@0 818 match = this._list.length > 0 ? this._list[0] : null;
michael@0 819
michael@0 820 return match;
michael@0 821 },
michael@0 822 };
michael@0 823

mercurial