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