summaryrefslogtreecommitdiff
path: root/vendor/oojs/oojs-ui/build
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2015-06-04 07:31:04 +0200
committerPierre Schmitz <pierre@archlinux.de>2015-06-04 07:58:39 +0200
commitf6d65e533c62f6deb21342d4901ece24497b433e (patch)
treef28adf0362d14bcd448f7b65a7aaf38650f923aa /vendor/oojs/oojs-ui/build
parentc27b2e832fe25651ef2410fae85b41072aae7519 (diff)
Update to MediaWiki 1.25.1
Diffstat (limited to 'vendor/oojs/oojs-ui/build')
-rw-r--r--vendor/oojs/oojs-ui/build/banner.txt10
-rw-r--r--vendor/oojs/oojs-ui/build/modules.json240
-rw-r--r--vendor/oojs/oojs-ui/build/tasks/colorize-svg.js538
-rw-r--r--vendor/oojs/oojs-ui/build/tasks/typos.js89
-rw-r--r--vendor/oojs/oojs-ui/build/typos.json17
5 files changed, 894 insertions, 0 deletions
diff --git a/vendor/oojs/oojs-ui/build/banner.txt b/vendor/oojs/oojs-ui/build/banner.txt
new file mode 100644
index 00000000..f6926e8f
--- /dev/null
+++ b/vendor/oojs/oojs-ui/build/banner.txt
@@ -0,0 +1,10 @@
+/*!
+ * OOjs UI v<%= pkg.version %>
+ * https://www.mediawiki.org/wiki/OOjs_UI
+ *
+ * Copyright 2011–<%= grunt.template.today("yyyy") %> OOjs Team and other contributors.
+ * Released under the MIT license
+ * http://oojs.mit-license.org
+ *
+ * Date: <%= grunt.template.today("isoUtcDateTime") %>
+ */
diff --git a/vendor/oojs/oojs-ui/build/modules.json b/vendor/oojs/oojs-ui/build/modules.json
new file mode 100644
index 00000000..5aa773ff
--- /dev/null
+++ b/vendor/oojs/oojs-ui/build/modules.json
@@ -0,0 +1,240 @@
+{
+ "oojs-ui": {
+ "scripts": [
+ "src/intro.js.txt",
+ "src/core.js",
+
+ "src/elements/PendingElement.js",
+
+ "src/ActionSet.js",
+ "src/Element.js",
+ "src/Layout.js",
+ "src/Widget.js",
+ "src/Window.js",
+ "src/Dialog.js",
+ "src/WindowManager.js",
+ "src/Error.js",
+ "src/HtmlSnippet.js",
+ "src/Process.js",
+ "src/ToolFactory.js",
+ "src/ToolGroupFactory.js",
+ "src/Theme.js",
+
+ "src/elements/TabIndexedElement.js",
+ "src/elements/ButtonElement.js",
+ "src/elements/GroupElement.js",
+ "src/elements/DraggableElement.js",
+ "src/elements/DraggableGroupElement.js",
+ "src/elements/IconElement.js",
+ "src/elements/IndicatorElement.js",
+ "src/elements/LabelElement.js",
+ "src/elements/LookupElement.js",
+ "src/elements/PopupElement.js",
+ "src/elements/FlaggedElement.js",
+ "src/elements/TitledElement.js",
+ "src/elements/ClippableElement.js",
+
+ "src/Tool.js",
+ "src/Toolbar.js",
+ "src/ToolGroup.js",
+
+ "src/dialogs/MessageDialog.js",
+ "src/dialogs/ProcessDialog.js",
+
+ "src/layouts/FieldLayout.js",
+ "src/layouts/ActionFieldLayout.js",
+ "src/layouts/FieldsetLayout.js",
+ "src/layouts/FormLayout.js",
+ "src/layouts/MenuLayout.js",
+ "src/layouts/BookletLayout.js",
+ "src/layouts/IndexLayout.js",
+ "src/layouts/PanelLayout.js",
+ "src/layouts/CardLayout.js",
+ "src/layouts/PageLayout.js",
+ "src/layouts/StackLayout.js",
+
+ "src/toolgroups/BarToolGroup.js",
+ "src/toolgroups/PopupToolGroup.js",
+ "src/toolgroups/ListToolGroup.js",
+ "src/toolgroups/MenuToolGroup.js",
+
+ "src/tools/PopupTool.js",
+ "src/tools/ToolGroupTool.js",
+
+ "src/widgets/GroupWidget.js",
+ "src/widgets/ItemWidget.js",
+ "src/widgets/OutlineControlsWidget.js",
+ "src/widgets/ToggleWidget.js",
+
+ "src/widgets/ButtonGroupWidget.js",
+ "src/widgets/ButtonWidget.js",
+ "src/widgets/ActionWidget.js",
+ "src/widgets/PopupButtonWidget.js",
+ "src/widgets/ToggleButtonWidget.js",
+ "src/widgets/DropdownWidget.js",
+ "src/widgets/IconWidget.js",
+ "src/widgets/IndicatorWidget.js",
+ "src/widgets/InputWidget.js",
+ "src/widgets/ButtonInputWidget.js",
+ "src/widgets/CheckboxInputWidget.js",
+ "src/widgets/DropdownInputWidget.js",
+ "src/widgets/RadioInputWidget.js",
+ "src/widgets/TextInputWidget.js",
+ "src/widgets/ComboBoxWidget.js",
+ "src/widgets/LabelWidget.js",
+ "src/widgets/OptionWidget.js",
+ "src/widgets/DecoratedOptionWidget.js",
+ "src/widgets/ButtonOptionWidget.js",
+ "src/widgets/RadioOptionWidget.js",
+ "src/widgets/MenuOptionWidget.js",
+ "src/widgets/MenuSectionOptionWidget.js",
+ "src/widgets/OutlineOptionWidget.js",
+ "src/widgets/TabOptionWidget.js",
+ "src/widgets/PopupWidget.js",
+ "src/widgets/ProgressBarWidget.js",
+ "src/widgets/SearchWidget.js",
+ "src/widgets/SelectWidget.js",
+ "src/widgets/ButtonSelectWidget.js",
+ "src/widgets/RadioSelectWidget.js",
+ "src/widgets/MenuSelectWidget.js",
+ "src/widgets/TextInputMenuSelectWidget.js",
+ "src/widgets/OutlineSelectWidget.js",
+ "src/widgets/TabSelectWidget.js",
+ "src/widgets/ToggleSwitchWidget.js",
+
+ "src/outro.js.txt"
+ ]
+ },
+ "oojs-ui-apex": {
+ "scripts": [
+ "src/themes/apex/ApexTheme.js"
+ ],
+ "styles": [
+ "src/themes/apex/core.less",
+ "src/themes/apex/icons.json",
+ "src/themes/apex/indicators.json",
+ "src/themes/apex/textures.json"
+ ]
+ },
+ "oojs-ui-apex-noimages": {
+ "styles": [
+ "src/themes/apex/core.less"
+ ]
+ },
+ "oojs-ui-apex-icons-movement": {
+ "styles": [
+ "src/themes/apex/icons-movement.json"
+ ]
+ },
+ "oojs-ui-apex-icons-moderation": {
+ "styles": [
+ "src/themes/apex/icons-moderation.json"
+ ]
+ },
+ "oojs-ui-apex-icons-editing-core": {
+ "styles": [
+ "src/themes/apex/icons-editing-core.json"
+ ]
+ },
+ "oojs-ui-apex-icons-editing-styling": {
+ "styles": [
+ "src/themes/apex/icons-editing-styling.json"
+ ]
+ },
+ "oojs-ui-apex-icons-editing-list": {
+ "styles": [
+ "src/themes/apex/icons-editing-list.json"
+ ]
+ },
+ "oojs-ui-apex-icons-editing-advanced": {
+ "styles": [
+ "src/themes/apex/icons-editing-advanced.json"
+ ]
+ },
+ "oojs-ui-mediawiki": {
+ "scripts": [
+ "src/themes/mediawiki/MediaWikiTheme.js"
+ ],
+ "styles": [
+ "src/themes/mediawiki/core.less",
+ "src/themes/mediawiki/icons.json",
+ "src/themes/mediawiki/indicators.json",
+ "src/themes/mediawiki/textures.json"
+ ]
+ },
+ "oojs-ui-mediawiki-noimages": {
+ "styles": [
+ "src/themes/mediawiki/core.less"
+ ]
+ },
+ "oojs-ui-mediawiki-icons-movement": {
+ "styles": [
+ "src/themes/mediawiki/icons-movement.json"
+ ]
+ },
+ "oojs-ui-mediawiki-icons-content": {
+ "styles": [
+ "src/themes/mediawiki/icons-content.json"
+ ]
+ },
+ "oojs-ui-mediawiki-icons-alerts": {
+ "styles": [
+ "src/themes/mediawiki/icons-alerts.json"
+ ]
+ },
+ "oojs-ui-mediawiki-icons-interactions": {
+ "styles": [
+ "src/themes/mediawiki/icons-interactions.json"
+ ]
+ },
+ "oojs-ui-mediawiki-icons-moderation": {
+ "styles": [
+ "src/themes/mediawiki/icons-moderation.json"
+ ]
+ },
+ "oojs-ui-mediawiki-icons-editing-core": {
+ "styles": [
+ "src/themes/mediawiki/icons-editing-core.json"
+ ]
+ },
+ "oojs-ui-mediawiki-icons-editing-styling": {
+ "styles": [
+ "src/themes/mediawiki/icons-editing-styling.json"
+ ]
+ },
+ "oojs-ui-mediawiki-icons-editing-list": {
+ "styles": [
+ "src/themes/mediawiki/icons-editing-list.json"
+ ]
+ },
+ "oojs-ui-mediawiki-icons-editing-advanced": {
+ "styles": [
+ "src/themes/mediawiki/icons-editing-advanced.json"
+ ]
+ },
+ "oojs-ui-mediawiki-icons-media": {
+ "styles": [
+ "src/themes/mediawiki/icons-media.json"
+ ]
+ },
+ "oojs-ui-mediawiki-icons-location": {
+ "styles": [
+ "src/themes/mediawiki/icons-location.json"
+ ]
+ },
+ "oojs-ui-mediawiki-icons-user": {
+ "styles": [
+ "src/themes/mediawiki/icons-user.json"
+ ]
+ },
+ "oojs-ui-mediawiki-icons-layout": {
+ "styles": [
+ "src/themes/mediawiki/icons-layout.json"
+ ]
+ },
+ "oojs-ui-mediawiki-icons-wikimedia": {
+ "styles": [
+ "src/themes/mediawiki/icons-wikimedia.json"
+ ]
+ }
+}
diff --git a/vendor/oojs/oojs-ui/build/tasks/colorize-svg.js b/vendor/oojs/oojs-ui/build/tasks/colorize-svg.js
new file mode 100644
index 00000000..0eb421f3
--- /dev/null
+++ b/vendor/oojs/oojs-ui/build/tasks/colorize-svg.js
@@ -0,0 +1,538 @@
+/*!
+ * Colorize SVG files.
+ *
+ * The task currently doesn't use the standard file specifying methods with this.filesSrc.
+ * An option to do it may be added in the future.
+ */
+
+/*jshint node:true */
+
+var Q = require( 'q' ),
+ path = require( 'path' ),
+ asyncTask = require( 'grunt-promise-q' );
+
+module.exports = function ( grunt ) {
+
+ asyncTask.registerMulti(
+ grunt,
+ 'colorizeSvg',
+ 'Generate colored variants of SVG images',
+ function () {
+ var
+ data = this.data,
+ options = this.options(),
+ source = new Source(
+ data.srcDir,
+ options.images,
+ options.variants,
+ {
+ intro: options.intro,
+ prefix: options.prefix,
+ cssPrependPath: data.cssPrependPath,
+ selectorWithoutVariant: options.selectorWithoutVariant || options.selector,
+ selectorWithVariant: options.selectorWithVariant || options.selector
+ }
+ );
+
+ return source.getImageList().generate(
+ new Destination(
+ data.destDir,
+ data.destLessFile || {
+ ltr: path.join( data.destDir, 'images.less' ),
+ rtl: path.join( data.destDir, 'images.rtl.less' )
+ }
+ )
+ ).then( function ( totalFiles ) {
+ grunt.log.writeln( 'Created ' + totalFiles + ' SVG files.' );
+ } );
+ }
+ );
+
+ /* Classes */
+
+ /**
+ * Image source.
+ *
+ * @class
+ *
+ * @constructor
+ * @param {string} path Directory containing source images
+ * @param {Object} images Lists of image configurations
+ * @param {Object} [variants] List of variant configurations, keyed by variant name
+ * @param {Object} [options] Additional options
+ */
+ function Source( path, images, variants, options ) {
+ this.path = path;
+ this.images = images;
+ this.variants = variants || {};
+ this.options = options || {};
+ }
+
+ /**
+ * Get the path to source images directory.
+ *
+ * @return {string} Path
+ */
+ Source.prototype.getPath = function () {
+ return this.path;
+ };
+
+ /**
+ * Get image list.
+ *
+ * @return ImageList Image list
+ */
+ Source.prototype.getImageList = function () {
+ return new ImageList(
+ this.path,
+ new VariantList( this.variants ),
+ this.options,
+ this.images
+ );
+ };
+
+ /**
+ * Destination for images.
+ *
+ * @class
+ *
+ * @constructor
+ * @param {string} path Image path
+ * @param {Object} stylesheetPath Stylesheet file path
+ * @param {string} stylesheetPath.ltr Stylesheet file path, left-to-right
+ * @param {string} stylesheetPath.rtl Stylesheet file path, right-to-left
+ */
+ function Destination( path, stylesheetPath ) {
+ this.path = path;
+ this.stylesheetPath = stylesheetPath;
+ }
+
+ /**
+ * Get image destination directory.
+ *
+ * @return {string} Destination path
+ */
+ Destination.prototype.getPath = function () {
+ return this.path;
+ };
+
+ /**
+ * Get path to file of generated Less stylesheet.
+ *
+ * @param {string} textDirection Text direction to get stylesheet path for, 'ltr' or 'rtl'
+ * @return {string} Destination path
+ */
+ Destination.prototype.getStylesheetPath = function ( textDirection ) {
+ return this.stylesheetPath[ textDirection ];
+ };
+
+ /**
+ * Source image.
+ *
+ * @class
+ *
+ * @constructor
+ * @param {Object} list Image list
+ * @param {string} name Image name
+ * @param {Object} data Image options
+ */
+ function Image( list, name, data ) {
+ this.list = list;
+ this.name = name;
+ this.file = data.file;
+ this.variantNames = ( data.variants || [] )
+ .concat( this.list.getVariants().getGlobalVariantNames() )
+ .filter( function ( variant, index, variants ) {
+ return variants.indexOf( variant ) === index;
+ } );
+ }
+
+ /**
+ * Generate CSS and images.
+ *
+ * @param {Destination} destination Destination
+ * @return {Q.Promise}
+ */
+ Image.prototype.generate = function ( destination ) {
+ // TODO Make configurable
+ function getDeclarations( primary ) {
+ // If 'primary' is not a SVG file, 'fallback' and 'primary' are intentionally the same
+ var fallback = primary.replace( /\.svg$/, '.png' );
+ return '.oo-ui-background-image-svg2(' +
+ '\'' + ( cssPrependPath || '' ) + primary + '\', ' +
+ '\'' + ( cssPrependPath || '' ) + fallback + '\'' +
+ ')';
+ }
+ function variantizeFileName( fileName, variantName ) {
+ if ( variantName ) {
+ return fileName.replace( /\.(\w+)$/, '-' + variantName + '.$1' );
+ }
+ return fileName;
+ }
+
+ var selector, declarations, direction, lang, langSelector,
+ deferred = Q.defer(),
+ file = typeof this.file === 'string' ?
+ { ltr: this.file, rtl: this.file } :
+ { ltr: this.file.ltr || this.file.default, rtl: this.file.rtl || this.file.default },
+ moreLangs = this.file.lang || {},
+ name = this.name,
+ sourcePath = this.list.getPath(),
+ destinationPath = destination.getPath(),
+ variants = this.list.getVariants(),
+ cssClassPrefix = this.list.getCssClassPrefix(),
+ cssSelectors = this.list.getCssSelectors(),
+ cssPrependPath = this.list.options.cssPrependPath,
+ originalSvg = {},
+ rules = {
+ ltr: [],
+ rtl: []
+ },
+ files = {},
+ uncolorizableImages = [],
+ unknownVariants = [];
+
+ // Expand shorthands:
+ // { "en,de,fr": "foo.svg" } → { "en": "foo.svg", "de": "foo.svg", "fr": "foo.svg" }
+ moreLangs = Object.keys( moreLangs ).reduce( function ( langs, langList ) {
+ langList.split( ',' ).forEach( function ( lang ) {
+ langs[ lang ] = moreLangs[ langList ];
+ } );
+ return langs;
+ }, {} );
+
+ // Original
+ selector = cssSelectors.selectorWithoutVariant
+ .replace( /{prefix}/g, cssClassPrefix )
+ .replace( /{name}/g, name )
+ .replace( /{variant}/g, '' );
+
+ for ( direction in file ) {
+ declarations = getDeclarations( file[ direction ] );
+ rules[ direction ].push( selector + ' {\n\t' + declarations + '\n}' );
+
+ originalSvg[ direction ] = grunt.file.read(
+ path.join( sourcePath, file[ direction ] )
+ );
+ files[ path.join( destinationPath, file[ direction ] ) ] = originalSvg[ direction ];
+
+ for ( lang in moreLangs ) {
+ // This will not work for selectors ending in a pseudo-element.
+ langSelector = ':lang(' + lang + ')';
+ declarations = getDeclarations( moreLangs[ lang ] );
+ rules[ direction ].push(
+ '/* @noflip */\n' +
+ selector.replace( /,|$/g, langSelector + '$&' ) +
+ ' {\n\t' + declarations + '\n}'
+ );
+
+ originalSvg[ 'lang-' + lang ] = grunt.file.read(
+ path.join( sourcePath, moreLangs[ lang ] )
+ );
+ files[ path.join( destinationPath, moreLangs[ lang ] ) ] = originalSvg[ 'lang-' + lang ];
+ }
+ }
+
+ // Variants
+ this.variantNames.forEach( function ( variantName ) {
+ var variantSvg, destinationFilePath,
+ variant = variants.getVariant( variantName );
+
+ if ( variant === undefined ) {
+ unknownVariants.push( variantName );
+ return;
+ }
+
+ selector = cssSelectors.selectorWithVariant
+ .replace( /{prefix}/g, cssClassPrefix )
+ .replace( /{name}/g, name )
+ .replace( /{variant}/g, variantName );
+
+ for ( direction in file ) {
+ declarations = getDeclarations( variantizeFileName( file[ direction ], variantName ) );
+ rules[ direction ].push( selector + ' {\n\t' + declarations + '\n}' );
+
+ // TODO: Do this in a safer and more clever way
+ variantSvg = originalSvg[ direction ].replace(
+ /<svg[^>]*>/, '$&<style>* { fill: ' + variant.getColor() + ' }</style>'
+ );
+
+ if ( originalSvg[ direction ] === variantSvg ) {
+ uncolorizableImages.push( file[ direction ] );
+ continue;
+ }
+
+ destinationFilePath = path.join(
+ destinationPath,
+ variantizeFileName( file[ direction ], variantName )
+ );
+ files[ destinationFilePath ] = variantSvg;
+
+ for ( lang in moreLangs ) {
+ langSelector = ':lang(' + lang + ')';
+ declarations = getDeclarations( variantizeFileName( moreLangs[ lang ], variantName ) );
+ rules[ direction ].push(
+ '/* @noflip */\n' +
+ selector.replace( /,|$/g, langSelector + '$&' ) +
+ ' {\n\t' + declarations + '\n}'
+ );
+
+ variantSvg = originalSvg[ 'lang-' + lang ].replace(
+ /<svg[^>]*>/, '$&<style>* { fill: ' + variant.getColor() + ' }</style>'
+ );
+
+ if ( originalSvg[ 'lang-' + lang ] === variantSvg ) {
+ uncolorizableImages.push( moreLangs[ lang ] );
+ continue;
+ }
+
+ destinationFilePath = path.join(
+ destinationPath,
+ variantizeFileName( moreLangs[ lang ], variantName )
+ );
+ files[ destinationFilePath ] = variantSvg;
+ }
+ }
+ } );
+
+ if ( unknownVariants.length || uncolorizableImages.length ) {
+ if ( unknownVariants.length ) {
+ grunt.log.error(
+ unknownVariants.length +
+ ' unknown variants requested: ' +
+ unknownVariants.join( ', ' )
+ );
+ }
+ if ( uncolorizableImages.length ) {
+ grunt.log.error(
+ uncolorizableImages.length +
+ ' invalid source images: ' +
+ uncolorizableImages.join( ', ' )
+ );
+ }
+ deferred.reject( 'Failed to generate some images' );
+ } else {
+ deferred.resolve( {
+ rules: rules,
+ files: files
+ } );
+ }
+
+ return deferred.promise;
+ };
+
+ /**
+ * List of source images.
+ *
+ * @class
+ *
+ * @constructor
+ * @param {string} path Images path
+ * @param {VariantList} variants Variants list
+ * @param {Object} options Additional options
+ * @param {Object} data List of image configurations keyed by name
+ */
+ function ImageList( path, variants, options, data ) {
+ var key;
+
+ this.list = {};
+ this.path = path;
+ this.variants = variants;
+ this.options = options;
+
+ for ( key in data ) {
+ this.list[ key ] = new Image( this, key, data[ key ] );
+ }
+ }
+
+ /**
+ * Get image path.
+ *
+ * @return {string} Image path
+ */
+ ImageList.prototype.getPath = function () {
+ return this.path;
+ };
+
+ /**
+ * Get image variants.
+ *
+ * @return {VariantsList} Image variants
+ */
+ ImageList.prototype.getVariants = function () {
+ return this.variants;
+ };
+
+ /**
+ * Get CSS class prefix.
+ *
+ * @return {string} CSS class prefix
+ */
+ ImageList.prototype.getCssClassPrefix = function () {
+ return this.options.prefix || '';
+ };
+
+ /**
+ * Get CSS selectors.
+ *
+ * @return {Object.<string, string>} CSS selectors
+ */
+ ImageList.prototype.getCssSelectors = function () {
+ return {
+ selectorWithoutVariant: this.options.selectorWithoutVariant || '.{prefix}-{name}',
+ selectorWithVariant: this.options.selectorWithVariant || '.{prefix}-{name}-{variant}'
+ };
+ };
+
+ /**
+ * Get CSS file intro.
+ *
+ * @return {string} CSS file intro
+ */
+ ImageList.prototype.getCssIntro = function () {
+ return this.options.intro || '';
+ };
+
+ /**
+ * Get number of images in list.
+ *
+ * @return {number} List length
+ */
+ ImageList.prototype.getLength = function () {
+ return Object.keys( this.list ).length;
+ };
+
+ /**
+ * Generate images and CSS.
+ *
+ * @param {Destination} destination Destination
+ * @return {Q.Promise} Promise resolved with number of generated SVG files
+ */
+ ImageList.prototype.generate = function ( destination ) {
+ var list = this.list,
+ intro = this.getCssIntro();
+ return Q.all( Object.keys( this.list ).map( function ( key ) {
+ return list[ key ].generate( destination );
+ } ) ).then( function ( data ) {
+ var textDirection, stylesheetPath, destinationFilePath, dataFormat;
+ dataFormat = {
+ files: {},
+ rules: {
+ ltr: [],
+ rtl: []
+ }
+ };
+
+ data = data.reduce( function ( a, b ) {
+ for ( destinationFilePath in b.files ) {
+ // This de-duplicates the entries, as the same file can be used by many Images
+ a.files[ destinationFilePath ] = b.files[ destinationFilePath ];
+ }
+ a.rules.ltr = a.rules.ltr.concat( b.rules.ltr );
+ a.rules.rtl = a.rules.rtl.concat( b.rules.rtl );
+ return a;
+ }, dataFormat );
+
+ for ( textDirection in data.rules ) {
+ stylesheetPath = destination.getStylesheetPath( textDirection );
+ grunt.file.write(
+ stylesheetPath,
+ intro + '\n' + data.rules[ textDirection ].join( '\n' )
+ );
+ grunt.log.writeln( 'Created "' + stylesheetPath + '".' );
+ }
+ for ( destinationFilePath in data.files ) {
+ grunt.file.write( destinationFilePath, data.files[ destinationFilePath ] );
+ }
+
+ return Object.keys( data.files ).length;
+ } );
+ };
+
+ /**
+ * Image variant.
+ *
+ * @class
+ *
+ * @constructor
+ * @param {VariantList} list Variant list
+ * @param {string} name Variant name
+ * @param {Object} data Variant options
+ */
+ function Variant( list, name, data ) {
+ // Properties
+ this.list = list;
+ this.name = name;
+ this.color = data.color;
+ this.global = data.global;
+ }
+
+ /**
+ * Check if variant is global.
+ *
+ * @return {boolean} Variant is global
+ */
+ Variant.prototype.isGlobal = function () {
+ return this.global;
+ };
+
+ /**
+ * Get variant color.
+ *
+ * @return {string} CSS color expression
+ */
+ Variant.prototype.getColor = function () {
+ return this.color;
+ };
+
+ /**
+ * List of variants.
+ *
+ * @class
+ *
+ * @constructor
+ * @param {Object} list List of variant configurations keyed by name
+ */
+ function VariantList( data ) {
+ var key;
+
+ this.list = {};
+ this.globals = [];
+
+ for ( key in data ) {
+ this.list[ key ] = new Variant( this, key, data[ key ] );
+ if ( this.list[ key ].isGlobal() ) {
+ this.globals.push( key );
+ }
+ }
+ }
+
+ /**
+ * Get names of global variants.
+ *
+ * @return {string[]} Global variant names
+ */
+ VariantList.prototype.getGlobalVariantNames = function () {
+ return this.globals;
+ };
+
+ /**
+ * Get variant by name.
+ *
+ * @param {string} name Variant name
+ * @return {Variant|undefined} Variant with matching name, or undefined of none exists.
+ */
+ VariantList.prototype.getVariant = function ( name ) {
+ return this.list[ name ];
+ };
+
+ /**
+ * Get number of variants in list.
+ *
+ * @return {number} List length
+ */
+ VariantList.prototype.getLength = function () {
+ return Object.keys( this.list ).length;
+ };
+
+};
diff --git a/vendor/oojs/oojs-ui/build/tasks/typos.js b/vendor/oojs/oojs-ui/build/tasks/typos.js
new file mode 100644
index 00000000..6c0bb4ee
--- /dev/null
+++ b/vendor/oojs/oojs-ui/build/tasks/typos.js
@@ -0,0 +1,89 @@
+/*!
+ * Check files from 'src' for typos; fail if any are found.
+ */
+
+/*jshint node:true */
+module.exports = function ( grunt ) {
+
+ grunt.registerMultiTask( 'typos', function () {
+ var typosData, typosCSRegExp, typosCIRegExp, file,
+ options = this.options( {
+ typos: 'typos.json'
+ } ),
+ files = this.filesSrc,
+ fileCount = files.length,
+ typosJSON = grunt.file.read( options.typos ),
+ typoCount = 0,
+ ok = true;
+
+ try {
+ typosData = JSON.parse( typosJSON );
+ } catch ( e ) {
+ grunt.log.error( 'Could not parse ' + options.typos + ': ' + e );
+ }
+
+ typosData.caseSensitive = typosData.caseSensitive || [];
+ typosData.caseInsensitive = typosData.caseInsensitive || [];
+
+ typoCount += typosData.caseSensitive.length + typosData.caseInsensitive.length;
+
+ function patternMap( typo ) {
+ return typo[ 0 ];
+ }
+
+ if ( typosData.caseSensitive.length ) {
+ typosCSRegExp = new RegExp(
+ '(' + typosData.caseSensitive.map( patternMap ).join( '|' ) + ')', 'g'
+ );
+ }
+
+ if ( typosData.caseInsensitive.length ) {
+ typosCIRegExp = new RegExp(
+ '(' + typosData.caseInsensitive.map( patternMap ).join( '|' ) + ')', 'gi'
+ );
+ }
+
+ function findTypo( line, lineNumber, filepath, typos, flags ) {
+ // Check each pattern to find the replacement
+ typos.forEach( function ( typo ) {
+ var replace,
+ pattern = new RegExp( typo[ 0 ], flags ),
+ matches = line.match( pattern );
+
+ if ( matches ) {
+ ok = false;
+ replace = matches[ 0 ].replace( pattern, typo[ 1 ], flags );
+ grunt.log.error(
+ 'File "' + filepath + '" contains typo "' + matches[ 0 ] + '" on line ' + lineNumber +
+ ', did you mean "' + replace + '"?'
+ );
+ }
+ } );
+ }
+
+ files.forEach( function ( filepath ) {
+ if ( grunt.file.isDir( filepath ) ) {
+ fileCount--;
+ return;
+ }
+ file = grunt.file.read( filepath );
+ file.split( '\n' ).forEach( function ( line, lineNumber ) {
+ // Check for any typos on the line with group expressions
+ if ( typosCSRegExp && typosCSRegExp.test( line ) ) {
+ findTypo( line, lineNumber, filepath, typosData.caseSensitive, 'g' );
+ }
+ if ( typosCIRegExp && typosCIRegExp.test( line ) ) {
+ findTypo( line, lineNumber, filepath, typosData.caseInsensitive, 'gi' );
+ }
+ } );
+ } );
+
+ if ( !ok ) {
+ return false;
+ }
+
+ grunt.log.ok( 'No typos found; ' +
+ fileCount + ' file' + ( fileCount !== 1 ? 's' : '' ) + ' checked for ' +
+ typoCount + ' typo' + ( typoCount !== 1 ? 's' : '' ) + '.' );
+ } );
+};
diff --git a/vendor/oojs/oojs-ui/build/typos.json b/vendor/oojs/oojs-ui/build/typos.json
new file mode 100644
index 00000000..5916d77e
--- /dev/null
+++ b/vendor/oojs/oojs-ui/build/typos.json
@@ -0,0 +1,17 @@
+{
+ "caseSensitive": [
+ [ "@returns", "@return" ]
+ ],
+ "caseInsensitive": [
+ [ "paralell", "parallel" ],
+ [ "properites", "properties" ],
+ [ "visiblit(ies|y)", "visibilit$1" ],
+ [ "movablilties", "movabilities" ],
+ [ "inpsect(ors?|ion)", "inspect$1" ],
+ [ "\bteh\b", "the" ],
+ [ "intialization", "initialization" ],
+ [ "durring", "during" ],
+ [ "contian", "contain" ],
+ [ "occured", "occurred" ]
+ ]
+}