Merge "ApiSandbox: Indicate when login is suppressed"
[lhc/web/wiklou.git] / resources / lib / oojs / oojs.jquery.js
index 3857f99..8cc6f11 100644 (file)
@@ -1,18 +1,18 @@
 /*!
- * OOjs v1.1.10 optimised for jQuery
+ * OOjs v2.1.0 optimised for jQuery
  * https://www.mediawiki.org/wiki/OOjs
  *
- * Copyright 2011-2015 OOjs Team and other contributors.
+ * Copyright 2011-2017 OOjs Team and other contributors.
  * Released under the MIT license
- * http://oojs.mit-license.org
+ * https://oojs.mit-license.org
  *
- * Date: 2015-11-11T16:49:11Z
+ * Date: 2017-05-30T22:56:52Z
  */
 ( function ( global ) {
 
 'use strict';
 
-/*exported toString */
+/* exported toString */
 var
        /**
         * Namespace for all classes, static methods and static properties.
@@ -22,21 +22,7 @@ var
        oo = {},
        // Optimisation: Local reference to Object.prototype.hasOwnProperty
        hasOwn = oo.hasOwnProperty,
-       toString = oo.toString,
-       // Object.create() is impossible to fully polyfill, so don't require it
-       createObject = Object.create || ( function () {
-               // Reusable constructor function
-               function Empty() {}
-               return function ( prototype, properties ) {
-                       var obj;
-                       Empty.prototype = prototype;
-                       obj = new Empty();
-                       if ( properties && hasOwn.call( properties, 'constructor' ) ) {
-                               obj.constructor = properties.constructor.value;
-                       }
-                       return obj;
-               };
-       } )();
+       toString = oo.toString;
 
 /* Class Methods */
 
@@ -92,8 +78,11 @@ oo.initClass = function ( fn ) {
 oo.inheritClass = function ( targetFn, originFn ) {
        var targetConstructor;
 
+       if ( !originFn ) {
+               throw new Error( 'inheritClass: Origin is not a function (actually ' + originFn + ')' );
+       }
        if ( targetFn.prototype instanceof originFn ) {
-               throw new Error( 'Target already inherits from origin' );
+               throw new Error( 'inheritClass: Target already inherits from origin' );
        }
 
        targetConstructor = targetFn.prototype.constructor;
@@ -102,9 +91,10 @@ oo.inheritClass = function ( targetFn, originFn ) {
        // by IE 8 and below (bug 63303).
        // Provide .parent as alias for code supporting older browsers which
        // allows people to comply with their style guide.
+       // eslint-disable-next-line dot-notation
        targetFn[ 'super' ] = targetFn.parent = originFn;
 
-       targetFn.prototype = createObject( originFn.prototype, {
+       targetFn.prototype = Object.create( originFn.prototype, {
                // Restore constructor property of targetFn
                constructor: {
                        value: targetConstructor,
@@ -116,7 +106,7 @@ oo.inheritClass = function ( targetFn, originFn ) {
 
        // Extend static properties - always initialize both sides
        oo.initClass( originFn );
-       targetFn.static = createObject( originFn.static );
+       targetFn.static = Object.create( originFn.static );
 };
 
 /**
@@ -153,6 +143,10 @@ oo.inheritClass = function ( targetFn, originFn ) {
 oo.mixinClass = function ( targetFn, originFn ) {
        var key;
 
+       if ( !originFn ) {
+               throw new Error( 'mixinClass: Origin is not a function (actually ' + originFn + ')' );
+       }
+
        // Copy prototype properties
        for ( key in originFn.prototype ) {
                if ( key !== 'constructor' && hasOwn.call( originFn.prototype, key ) ) {
@@ -173,13 +167,26 @@ oo.mixinClass = function ( targetFn, originFn ) {
        }
 };
 
+/**
+ * Test whether one class is a subclass of another, without instantiating it.
+ *
+ * Every class is considered a subclass of Object and of itself.
+ *
+ * @param {Function} testFn The class to be tested
+ * @param {Function} baseFn The base class
+ * @return {boolean} Whether testFn is a subclass of baseFn (or equal to it)
+ */
+oo.isSubclass = function ( testFn, baseFn ) {
+       return testFn === baseFn || testFn.prototype instanceof baseFn;
+};
+
 /* Object Methods */
 
 /**
  * Get a deeply nested property of an object using variadic arguments, protecting against
  * undefined property errors.
  *
- * `quux = oo.getProp( obj, 'foo', 'bar', 'baz' );` is equivalent to `quux = obj.foo.bar.baz;`
+ * `quux = OO.getProp( obj, 'foo', 'bar', 'baz' );` is equivalent to `quux = obj.foo.bar.baz;`
  * except that the former protects against JS errors if one of the intermediate properties
  * is undefined. Instead of throwing an error, this function will return undefined in
  * that case.
@@ -218,7 +225,7 @@ oo.getProp = function ( obj ) {
 oo.setProp = function ( obj ) {
        var i,
                prop = obj;
-       if ( Object( obj ) !== obj ) {
+       if ( Object( obj ) !== obj || arguments.length < 2 ) {
                return;
        }
        for ( i = 1; i < arguments.length - 2; i++ ) {
@@ -233,6 +240,34 @@ oo.setProp = function ( obj ) {
        prop[ arguments[ arguments.length - 2 ] ] = arguments[ arguments.length - 1 ];
 };
 
+/**
+ * Delete a deeply nested property of an object using variadic arguments, protecting against
+ * undefined property errors, and deleting resulting empty objects.
+ *
+ * @param {Object} obj
+ * @param {...Mixed} [keys]
+ */
+oo.deleteProp = function ( obj ) {
+       var i,
+               prop = obj,
+               props = [ prop ];
+       if ( Object( obj ) !== obj || arguments.length < 2 ) {
+               return;
+       }
+       for ( i = 1; i < arguments.length - 1; i++ ) {
+               if ( prop[ arguments[ i ] ] === undefined || Object( prop[ arguments[ i ] ] ) !== prop[ arguments[ i ] ] ) {
+                       return;
+               }
+               prop = prop[ arguments[ i ] ];
+               props.push( prop );
+       }
+       delete prop[ arguments[ i ] ];
+       // Walk back through props removing any plain empty objects
+       while ( ( prop = props.pop() ) && oo.isPlainObject( prop ) && !Object.keys( prop ).length ) {
+               delete props[ props.length - 1 ][ arguments[ props.length ] ];
+       }
+};
+
 /**
  * Create a new object that is an instance of the same
  * constructor as the input, inherits from the same object
@@ -258,7 +293,7 @@ oo.setProp = function ( obj ) {
 oo.cloneObject = function ( origin ) {
        var key, r;
 
-       r = createObject( origin.constructor.prototype );
+       r = Object.create( origin.constructor.prototype );
 
        for ( key in origin ) {
                if ( hasOwn.call( origin, key ) ) {
@@ -314,7 +349,7 @@ oo.binarySearch = function ( arr, searchFunc, forInsertion ) {
                right = arr.length;
        while ( left < right ) {
                // Equivalent to Math.floor( ( left + right ) / 2 ) but much faster
-               /*jshint bitwise:false */
+               // eslint-disable-next-line no-bitwise
                mid = ( left + right ) >> 1;
                cmpResult = searchFunc( arr[ mid ] );
                if ( cmpResult < 0 ) {
@@ -479,10 +514,9 @@ oo.getHash.keySortReplacer = function ( key, val ) {
                        normalized[ keys[ i ] ] = val[ keys[ i ] ];
                }
                return normalized;
-
-       // Primitive values and arrays get stable hashes
-       // by default. Lets those be stringified as-is.
        } else {
+               // Primitive values and arrays get stable hashes
+               // by default. Lets those be stringified as-is.
                return val;
        }
 };
@@ -592,11 +626,11 @@ oo.simpleArrayDifference = function ( a, b ) {
        return simpleArrayCombine( a, b, false );
 };
 
-/*global $ */
+/* global $ */
 
 oo.isPlainObject = $.isPlainObject;
 
-/*global hasOwn */
+/* global hasOwn */
 
 ( function () {
 
@@ -649,6 +683,24 @@ oo.isPlainObject = $.isPlainObject;
                }
        }
 
+       /**
+        * @private
+        * @param {OO.EventEmitter} ee
+        * @param {Function|string} method Function or method name
+        * @param {Object} binding
+        */
+       function addBinding( ee, event, binding ) {
+               var bindings;
+               // Auto-initialize bindings list
+               if ( hasOwn.call( ee.bindings, event ) ) {
+                       bindings = ee.bindings[ event ];
+               } else {
+                       bindings = ee.bindings[ event ] = [];
+               }
+               // Add binding
+               bindings.push( binding );
+       }
+
        /* Methods */
 
        /**
@@ -665,21 +717,14 @@ oo.isPlainObject = $.isPlainObject;
         * @chainable
         */
        oo.EventEmitter.prototype.on = function ( event, method, args, context ) {
-               var bindings;
-
                validateMethod( method, context );
 
-               if ( hasOwn.call( this.bindings, event ) ) {
-                       bindings = this.bindings[ event ];
-               } else {
-                       // Auto-initialize bindings list
-                       bindings = this.bindings[ event ] = [];
-               }
-               // Add binding
-               bindings.push( {
+               // Ensure consistent object shape (optimisation)
+               addBinding( this, event, {
                        method: method,
                        args: args,
-                       context: ( arguments.length < 4 ) ? null : context
+                       context: ( arguments.length < 4 ) ? null : context,
+                       once: false
                } );
                return this;
        };
@@ -692,12 +737,16 @@ oo.isPlainObject = $.isPlainObject;
         * @chainable
         */
        oo.EventEmitter.prototype.once = function ( event, listener ) {
-               var eventEmitter = this,
-                       wrapper = function () {
-                               eventEmitter.off( event, wrapper );
-                               return listener.apply( this, arguments );
-                       };
-               return this.on( event, wrapper );
+               validateMethod( listener );
+
+               // Ensure consistent object shape (optimisation)
+               addBinding( this, event, {
+                       method: listener,
+                       args: undefined,
+                       context: null,
+                       once: true
+               } );
+               return this;
        };
 
        /**
@@ -772,6 +821,11 @@ oo.isPlainObject = $.isPlainObject;
                                } else {
                                        method = binding.method;
                                }
+                               if ( binding.once ) {
+                                       // Must unbind before calling method to avoid
+                                       // any nested triggers.
+                                       this.off( event, method );
+                               }
                                method.apply(
                                        binding.context,
                                        binding.args ? binding.args.concat( args ) : args
@@ -789,7 +843,7 @@ oo.isPlainObject = $.isPlainObject;
         * @param {Object.<string,string>|Object.<string,Function>|Object.<string,Array>} methods List of
         *  event bindings keyed by event name containing either method names, functions or arrays containing
         *  method name or function followed by a list of arguments to be passed to callback before emitted
-        *  arguments
+        *  arguments.
         * @chainable
         */
        oo.EventEmitter.prototype.connect = function ( context, methods ) {
@@ -815,8 +869,13 @@ oo.isPlainObject = $.isPlainObject;
         *
         * @param {Object} context Object to disconnect methods from
         * @param {Object.<string,string>|Object.<string,Function>|Object.<string,Array>} [methods] List of
-        * event bindings keyed by event name. Values can be either method names or functions, but must be
-        * consistent with those used in the corresponding call to "connect".
+        *  event bindings keyed by event name. Values can be either method names, functions or arrays
+        *  containing a method name.
+        *  NOTE: To allow matching call sites with connect(), array values are allowed to contain the
+        *  parameters as well, but only the method name is used to find bindings. Tt is discouraged to
+        *  have multiple bindings for the same event to the same listener, but if used (and only the
+        *  parameters vary), disconnecting one variation of (event name, event listener, parameters)
+        *  will disconnect other variations as well.
         * @chainable
         */
        oo.EventEmitter.prototype.disconnect = function ( context, methods ) {
@@ -1075,8 +1134,10 @@ oo.isPlainObject = $.isPlainObject;
                // Remove the item from the current index
                this.items.splice( existingIndex, 1 );
 
-               // Adjust new index after removal
-               newIndex--;
+               // If necessary, adjust new index after removal
+               if ( existingIndex < newIndex ) {
+                       newIndex--;
+               }
 
                // Move the item to the new index
                this.items.splice( newIndex, 0, item );
@@ -1091,7 +1152,18 @@ oo.isPlainObject = $.isPlainObject;
         * Don't call this directly unless you know what you're doing.
         * Use #addItems instead.
         *
-        * @private
+        * This method can be extended in child classes to produce
+        * different behavior when an item is inserted. For example,
+        * inserted items may also be attached to the DOM or may
+        * interact with some other nodes in certain ways. Extending
+        * this method is allowed, but if overriden, the aggregation
+        * of events must be preserved, or behavior of emitted events
+        * will be broken.
+        *
+        * If you are extending this method, please make sure the
+        * parent method is called.
+        *
+        * @protected
         * @param {OO.EventEmitter} item Items to add
         * @param {number} index Index to add items at
         * @return {number} The index the item was added at
@@ -1297,7 +1369,7 @@ oo.SortedEmitterList.prototype.addItems = function ( items ) {
 
                // Insert item at the insertion index
                index = this.insertItem( items[ i ], insertionIndex );
-               this.emit( 'add', items[ i ], insertionIndex );
+               this.emit( 'add', items[ i ], index );
        }
 
        return this;
@@ -1325,7 +1397,7 @@ oo.SortedEmitterList.prototype.findInsertionIndex = function ( item ) {
 
 };
 
-/*global hasOwn */
+/* global hasOwn */
 
 /**
  * @class OO.Registry
@@ -1421,8 +1493,6 @@ oo.Registry.prototype.lookup = function ( name ) {
        }
 };
 
-/*global createObject */
-
 /**
  * @class OO.Factory
  * @extends OO.Registry
@@ -1431,7 +1501,7 @@ oo.Registry.prototype.lookup = function ( name ) {
  */
 oo.Factory = function OoFactory() {
        // Parent constructor
-       oo.Factory.parent.call( this );
+       oo.Factory.super.call( this );
 };
 
 /* Inheritance */
@@ -1468,7 +1538,7 @@ oo.Factory.prototype.register = function ( constructor ) {
        }
 
        // Parent method
-       oo.Factory.parent.prototype.register.call( this, name, constructor );
+       oo.Factory.super.prototype.register.call( this, name, constructor );
 };
 
 /**
@@ -1490,7 +1560,7 @@ oo.Factory.prototype.unregister = function ( constructor ) {
        }
 
        // Parent method
-       oo.Factory.parent.prototype.unregister.call( this, name );
+       oo.Factory.super.prototype.unregister.call( this, name );
 };
 
 /**
@@ -1523,12 +1593,14 @@ oo.Factory.prototype.create = function ( name ) {
        // the constructor's prototype (which also makes it an "instanceof" the constructor),
        // then invoke the constructor with the object as context, and return it (ignoring
        // the constructor's return value).
-       obj = createObject( constructor.prototype );
+       obj = Object.create( constructor.prototype );
        constructor.apply( obj, args );
        return obj;
 };
 
-/*jshint node:true */
+/* eslint-env node */
+
+/* istanbul ignore next */
 if ( typeof module !== 'undefined' && module.exports ) {
        module.exports = oo;
 } else {