Divec has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/323353

Change subject: OOjs: remove internal lib/ version and use npm instead
......................................................................

OOjs: remove internal lib/ version and use npm instead

Change-Id: I23f1e0933023ed196b32af00d887674de21c5c4b
---
M .jsduck/eg-iframe.html
M build/modules.json
M demos/ve/desktop-dist.html
M demos/ve/desktop.html
M demos/ve/minimal-rtl.html
M demos/ve/minimal.html
M demos/ve/mobile-dist.html
M demos/ve/mobile.html
D lib/oojs/AUTHORS.txt
D lib/oojs/LICENSE-MIT.txt
D lib/oojs/README.md
D lib/oojs/oojs.jquery.js
M package.json
M tests/index.html
14 files changed, 10 insertions(+), 1,692 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/VisualEditor/VisualEditor 
refs/changes/53/323353/1

diff --git a/.jsduck/eg-iframe.html b/.jsduck/eg-iframe.html
index b7fb423..3eacb11 100644
--- a/.jsduck/eg-iframe.html
+++ b/.jsduck/eg-iframe.html
@@ -33,7 +33,7 @@
                <script src="../lib/jquery/jquery.js"></script>
 
                <!-- oojs -->
-               <script src="../lib/oojs/oojs.jquery.js"></script>
+               <script src="../node_modules/oojs/dist/oojs.jquery.js"></script>
 
                <!-- oojs-ui -->
                <script src="../lib/oojs-ui/oojs-ui-core.js"></script>
