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.

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

mercurial