diff --git a/build/modules.json b/build/modules.json
index d50d6ba..09e2b27 100644
--- a/build/modules.json
+++ b/build/modules.json
@@ -33,7 +33,7 @@
        },
        "oojs": {
                "scripts": [
-                       "lib/oojs/oojs.jquery.js"
+                       "node_modules/oojs/dist/oojs.jquery.js"
                ]
        },
        "oojs-ui": {
diff --git a/demos/ve/desktop-dist.html b/demos/ve/desktop-dist.html
index e51591e..adb08d5 100644
--- a/demos/ve/desktop-dist.html
+++ b/demos/ve/desktop-dist.html
@@ -47,7 +47,7 @@
                <script src="../../lib/jquery/jquery.js"></script>
 
                <!-- oojs -->
-               <script src="../../lib/oojs/oojs.jquery.js"></script>
+               <script 
src="../../node_modules/oojs/dist/oojs.jquery.js"></script>
 
                <!-- oojs-ui -->
                <script src="../../lib/oojs-ui/oojs-ui-core.js"></script>
diff --git a/demos/ve/desktop.html b/demos/ve/desktop.html
index 7b88b5d..a2dfd8b 100644
--- a/demos/ve/desktop.html
+++ b/demos/ve/desktop.html
@@ -102,7 +102,7 @@
                <script src="../../lib/jquery/jquery.js"></script>
 
                <!-- oojs -->
-               <script src="../../lib/oojs/oojs.jquery.js"></script>
+               <script 
src="../../node_modules/oojs/dist/oojs.jquery.js"></script>
 
                <!-- oojs-ui -->
                <script src="../../lib/oojs-ui/oojs-ui-core.js"></script>
diff --git a/demos/ve/minimal-rtl.html b/demos/ve/minimal-rtl.html
index f4d08ee..db74763 100644
--- a/demos/ve/minimal-rtl.html
+++ b/demos/ve/minimal-rtl.html
@@ -40,7 +40,7 @@
                <script src="../../lib/jquery/jquery.js"></script>
 
                <!-- oojs -->
-               <script src="../../lib/oojs/oojs.jquery.js"></script>
+               <script 
src="../../node_modules/oojs/dist/oojs.jquery.js"></script>
 
                <!-- oojs-ui -->
                <script src="../../lib/oojs-ui/oojs-ui-core.js"></script>
diff --git a/demos/ve/minimal.html b/demos/ve/minimal.html
index 2f0c730..4388b0b 100644
--- a/demos/ve/minimal.html
+++ b/demos/ve/minimal.html
@@ -40,7 +40,7 @@
                <script src="../../lib/jquery/jquery.js"></script>
 
                <!-- oojs -->
-               <script src="../../lib/oojs/oojs.jquery.js"></script>
+               <script 
src="../../node_modules/oojs/dist/oojs.jquery.js"></script>
 
                <!-- oojs-ui -->
                <script src="../../lib/oojs-ui/oojs-ui-core.js"></script>
diff --git a/demos/ve/mobile-dist.html b/demos/ve/mobile-dist.html
index db92755..50c719d 100644
--- a/demos/ve/mobile-dist.html
+++ b/demos/ve/mobile-dist.html
@@ -47,7 +47,7 @@
                <script src="../../lib/jquery/jquery.js"></script>
 
                <!-- oojs -->
-               <script src="../../lib/oojs/oojs.jquery.js"></script>
+               <script 
src="../../node_modules/oojs/dist/oojs.jquery.js"></script>
 
                <!-- oojs-ui -->
                <script src="../../lib/oojs-ui/oojs-ui-core.js"></script>
diff --git a/demos/ve/mobile.html b/demos/ve/mobile.html
index 4168a23..8e0af33 100644
--- a/demos/ve/mobile.html
+++ b/demos/ve/mobile.html
@@ -100,7 +100,7 @@
                <script src="../../lib/jquery/jquery.js"></script>
 
                <!-- oojs -->
-               <script src="../../lib/oojs/oojs.jquery.js"></script>
+               <script 
src="../../node_modules/oojs/dist/oojs.jquery.js"></script>
 
                <!-- oojs-ui -->
                <script src="../../lib/oojs-ui/oojs-ui-core.js"></script>
diff --git a/lib/oojs/AUTHORS.txt b/lib/oojs/AUTHORS.txt
deleted file mode 100644
index f5bce10..0000000
--- a/lib/oojs/AUTHORS.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-Bartosz Dziewoński <matma...@wikimedia.org>
-David Chan <dc...@wikimedia.org>
-Ed Sanders <ejsand...@gmail.com>
-James D. Forrester <jforres...@wikimedia.org>
-Ori Livneh <o...@wikimedia.org>
-Roan Kattouw <roan.katt...@gmail.com>
-Timo Tijhof <krinklem...@gmail.com>
-Trevor Parscal <trevorpars...@gmail.com>
diff --git a/lib/oojs/LICENSE-MIT.txt b/lib/oojs/LICENSE-MIT.txt
deleted file mode 100644
index 1eef012..0000000
--- a/lib/oojs/LICENSE-MIT.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright 2011-2015 OOjs Team and other contributors.
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/lib/oojs/README.md b/lib/oojs/README.md
deleted file mode 100644
index df6b2f9..0000000
--- a/lib/oojs/README.md
+++ /dev/null
@@ -1,117 +0,0 @@
-[![npm](https://img.shields.io/npm/v/oojs.svg?style=flat)](https://www.npmjs.com/package/oojs)
 
[![David](https://img.shields.io/david/dev/wikimedia/oojs.svg?style=flat)](https://david-dm.org/wikimedia/oojs#info=devDependencies)
-
-OOjs
-=================
-
-OOjs is a JavaScript library for working with objects.
-
-Key features include inheritance, mixins and utilities for working with 
objects.
-
-<pre lang="javascript">
-/* Example */
-( function ( oo ) {
-    function Animal() {}
-    function Magic() {}
-    function Unicorn() {
-        Animal.call( this );
-        Magic.call( this );
-    }
-    oo.inheritClass( Unicorn, Animal );
-    oo.mixinClass( Unicorn, Magic );
-}( OO ) );
-</pre>
-
-Quick start
-----------
-
-This library is available as an [npm](https://npmjs.org/) package! Install it 
right away:
-<pre lang="bash">
-npm install oojs
-</pre>
-
-Or clone the repo, `git clone https://git.wikimedia.org/git/oojs/core.git`.
-
-ECMAScript 5
-----------
-
-OOjs is optimised for modern ECMAScript 5 environments. However, care has been 
taken to maintain
-parser compatibility with ES3 engines (such as for IE 6-8).
-
-No ES5 syntax or unpolyfillable features are used. To support ES3 engines, 
ensure an appropriate
-polyfill is loaded before OOjs.
-
-These are the methods used:
-
-* Array.isArray
-* Object.keys
-* JSON.stringify
-
-Additionally, Object.create is used if available. As it is impossible to fully 
polyfill, OOjs
-includes a fallback implementing the minimal set of required features.
-
-While there are no plans to ship a polyfill for remaining methods, we 
recommend the following and
-use them ourselves in the unit tests to assert support for older browsers.
-
-* [json2.js](https://github.com/douglascrockford/JSON-js) (only for IE6/IE7)
-* [es5-shim.js](https://github.com/es-shims/es5-shim)
-
-jQuery
-----------
-
-If your project uses jQuery, use the optimised `oojs.jquery.js` build instead.
-
-This build assumes jQuery is present and omits various chunks of code in 
favour of references to jQuery.
-
-jQuery 1.8.3 or higher is recommended.
-
-Versioning
-----------
-
-We use the Semantic Versioning guidelines as much as possible.
-
-Releases will be numbered in the following format:
-
-`<major>.<minor>.<patch>`
-
-For more information on SemVer, please visit http://semver.org/.
-
-Bug tracker
------------
-
-Found a bug? Please report it in the [issue 
tracker](https://phabricator.wikimedia.org/maniphest/task/create/?projects=OOjs)!
-
-Release
-----------
-
-Release process:
-<pre lang="bash">
-$ cd path/to/oojs/
-$ git remote update
-$ git checkout -B release -t origin/master
-
-# Ensure tests pass
-$ npm install && npm test
-
-# Avoid using "npm version patch" because that creates
-# both a commit and a tag, and we shouldn't tag until after
-# the commit is merged.
-
-# Update release notes
-# Copy the resulting list into a new section on History.md
-$ git log --format='* %s (%aN)' --no-merges --reverse v$(node -e 
'console.log(require("./package.json").version);')...HEAD
-$ edit History.md
-
-# Update the version number
-$ edit package.json
-
-$ git add -p
-$ git commit -m "Tag vX.X.X"
-$ git review
-
-# After merging:
-$ git remote update
-$ git checkout origin/master
-$ git tag "vX.X.X"
-$ git push --tags
-$ npm publish
-</pre>
diff --git a/lib/oojs/oojs.jquery.js b/lib/oojs/oojs.jquery.js
deleted file mode 100644
index 3857f99..0000000
--- a/lib/oojs/oojs.jquery.js
+++ /dev/null
@@ -1,1538 +0,0 @@
-/*!
- * OOjs v1.1.10 optimised for jQuery
- * https://www.mediawiki.org/wiki/OOjs
- *
- * Copyright 2011-2015 OOjs Team and other contributors.
- * Released under the MIT license
- * http://oojs.mit-license.org
- *
- * Date: 2015-11-11T16:49:11Z
- */
-( function ( global ) {
-
-'use strict';
-
-/*exported toString */
-var
-       /**
-        * Namespace for all classes, static methods and static properties.
-        * @class OO
-        * @singleton
-        */
-       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;
-               };
-       } )();
-
-/* Class Methods */
-
-/**
- * Utility to initialize a class for OO inheritance.
- *
- * Currently this just initializes an empty static object.
- *
- * @param {Function} fn
- */
-oo.initClass = function ( fn ) {
-       fn.static = fn.static || {};
-};
-
-/**
- * Inherit from prototype to another using Object#create.
- *
- * Beware: This redefines the prototype, call before setting your prototypes.
- *
- * Beware: This redefines the prototype, can only be called once on a function.
- * If called multiple times on the same function, the previous prototype is 
lost.
- * This is how prototypal inheritance works, it can only be one straight chain
- * (just like classical inheritance in PHP for example). If you need to work 
with
- * multiple constructors consider storing an instance of the other constructor 
in a
- * property instead, or perhaps use a mixin (see OO.mixinClass).
- *
- *     function Thing() {}
- *     Thing.prototype.exists = function () {};
- *
- *     function Person() {
- *         Person.super.apply( this, arguments );
- *     }
- *     OO.inheritClass( Person, Thing );
- *     Person.static.defaultEyeCount = 2;
- *     Person.prototype.walk = function () {};
- *
- *     function Jumper() {
- *         Jumper.super.apply( this, arguments );
- *     }
- *     OO.inheritClass( Jumper, Person );
- *     Jumper.prototype.jump = function () {};
- *
- *     Jumper.static.defaultEyeCount === 2;
- *     var x = new Jumper();
- *     x.jump();
- *     x.walk();
- *     x instanceof Thing && x instanceof Person && x instanceof Jumper;
- *
- * @param {Function} targetFn
- * @param {Function} originFn
- * @throws {Error} If target already inherits from origin
- */
-oo.inheritClass = function ( targetFn, originFn ) {
-       var targetConstructor;
-
-       if ( targetFn.prototype instanceof originFn ) {
-               throw new Error( 'Target already inherits from origin' );
-       }
-
-       targetConstructor = targetFn.prototype.constructor;
-
-       // Using ['super'] instead of .super because 'super' is not supported
-       // 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.
-       targetFn[ 'super' ] = targetFn.parent = originFn;
-
-       targetFn.prototype = createObject( originFn.prototype, {
-               // Restore constructor property of targetFn
-               constructor: {
-                       value: targetConstructor,
-                       enumerable: false,
-                       writable: true,
-                       configurable: true
-               }
-       } );
-
-       // Extend static properties - always initialize both sides
-       oo.initClass( originFn );
-       targetFn.static = createObject( originFn.static );
-};
-
-/**
- * Copy over *own* prototype properties of a mixin.
- *
- * The 'constructor' (whether implicit or explicit) is not copied over.
- *
- * This does not create inheritance to the origin. If you need inheritance,
- * use OO.inheritClass instead.
- *
- * Beware: This can redefine a prototype property, call before setting your 
prototypes.
- *
- * Beware: Don't call before OO.inheritClass.
- *
- *     function Foo() {}
- *     function Context() {}
- *
- *     // Avoid repeating this code
- *     function ContextLazyLoad() {}
- *     ContextLazyLoad.prototype.getContext = function () {
- *         if ( !this.context ) {
- *             this.context = new Context();
- *         }
- *         return this.context;
- *     };
- *
- *     function FooBar() {}
- *     OO.inheritClass( FooBar, Foo );
- *     OO.mixinClass( FooBar, ContextLazyLoad );
- *
- * @param {Function} targetFn
- * @param {Function} originFn
- */
-oo.mixinClass = function ( targetFn, originFn ) {
-       var key;
-
-       // Copy prototype properties
-       for ( key in originFn.prototype ) {
-               if ( key !== 'constructor' && hasOwn.call( originFn.prototype, 
key ) ) {
-                       targetFn.prototype[ key ] = originFn.prototype[ key ];
-               }
-       }
-
-       // Copy static properties - always initialize both sides
-       oo.initClass( targetFn );
-       if ( originFn.static ) {
-               for ( key in originFn.static ) {
-                       if ( hasOwn.call( originFn.static, key ) ) {
-                               targetFn.static[ key ] = originFn.static[ key ];
-                       }
-               }
-       } else {
-               oo.initClass( originFn );
-       }
-};
-
-/* 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;`
- * 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.
- *
- * @param {Object} obj
- * @param {...Mixed} [keys]
- * @return {Object|undefined} obj[arguments[1]][arguments[2]].... or undefined
- */
-oo.getProp = function ( obj ) {
-       var i,
-               retval = obj;
-       for ( i = 1; i < arguments.length; i++ ) {
-               if ( retval === undefined || retval === null ) {
-                       // Trying to access a property of undefined or null 
causes an error
-                       return undefined;
-               }
-               retval = retval[ arguments[ i ] ];
-       }
-       return retval;
-};
-
-/**
- * Set a deeply nested property of an object using variadic arguments, 
protecting against
- * undefined property errors.
- *
- * `oo.setProp( obj, 'foo', 'bar', 'baz' );` is equivalent to `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, undefined intermediate properties 
will be
- * initialized to an empty object. If an intermediate property is not an 
object, or if obj itself
- * is not an object, this function will silently abort.
- *
- * @param {Object} obj
- * @param {...Mixed} [keys]
- * @param {Mixed} [value]
- */
-oo.setProp = function ( obj ) {
-       var i,
-               prop = obj;
-       if ( Object( obj ) !== obj ) {
-               return;
-       }
-       for ( i = 1; i < arguments.length - 2; i++ ) {
-               if ( prop[ arguments[ i ] ] === undefined ) {
-                       prop[ arguments[ i ] ] = {};
-               }
-               if ( Object( prop[ arguments[ i ] ] ) !== prop[ arguments[ i ] 
] ) {
-                       return;
-               }
-               prop = prop[ arguments[ i ] ];
-       }
-       prop[ arguments[ arguments.length - 2 ] ] = arguments[ arguments.length 
- 1 ];
-};
-
-/**
- * Create a new object that is an instance of the same
- * constructor as the input, inherits from the same object
- * and contains the same own properties.
- *
- * This makes a shallow non-recursive copy of own properties.
- * To create a recursive copy of plain objects, use #copy.
- *
- *     var foo = new Person( mom, dad );
- *     foo.setAge( 21 );
- *     var foo2 = OO.cloneObject( foo );
- *     foo.setAge( 22 );
- *
- *     // Then
- *     foo2 !== foo; // true
- *     foo2 instanceof Person; // true
- *     foo2.getAge(); // 21
- *     foo.getAge(); // 22
- *
- * @param {Object} origin
- * @return {Object} Clone of origin
- */
-oo.cloneObject = function ( origin ) {
-       var key, r;
-
-       r = createObject( origin.constructor.prototype );
-
-       for ( key in origin ) {
-               if ( hasOwn.call( origin, key ) ) {
-                       r[ key ] = origin[ key ];
-               }
-       }
-
-       return r;
-};
-
-/**
- * Get an array of all property values in an object.
- *
- * @param {Object} obj Object to get values from
- * @return {Array} List of object values
- */
-oo.getObjectValues = function ( obj ) {
-       var key, values;
-
-       if ( obj !== Object( obj ) ) {
-               throw new TypeError( 'Called on non-object' );
-       }
-
-       values = [];
-       for ( key in obj ) {
-               if ( hasOwn.call( obj, key ) ) {
-                       values[ values.length ] = obj[ key ];
-               }
-       }
-
-       return values;
-};
-
-/**
- * Use binary search to locate an element in a sorted array.
- *
- * searchFunc is given an element from the array. `searchFunc(elem)` must 
return a number
- * above 0 if the element we're searching for is to the right of (has a higher 
index than) elem,
- * below 0 if it is to the left of elem, or zero if it's equal to elem.
- *
- * To search for a specific value with a comparator function (a `function 
cmp(a,b)` that returns
- * above 0 if `a > b`, below 0 if `a < b`, and 0 if `a == b`), you can use
- * `searchFunc = cmp.bind( null, value )`.
- *
- * @param {Array} arr Array to search in
- * @param {Function} searchFunc Search function
- * @param {boolean} [forInsertion] If not found, return index where val could 
be inserted
- * @return {number|null} Index where val was found, or null if not found
- */
-oo.binarySearch = function ( arr, searchFunc, forInsertion ) {
-       var mid, cmpResult,
-               left = 0,
-               right = arr.length;
-       while ( left < right ) {
-               // Equivalent to Math.floor( ( left + right ) / 2 ) but much 
faster
-               /*jshint bitwise:false */
-               mid = ( left + right ) >> 1;
-               cmpResult = searchFunc( arr[ mid ] );
-               if ( cmpResult < 0 ) {
-                       right = mid;
-               } else if ( cmpResult > 0 ) {
-                       left = mid + 1;
-               } else {
-                       return mid;
-               }
-       }
-       return forInsertion ? right : null;
-};
-
-/**
- * Recursively compare properties between two objects.
- *
- * A false result may be caused by property inequality or by properties in one 
object missing from
- * the other. An asymmetrical test may also be performed, which checks only 
that properties in the
- * first object are present in the second object, but not the inverse.
- *
- * If either a or b is null or undefined it will be treated as an empty object.
- *
- * @param {Object|undefined|null} a First object to compare
- * @param {Object|undefined|null} b Second object to compare
- * @param {boolean} [asymmetrical] Whether to check only that a's values are 
equal to b's
- *  (i.e. a is a subset of b)
- * @return {boolean} If the objects contain the same values as each other
- */
-oo.compare = function ( a, b, asymmetrical ) {
-       var aValue, bValue, aType, bType, k;
-
-       if ( a === b ) {
-               return true;
-       }
-
-       a = a || {};
-       b = b || {};
-
-       if ( typeof a.nodeType === 'number' && typeof a.isEqualNode === 
'function' ) {
-               return a.isEqualNode( b );
-       }
-
-       for ( k in a ) {
-               if ( !hasOwn.call( a, k ) || a[ k ] === undefined || a[ k ] === 
b[ k ] ) {
-                       // Support es3-shim: Without the hasOwn filter, 
comparing [] to {} will be false in ES3
-                       // because the shimmed "forEach" is enumerable and 
shows up in Array but not Object.
-                       // Also ignore undefined values, because there is no 
conceptual difference between
-                       // a key that is absent and a key that is present but 
whose value is undefined.
-                       continue;
-               }
-
-               aValue = a[ k ];
-               bValue = b[ k ];
-               aType = typeof aValue;
-               bType = typeof bValue;
-               if ( aType !== bType ||
-                       (
-                               ( aType === 'string' || aType === 'number' || 
aType === 'boolean' ) &&
-                               aValue !== bValue
-                       ) ||
-                       ( aValue === Object( aValue ) && !oo.compare( aValue, 
bValue, true ) ) ) {
-                       return false;
-               }
-       }
-       // If the check is not asymmetrical, recursing with the arguments 
swapped will verify our result
-       return asymmetrical ? true : oo.compare( b, a, true );
-};
-
-/**
- * Create a plain deep copy of any kind of object.
- *
- * Copies are deep, and will either be an object or an array depending on 
`source`.
- *
- * @param {Object} source Object to copy
- * @param {Function} [leafCallback] Applied to leaf values after they are 
cloned but before they are added to the clone
- * @param {Function} [nodeCallback] Applied to all values before they are 
cloned.  If the nodeCallback returns a value other than undefined, the returned 
value is used instead of attempting to clone.
- * @return {Object} Copy of source object
- */
-oo.copy = function ( source, leafCallback, nodeCallback ) {
-       var key, destination;
-
-       if ( nodeCallback ) {
-               // Extensibility: check before attempting to clone source.
-               destination = nodeCallback( source );
-               if ( destination !== undefined ) {
-                       return destination;
-               }
-       }
-
-       if ( Array.isArray( source ) ) {
-               // Array (fall through)
-               destination = new Array( source.length );
-       } else if ( source && typeof source.clone === 'function' ) {
-               // Duck type object with custom clone method
-               return leafCallback ? leafCallback( source.clone() ) : 
source.clone();
-       } else if ( source && typeof source.cloneNode === 'function' ) {
-               // DOM Node
-               return leafCallback ?
-                       leafCallback( source.cloneNode( true ) ) :
-                       source.cloneNode( true );
-       } else if ( oo.isPlainObject( source ) ) {
-               // Plain objects (fall through)
-               destination = {};
-       } else {
-               // Non-plain objects (incl. functions) and primitive values
-               return leafCallback ? leafCallback( source ) : source;
-       }
-
-       // source is an array or a plain object
-       for ( key in source ) {
-               destination[ key ] = oo.copy( source[ key ], leafCallback, 
nodeCallback );
-       }
-
-       // This is an internal node, so we don't apply the leafCallback.
-       return destination;
-};
-
-/**
- * Generate a hash of an object based on its name and data.
- *
- * Performance optimization: 
<http://jsperf.com/ve-gethash-201208#/toJson_fnReplacerIfAoForElse>
- *
- * To avoid two objects with the same values generating different hashes, we 
utilize the replacer
- * argument of JSON.stringify and sort the object by key as it's being 
serialized. This may or may
- * not be the fastest way to do this; we should investigate this further.
- *
- * Objects and arrays are hashed recursively. When hashing an object that has 
a .getHash()
- * function, we call that function and use its return value rather than 
hashing the object
- * ourselves. This allows classes to define custom hashing.
- *
- * @param {Object} val Object to generate hash for
- * @return {string} Hash of object
- */
-oo.getHash = function ( val ) {
-       return JSON.stringify( val, oo.getHash.keySortReplacer );
-};
-
-/**
- * Sort objects by key (helper function for OO.getHash).
- *
- * This is a callback passed into JSON.stringify.
- *
- * @method getHash_keySortReplacer
- * @param {string} key Property name of value being replaced
- * @param {Mixed} val Property value to replace
- * @return {Mixed} Replacement value
- */
-oo.getHash.keySortReplacer = function ( key, val ) {
-       var normalized, keys, i, len;
-       if ( val && typeof val.getHashObject === 'function' ) {
-               // This object has its own custom hash function, use it
-               val = val.getHashObject();
-       }
-       if ( !Array.isArray( val ) && Object( val ) === val ) {
-               // Only normalize objects when the key-order is ambiguous
-               // (e.g. any object not an array).
-               normalized = {};
-               keys = Object.keys( val ).sort();
-               i = 0;
-               len = keys.length;
-               for ( ; i < len; i += 1 ) {
-                       normalized[ keys[ i ] ] = val[ keys[ i ] ];
-               }
-               return normalized;
-
-       // Primitive values and arrays get stable hashes
-       // by default. Lets those be stringified as-is.
-       } else {
-               return val;
-       }
-};
-
-/**
- * Get the unique values of an array, removing duplicates
- *
- * @param {Array} arr Array
- * @return {Array} Unique values in array
- */
-oo.unique = function ( arr ) {
-       return arr.reduce( function ( result, current ) {
-               if ( result.indexOf( current ) === -1 ) {
-                       result.push( current );
-               }
-               return result;
-       }, [] );
-};
-
-/**
- * Compute the union (duplicate-free merge) of a set of arrays.
- *
- * Arrays values must be convertable to object keys (strings).
- *
- * By building an object (with the values for keys) in parallel with
- * the array, a new item's existence in the union can be computed faster.
- *
- * @param {...Array} arrays Arrays to union
- * @return {Array} Union of the arrays
- */
-oo.simpleArrayUnion = function () {
-       var i, ilen, arr, j, jlen,
-               obj = {},
-               result = [];
-
-       for ( i = 0, ilen = arguments.length; i < ilen; i++ ) {
-               arr = arguments[ i ];
-               for ( j = 0, jlen = arr.length; j < jlen; j++ ) {
-                       if ( !obj[ arr[ j ] ] ) {
-                               obj[ arr[ j ] ] = true;
-                               result.push( arr[ j ] );
-                       }
-               }
-       }
-
-       return result;
-};
-
-/**
- * Combine arrays (intersection or difference).
- *
- * An intersection checks the item exists in 'b' while difference checks it 
doesn't.
- *
- * Arrays values must be convertable to object keys (strings).
- *
- * By building an object (with the values for keys) of 'b' we can
- * compute the result faster.
- *
- * @private
- * @param {Array} a First array
- * @param {Array} b Second array
- * @param {boolean} includeB Whether to items in 'b'
- * @return {Array} Combination (intersection or difference) of arrays
- */
-function simpleArrayCombine( a, b, includeB ) {
-       var i, ilen, isInB,
-               bObj = {},
-               result = [];
-
-       for ( i = 0, ilen = b.length; i < ilen; i++ ) {
-               bObj[ b[ i ] ] = true;
-       }
-
-       for ( i = 0, ilen = a.length; i < ilen; i++ ) {
-               isInB = !!bObj[ a[ i ] ];
-               if ( isInB === includeB ) {
-                       result.push( a[ i ] );
-               }
-       }
-
-       return result;
-}
-
-/**
- * Compute the intersection of two arrays (items in both arrays).
- *
- * Arrays values must be convertable to object keys (strings).
- *
- * @param {Array} a First array
- * @param {Array} b Second array
- * @return {Array} Intersection of arrays
- */
-oo.simpleArrayIntersection = function ( a, b ) {
-       return simpleArrayCombine( a, b, true );
-};
-
-/**
- * Compute the difference of two arrays (items in 'a' but not 'b').
- *
- * Arrays values must be convertable to object keys (strings).
- *
- * @param {Array} a First array
- * @param {Array} b Second array
- * @return {Array} Intersection of arrays
- */
-oo.simpleArrayDifference = function ( a, b ) {
-       return simpleArrayCombine( a, b, false );
-};
-
-/*global $ */
-
-oo.isPlainObject = $.isPlainObject;
-
-/*global hasOwn */
-
-( function () {
-
-       /**
-        * @class OO.EventEmitter
-        *
-        * @constructor
-        */
-       oo.EventEmitter = function OoEventEmitter() {
-               // Properties
-
-               /**
-                * Storage of bound event handlers by event name.
-                *
-                * @property
-                */
-               this.bindings = {};
-       };
-
-       oo.initClass( oo.EventEmitter );
-
-       /* Private helper functions */
-
-       /**
-        * Validate a function or method call in a context
-        *
-        * For a method name, check that it names a function in the context 
object
-        *
-        * @private
-        * @param {Function|string} method Function or method name
-        * @param {Mixed} context The context of the call
-        * @throws {Error} A method name is given but there is no context
-        * @throws {Error} In the context object, no property exists with the 
given name
-        * @throws {Error} In the context object, the named property is not a 
function
-        */
-       function validateMethod( method, context ) {
-               // Validate method and context
-               if ( typeof method === 'string' ) {
-                       // Validate method
-                       if ( context === undefined || context === null ) {
-                               throw new Error( 'Method name "' + method + '" 
has no context.' );
-                       }
-                       if ( typeof context[ method ] !== 'function' ) {
-                               // Technically the property could be replaced 
by a function before
-                               // call time. But this probably signals a typo.
-                               throw new Error( 'Property "' + method + '" is 
not a function' );
-                       }
-               } else if ( typeof method !== 'function' ) {
-                       throw new Error( 'Invalid callback. Function or method 
name expected.' );
-               }
-       }
-
-       /* Methods */
-
-       /**
-        * Add a listener to events of a specific event.
-        *
-        * The listener can be a function or the string name of a method; if 
the latter, then the
-        * name lookup happens at the time the listener is called.
-        *
-        * @param {string} event Type of event to listen to
-        * @param {Function|string} method Function or method name to call when 
event occurs
-        * @param {Array} [args] Arguments to pass to listener, will be 
prepended to emitted arguments
-        * @param {Object} [context=null] Context object for function or method 
call
-        * @throws {Error} Listener argument is not a function or a valid 
method name
-        * @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( {
-                       method: method,
-                       args: args,
-                       context: ( arguments.length < 4 ) ? null : context
-               } );
-               return this;
-       };
-
-       /**
-        * Add a one-time listener to a specific event.
-        *
-        * @param {string} event Type of event to listen to
-        * @param {Function} listener Listener to call when event occurs
-        * @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 );
-       };
-
-       /**
-        * Remove a specific listener from a specific event.
-        *
-        * @param {string} event Type of event to remove listener from
-        * @param {Function|string} [method] Listener to remove. Must be in the 
same form as was passed
-        * to "on". Omit to remove all listeners.
-        * @param {Object} [context=null] Context object function or method call
-        * @chainable
-        * @throws {Error} Listener argument is not a function or a valid 
method name
-        */
-       oo.EventEmitter.prototype.off = function ( event, method, context ) {
-               var i, bindings;
-
-               if ( arguments.length === 1 ) {
-                       // Remove all bindings for event
-                       delete this.bindings[ event ];
-                       return this;
-               }
-
-               validateMethod( method, context );
-
-               if ( !hasOwn.call( this.bindings, event ) || !this.bindings[ 
event ].length ) {
-                       // No matching bindings
-                       return this;
-               }
-
-               // Default to null context
-               if ( arguments.length < 3 ) {
-                       context = null;
-               }
-
-               // Remove matching handlers
-               bindings = this.bindings[ event ];
-               i = bindings.length;
-               while ( i-- ) {
-                       if ( bindings[ i ].method === method && bindings[ i 
].context === context ) {
-                               bindings.splice( i, 1 );
-                       }
-               }
-
-               // Cleanup if now empty
-               if ( bindings.length === 0 ) {
-                       delete this.bindings[ event ];
-               }
-               return this;
-       };
-
-       /**
-        * Emit an event.
-        *
-        * @param {string} event Type of event
-        * @param {...Mixed} args First in a list of variadic arguments passed 
to event handler (optional)
-        * @return {boolean} Whether the event was handled by at least one 
listener
-        */
-       oo.EventEmitter.prototype.emit = function ( event ) {
-               var args = [],
-                       i, len, binding, bindings, method;
-
-               if ( hasOwn.call( this.bindings, event ) ) {
-                       // Slicing ensures that we don't get tripped up by 
event handlers that add/remove bindings
-                       bindings = this.bindings[ event ].slice();
-                       for ( i = 1, len = arguments.length; i < len; i++ ) {
-                               args.push( arguments[ i ] );
-                       }
-                       for ( i = 0, len = bindings.length; i < len; i++ ) {
-                               binding = bindings[ i ];
-                               if ( typeof binding.method === 'string' ) {
-                                       // Lookup method by name (late binding)
-                                       method = binding.context[ 
binding.method ];
-                               } else {
-                                       method = binding.method;
-                               }
-                               method.apply(
-                                       binding.context,
-                                       binding.args ? binding.args.concat( 
args ) : args
-                               );
-                       }
-                       return true;
-               }
-               return false;
-       };
-
-       /**
-        * Connect event handlers to an object.
-        *
-        * @param {Object} context Object to call methods on when events occur
-        * @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
-        * @chainable
-        */
-       oo.EventEmitter.prototype.connect = function ( context, methods ) {
-               var method, args, event;
-
-               for ( event in methods ) {
-                       method = methods[ event ];
-                       // Allow providing additional args
-                       if ( Array.isArray( method ) ) {
-                               args = method.slice( 1 );
-                               method = method[ 0 ];
-                       } else {
-                               args = [];
-                       }
-                       // Add binding
-                       this.on( event, method, args, context );
-               }
-               return this;
-       };
-
-       /**
-        * Disconnect event handlers from an object.
-        *
-        * @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".
-        * @chainable
-        */
-       oo.EventEmitter.prototype.disconnect = function ( context, methods ) {
-               var i, event, method, bindings;
-
-               if ( methods ) {
-                       // Remove specific connections to the context
-                       for ( event in methods ) {
-                               method = methods[ event ];
-                               if ( Array.isArray( method ) ) {
-                                       method = method[ 0 ];
-                               }
-                               this.off( event, method, context );
-                       }
-               } else {
-                       // Remove all connections to the context
-                       for ( event in this.bindings ) {
-                               bindings = this.bindings[ event ];
-                               i = bindings.length;
-                               while ( i-- ) {
-                                       // bindings[i] may have been removed by 
the previous step's
-                                       // this.off so check it still exists
-                                       if ( bindings[ i ] && bindings[ i 
].context === context ) {
-                                               this.off( event, bindings[ i 
].method, context );
-                                       }
-                               }
-                       }
-               }
-
-               return this;
-       };
-
-}() );
-
-( function () {
-
-       /**
-        * Contain and manage a list of OO.EventEmitter items.
-        *
-        * Aggregates and manages their events collectively.
-        *
-        * This mixin must be used in a class that also mixes in 
OO.EventEmitter.
-        *
-        * @abstract
-        * @class OO.EmitterList
-        * @constructor
-        */
-       oo.EmitterList = function OoEmitterList() {
-               this.items = [];
-               this.aggregateItemEvents = {};
-       };
-
-       /* Events */
-
-       /**
-        * Item has been added
-        *
-        * @event add
-        * @param {OO.EventEmitter} item Added item
-        * @param {number} index Index items were added at
-        */
-
-       /**
-        * Item has been moved to a new index
-        *
-        * @event move
-        * @param {OO.EventEmitter} item Moved item
-        * @param {number} index Index item was moved to
-        * @param {number} oldIndex The original index the item was in
-        */
-
-       /**
-        * Item has been removed
-        *
-        * @event remove
-        * @param {OO.EventEmitter} item Removed item
-        * @param {number} index Index the item was removed from
-        */
-
-       /**
-        * @event clear The list has been cleared of items
-        */
-
-       /* Methods */
-
-       /**
-        * Normalize requested index to fit into the bounds of the given array.
-        *
-        * @private
-        * @static
-        * @param {Array} arr Given array
-        * @param {number|undefined} index Requested index
-        * @return {number} Normalized index
-        */
-       function normalizeArrayIndex( arr, index ) {
-               return ( index === undefined || index < 0 || index >= 
arr.length ) ?
-                       arr.length :
-                       index;
-       }
-
-       /**
-        * Get all items.
-        *
-        * @return {OO.EventEmitter[]} Items in the list
-        */
-       oo.EmitterList.prototype.getItems = function () {
-               return this.items.slice( 0 );
-       };
-
-       /**
-        * Get the index of a specific item.
-        *
-        * @param {OO.EventEmitter} item Requested item
-        * @return {number} Index of the item
-        */
-       oo.EmitterList.prototype.getItemIndex = function ( item ) {
-               return this.items.indexOf( item );
-       };
-
-       /**
-        * Get number of items.
-        *
-        * @return {number} Number of items in the list
-        */
-       oo.EmitterList.prototype.getItemCount = function () {
-               return this.items.length;
-       };
-
-       /**
-        * Check if a list contains no items.
-        *
-        * @return {boolean} Group is empty
-        */
-       oo.EmitterList.prototype.isEmpty = function () {
-               return !this.items.length;
-       };
-
-       /**
-        * Aggregate the events emitted by the group.
-        *
-        * When events are aggregated, the group will listen to all contained 
items for the event,
-        * and then emit the event under a new name. The new event will contain 
an additional leading
-        * parameter containing the item that emitted the original event. Other 
arguments emitted from
-        * the original event are passed through.
-        *
-        * @param {Object.<string,string|null>} events An object keyed by the 
name of the event that should be
-        *  aggregated  (e.g., ‘click’) and the value of the new name to use 
(e.g., ‘groupClick’).
-        *  A `null` value will remove aggregated events.
-
-        * @throws {Error} If aggregation already exists
-        */
-       oo.EmitterList.prototype.aggregate = function ( events ) {
-               var i, item, add, remove, itemEvent, groupEvent;
-
-               for ( itemEvent in events ) {
-                       groupEvent = events[ itemEvent ];
-
-                       // Remove existing aggregated event
-                       if ( Object.prototype.hasOwnProperty.call( 
this.aggregateItemEvents, itemEvent ) ) {
-                               // Don't allow duplicate aggregations
-                               if ( groupEvent ) {
-                                       throw new Error( 'Duplicate item event 
aggregation for ' + itemEvent );
-                               }
-                               // Remove event aggregation from existing items
-                               for ( i = 0; i < this.items.length; i++ ) {
-                                       item = this.items[ i ];
-                                       if ( item.connect && item.disconnect ) {
-                                               remove = {};
-                                               remove[ itemEvent ] = [ 'emit', 
this.aggregateItemEvents[ itemEvent ], item ];
-                                               item.disconnect( this, remove );
-                                       }
-                               }
-                               // Prevent future items from aggregating event
-                               delete this.aggregateItemEvents[ itemEvent ];
-                       }
-
-                       // Add new aggregate event
-                       if ( groupEvent ) {
-                               // Make future items aggregate event
-                               this.aggregateItemEvents[ itemEvent ] = 
groupEvent;
-                               // Add event aggregation to existing items
-                               for ( i = 0; i < this.items.length; i++ ) {
-                                       item = this.items[ i ];
-                                       if ( item.connect && item.disconnect ) {
-                                               add = {};
-                                               add[ itemEvent ] = [ 'emit', 
groupEvent, item ];
-                                               item.connect( this, add );
-                                       }
-                               }
-                       }
-               }
-       };
-
-       /**
-        * Add items to the list.
-        *
-        * @param {OO.EventEmitter|OO.EventEmitter[]} items Item to add or
-        *  an array of items to add
-        * @param {number} [index] Index to add items at. If no index is
-        *  given, or if the index that is given is invalid, the item
-        *  will be added at the end of the list.
-        * @chainable
-        * @fires add
-        * @fires move
-        */
-       oo.EmitterList.prototype.addItems = function ( items, index ) {
-               var i, oldIndex;
-
-               if ( !Array.isArray( items ) ) {
-                       items = [ items ];
-               }
-
-               if ( items.length === 0 ) {
-                       return this;
-               }
-
-               index = normalizeArrayIndex( this.items, index );
-               for ( i = 0; i < items.length; i++ ) {
-                       oldIndex = this.items.indexOf( items[ i ] );
-                       if ( oldIndex !== -1 ) {
-                               // Move item to new index
-                               index = this.moveItem( items[ i ], index );
-                               this.emit( 'move', items[ i ], index, oldIndex 
);
-                       } else {
-                               // insert item at index
-                               index = this.insertItem( items[ i ], index );
-                               this.emit( 'add', items[ i ], index );
-                       }
-                       index++;
-               }
-
-               return this;
-       };
-
-       /**
-        * Move an item from its current position to a new index.
-        *
-        * The item is expected to exist in the list. If it doesn't,
-        * the method will throw an exception.
-        *
-        * @private
-        * @param {OO.EventEmitter} item Items to add
-        * @param {number} newIndex Index to move the item to
-        * @return {number} The index the item was moved to
-        * @throws {Error} If item is not in the list
-        */
-       oo.EmitterList.prototype.moveItem = function ( item, newIndex ) {
-               var existingIndex = this.items.indexOf( item );
-
-               if ( existingIndex === -1 ) {
-                       throw new Error( 'Item cannot be moved, because it is 
not in the list.' );
-               }
-
-               newIndex = normalizeArrayIndex( this.items, newIndex );
-
-               // Remove the item from the current index
-               this.items.splice( existingIndex, 1 );
-
-               // Adjust new index after removal
-               newIndex--;
-
-               // Move the item to the new index
-               this.items.splice( newIndex, 0, item );
-
-               return newIndex;
-       };
-
-       /**
-        * Utility method to insert an item into the list, and
-        * connect it to aggregate events.
-        *
-        * Don't call this directly unless you know what you're doing.
-        * Use #addItems instead.
-        *
-        * @private
-        * @param {OO.EventEmitter} item Items to add
-        * @param {number} index Index to add items at
-        * @return {number} The index the item was added at
-        */
-       oo.EmitterList.prototype.insertItem = function ( item, index ) {
-               var events, event;
-
-               // Add the item to event aggregation
-               if ( item.connect && item.disconnect ) {
-                       events = {};
-                       for ( event in this.aggregateItemEvents ) {
-                               events[ event ] = [ 'emit', 
this.aggregateItemEvents[ event ], item ];
-                       }
-                       item.connect( this, events );
-               }
-
-               index = normalizeArrayIndex( this.items, index );
-
-               // Insert into items array
-               this.items.splice( index, 0, item );
-               return index;
-       };
-
-       /**
-        * Remove items.
-        *
-        * @param {OO.EventEmitter[]} items Items to remove
-        * @chainable
-        * @fires remove
-        */
-       oo.EmitterList.prototype.removeItems = function ( items ) {
-               var i, item, index;
-
-               if ( !Array.isArray( items ) ) {
-                       items = [ items ];
-               }
-
-               if ( items.length === 0 ) {
-                       return this;
-               }
-
-               // Remove specific items
-               for ( i = 0; i < items.length; i++ ) {
-                       item = items[ i ];
-                       index = this.items.indexOf( item );
-                       if ( index !== -1 ) {
-                               if ( item.connect && item.disconnect ) {
-                                       // Disconnect all listeners from the 
item
-                                       item.disconnect( this );
-                               }
-                               this.items.splice( index, 1 );
-                               this.emit( 'remove', item, index );
-                       }
-               }
-
-               return this;
-       };
-
-       /**
-        * Clear all items
-        *
-        * @chainable
-        * @fires clear
-        */
-       oo.EmitterList.prototype.clearItems = function () {
-               var i, item,
-                       cleared = this.items.splice( 0, this.items.length );
-
-               // Disconnect all items
-               for ( i = 0; i < cleared.length; i++ ) {
-                       item = cleared[ i ];
-                       if ( item.connect && item.disconnect ) {
-                               item.disconnect( this );
-                       }
-               }
-
-               this.emit( 'clear' );
-
-               return this;
-       };
-
-}() );
-
-/**
- * Manage a sorted list of OO.EmitterList objects.
- *
- * The sort order is based on a callback that compares two items. The return 
value of
- * callback( a, b ) must be less than zero if a < b, greater than zero if a > 
b, and zero
- * if a is equal to b. The callback should only return zero if the two objects 
are
- * considered equal.
- *
- * When an item changes in a way that could affect their sorting behavior, it 
must
- * emit the itemSortChange event. This will cause it to be re-sorted 
automatically.
- *
- * This mixin must be used in a class that also mixes in OO.EventEmitter.
- *
- * @abstract
- * @class OO.SortedEmitterList
- * @mixins OO.EmitterList
- * @constructor
- * @param {Function} sortingCallback Callback that compares two items.
- */
-oo.SortedEmitterList = function OoSortedEmitterList( sortingCallback ) {
-       // Mixin constructors
-       oo.EmitterList.call( this );
-
-       this.sortingCallback = sortingCallback;
-
-       // Listen to sortChange event and make sure
-       // we re-sort the changed item when that happens
-       this.aggregate( {
-               sortChange: 'itemSortChange'
-       } );
-
-       this.connect( this, {
-               itemSortChange: 'onItemSortChange'
-       } );
-};
-
-oo.mixinClass( oo.SortedEmitterList, oo.EmitterList );
-
-/* Events */
-
-/**
- * An item has changed properties that affect its sort positioning
- * inside the list.
- *
- * @private
- * @event itemSortChange
- */
-
-/* Methods */
-
-/**
- * Handle a case where an item changed a property that relates
- * to its sorted order
- *
- * @param {OO.EventEmitter} item Item in the list
- */
-oo.SortedEmitterList.prototype.onItemSortChange = function ( item ) {
-       // Remove the item
-       this.removeItems( item );
-       // Re-add the item so it is in the correct place
-       this.addItems( item );
-};
-
-/**
- * Change the sorting callback for this sorted list.
- *
- * The callback receives two items. The return value of callback(a, b) must be 
less than zero
- * if a < b, greater than zero if a > b, and zero if a is equal to b.
- *
- * @param {Function} sortingCallback Sorting callback
- */
-oo.SortedEmitterList.prototype.setSortingCallback = function ( sortingCallback 
) {
-       var items = this.getItems();
-
-       this.sortingCallback = sortingCallback;
-
-       // Empty the list
-       this.clearItems();
-       // Re-add the items in the new order
-       this.addItems( items );
-};
-
-/**
- * Add items to the sorted list.
- *
- * @chainable
- * @param {OO.EventEmitter|OO.EventEmitter[]} items Item to add or
- *  an array of items to add
- */
-oo.SortedEmitterList.prototype.addItems = function ( items ) {
-       var index, i, insertionIndex;
-
-       if ( !Array.isArray( items ) ) {
-               items = [ items ];
-       }
-
-       if ( items.length === 0 ) {
-               return this;
-       }
-
-       for ( i = 0; i < items.length; i++ ) {
-               // Find insertion index
-               insertionIndex = this.findInsertionIndex( items[ i ] );
-
-               // Check if the item exists using the sorting callback
-               // and remove it first if it exists
-               if (
-                       // First make sure the insertion index is not at the end
-                       // of the list (which means it does not point to any 
actual
-                       // items)
-                       insertionIndex <= this.items.length &&
-                       // Make sure there actually is an item in this index
-                       this.items[ insertionIndex ] &&
-                       // The callback returns 0 if the items are equal
-                       this.sortingCallback( this.items[ insertionIndex ], 
items[ i ] ) === 0
-               ) {
-                       // Remove the existing item
-                       this.removeItems( this.items[ insertionIndex ] );
-               }
-
-               // Insert item at the insertion index
-               index = this.insertItem( items[ i ], insertionIndex );
-               this.emit( 'add', items[ i ], insertionIndex );
-       }
-
-       return this;
-};
-
-/**
- * Find the index a given item should be inserted at. If the item is already
- * in the list, this will return the index where the item currently is.
- *
- * @param {OO.EventEmitter} item Items to insert
- * @return {number} The index the item should be inserted at
- */
-oo.SortedEmitterList.prototype.findInsertionIndex = function ( item ) {
-       var list = this;
-
-       return oo.binarySearch(
-               this.items,
-               // Fake a this.sortingCallback.bind( null, item ) call here
-               // otherwise this doesn't pass tests in phantomJS
-               function ( otherItem ) {
-                       return list.sortingCallback( item, otherItem );
-               },
-               true
-       );
-
-};
-
-/*global hasOwn */
-
-/**
- * @class OO.Registry
- * @mixins OO.EventEmitter
- *
- * @constructor
- */
-oo.Registry = function OoRegistry() {
-       // Mixin constructors
-       oo.EventEmitter.call( this );
-
-       // Properties
-       this.registry = {};
-};
-
-/* Inheritance */
-
-oo.mixinClass( oo.Registry, oo.EventEmitter );
-
-/* Events */
-
-/**
- * @event register
- * @param {string} name
- * @param {Mixed} data
- */
-
-/**
- * @event unregister
- * @param {string} name
- * @param {Mixed} data Data removed from registry
- */
-
-/* Methods */
-
-/**
- * Associate one or more symbolic names with some data.
- *
- * Any existing entry with the same name will be overridden.
- *
- * @param {string|string[]} name Symbolic name or list of symbolic names
- * @param {Mixed} data Data to associate with symbolic name
- * @fires register
- * @throws {Error} Name argument must be a string or array
- */
-oo.Registry.prototype.register = function ( name, data ) {
-       var i, len;
-       if ( typeof name === 'string' ) {
-               this.registry[ name ] = data;
-               this.emit( 'register', name, data );
-       } else if ( Array.isArray( name ) ) {
-               for ( i = 0, len = name.length; i < len; i++ ) {
-                       this.register( name[ i ], data );
-               }
-       } else {
-               throw new Error( 'Name must be a string or array, cannot be a ' 
+ typeof name );
-       }
-};
-
-/**
- * Remove one or more symbolic names from the registry
- *
- * @param {string|string[]} name Symbolic name or list of symbolic names
- * @fires unregister
- * @throws {Error} Name argument must be a string or array
- */
-oo.Registry.prototype.unregister = function ( name ) {
-       var i, len, data;
-       if ( typeof name === 'string' ) {
-               data = this.lookup( name );
-               if ( data !== undefined ) {
-                       delete this.registry[ name ];
-                       this.emit( 'unregister', name, data );
-               }
-       } else if ( Array.isArray( name ) ) {
-               for ( i = 0, len = name.length; i < len; i++ ) {
-                       this.unregister( name[ i ] );
-               }
-       } else {
-               throw new Error( 'Name must be a string or array, cannot be a ' 
+ typeof name );
-       }
-};
-
-/**
- * Get data for a given symbolic name.
- *
- * @param {string} name Symbolic name
- * @return {Mixed|undefined} Data associated with symbolic name
- */
-oo.Registry.prototype.lookup = function ( name ) {
-       if ( hasOwn.call( this.registry, name ) ) {
-               return this.registry[ name ];
-       }
-};
-
-/*global createObject */
-
-/**
- * @class OO.Factory
- * @extends OO.Registry
- *
- * @constructor
- */
-oo.Factory = function OoFactory() {
-       // Parent constructor
-       oo.Factory.parent.call( this );
-};
-
-/* Inheritance */
-
-oo.inheritClass( oo.Factory, oo.Registry );
-
-/* Methods */
-
-/**
- * Register a constructor with the factory.
- *
- * Classes must have a static `name` property to be registered.
- *
- *     function MyClass() {};
- *     OO.initClass( MyClass );
- *     // Adds a static property to the class defining a symbolic name
- *     MyClass.static.name = 'mine';
- *     // Registers class with factory, available via symbolic name 'mine'
- *     factory.register( MyClass );
- *
- * @param {Function} constructor Constructor to use when creating object
- * @throws {Error} Name must be a string and must not be empty
- * @throws {Error} Constructor must be a function
- */
-oo.Factory.prototype.register = function ( constructor ) {
-       var name;
-
-       if ( typeof constructor !== 'function' ) {
-               throw new Error( 'constructor must be a function, cannot be a ' 
+ typeof constructor );
-       }
-       name = constructor.static && constructor.static.name;
-       if ( typeof name !== 'string' || name === '' ) {
-               throw new Error( 'Name must be a string and must not be empty' 
);
-       }
-
-       // Parent method
-       oo.Factory.parent.prototype.register.call( this, name, constructor );
-};
-
-/**
- * Unregister a constructor from the factory.
- *
- * @param {Function} constructor Constructor to unregister
- * @throws {Error} Name must be a string and must not be empty
- * @throws {Error} Constructor must be a function
- */
-oo.Factory.prototype.unregister = function ( constructor ) {
-       var name;
-
-       if ( typeof constructor !== 'function' ) {
-               throw new Error( 'constructor must be a function, cannot be a ' 
+ typeof constructor );
-       }
-       name = constructor.static && constructor.static.name;
-       if ( typeof name !== 'string' || name === '' ) {
-               throw new Error( 'Name must be a string and must not be empty' 
);
-       }
-
-       // Parent method
-       oo.Factory.parent.prototype.unregister.call( this, name );
-};
-
-/**
- * Create an object based on a name.
- *
- * Name is used to look up the constructor to use, while all additional 
arguments are passed to the
- * constructor directly, so leaving one out will pass an undefined to the 
constructor.
- *
- * @param {string} name Object name
- * @param {...Mixed} [args] Arguments to pass to the constructor
- * @return {Object} The new object
- * @throws {Error} Unknown object name
- */
-oo.Factory.prototype.create = function ( name ) {
-       var obj, i,
-               args = [],
-               constructor = this.lookup( name );
-
-       if ( !constructor ) {
-               throw new Error( 'No class registered by that name: ' + name );
-       }
-
-       // Convert arguments to array and shift the first argument (name) off
-       for ( i = 1; i < arguments.length; i++ ) {
-               args.push( arguments[ i ] );
-       }
-
-       // We can't use the "new" operator with .apply directly because apply 
needs a
-       // context. So instead just do what "new" does: create an object that 
inherits from
-       // 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 );
-       constructor.apply( obj, args );
-       return obj;
-};
-
-/*jshint node:true */
-if ( typeof module !== 'undefined' && module.exports ) {
-       module.exports = oo;
-} else {
-       global.OO = oo;
-}
-
-}( this ) );
diff --git a/package.json b/package.json
index dc95dba..7ca60dc 100644
--- a/package.json
+++ b/package.json
@@ -40,6 +40,7 @@
     "karma-coverage": "1.1.1",
     "karma-firefox-launcher": "1.0.0",
     "karma-qunit": "1.0.0",
+    "oojs": "1.1.10",
     "qunitjs": "1.23.1",
     "stylelint-config-wikimedia": "0.3.0"
   }
diff --git a/tests/index.html b/tests/index.html
index bbc90f8..296dd23 100644
--- a/tests/index.html
+++ b/tests/index.html
@@ -26,7 +26,7 @@
                <script src="../lib/jquery/jquery.js"></script>
 
                <!-- oojs -->
-               <script src="../lib/oojs/oojs.jquery.js"></script>
+               <script src="../node_modules/oojs/dist/oojs.jquery.js"></script>
 
                <!-- oojs-ui -->
                <script src="../lib/oojs-ui/oojs-ui-core.js"></script>

-- 
To view, visit https://gerrit.wikimedia.org/r/323353
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I23f1e0933023ed196b32af00d887674de21c5c4b
Gerrit-PatchSet: 1
Gerrit-Project: VisualEditor/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Divec <da...@troi.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to