12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366 |
- /*!
- * OOjs UI v0.21.1
- * https://www.mediawiki.org/wiki/OOjs_UI
- *
- * Copyright 2011–2017 OOjs UI Team and other contributors.
- * Released under the MIT license
- * http://oojs.mit-license.org
- *
- * Date: 2017-04-18T23:32:49Z
- */
- ( function ( OO ) {
- 'use strict';
- /**
- * Toolbars are complex interface components that permit users to easily access a variety
- * of {@link OO.ui.Tool tools} (e.g., formatting commands) and actions, which are additional commands that are
- * part of the toolbar, but not configured as tools.
- *
- * Individual tools are customized and then registered with a {@link OO.ui.ToolFactory tool factory}, which creates
- * the tools on demand. Each tool has a symbolic name (used when registering the tool), a title (e.g., ‘Insert
- * image’), and an icon.
- *
- * Individual tools are organized in {@link OO.ui.ToolGroup toolgroups}, which can be {@link OO.ui.MenuToolGroup menus}
- * of tools, {@link OO.ui.ListToolGroup lists} of tools, or a single {@link OO.ui.BarToolGroup bar} of tools.
- * The arrangement and order of the toolgroups is customized when the toolbar is set up. Tools can be presented in
- * any order, but each can only appear once in the toolbar.
- *
- * The toolbar can be synchronized with the state of the external "application", like a text
- * editor's editing area, marking tools as active/inactive (e.g. a 'bold' tool would be shown as
- * active when the text cursor was inside bolded text) or enabled/disabled (e.g. a table caption
- * tool would be disabled while the user is not editing a table). A state change is signalled by
- * emitting the {@link #event-updateState 'updateState' event}, which calls Tools'
- * {@link OO.ui.Tool#onUpdateState onUpdateState method}.
- *
- * The following is an example of a basic toolbar.
- *
- * @example
- * // Example of a toolbar
- * // Create the toolbar
- * var toolFactory = new OO.ui.ToolFactory();
- * var toolGroupFactory = new OO.ui.ToolGroupFactory();
- * var toolbar = new OO.ui.Toolbar( toolFactory, toolGroupFactory );
- *
- * // We will be placing status text in this element when tools are used
- * var $area = $( '<p>' ).text( 'Toolbar example' );
- *
- * // Define the tools that we're going to place in our toolbar
- *
- * // Create a class inheriting from OO.ui.Tool
- * function SearchTool() {
- * SearchTool.parent.apply( this, arguments );
- * }
- * OO.inheritClass( SearchTool, OO.ui.Tool );
- * // Each tool must have a 'name' (used as an internal identifier, see later) and at least one
- * // of 'icon' and 'title' (displayed icon and text).
- * SearchTool.static.name = 'search';
- * SearchTool.static.icon = 'search';
- * SearchTool.static.title = 'Search...';
- * // Defines the action that will happen when this tool is selected (clicked).
- * SearchTool.prototype.onSelect = function () {
- * $area.text( 'Search tool clicked!' );
- * // Never display this tool as "active" (selected).
- * this.setActive( false );
- * };
- * SearchTool.prototype.onUpdateState = function () {};
- * // Make this tool available in our toolFactory and thus our toolbar
- * toolFactory.register( SearchTool );
- *
- * // Register two more tools, nothing interesting here
- * function SettingsTool() {
- * SettingsTool.parent.apply( this, arguments );
- * }
- * OO.inheritClass( SettingsTool, OO.ui.Tool );
- * SettingsTool.static.name = 'settings';
- * SettingsTool.static.icon = 'settings';
- * SettingsTool.static.title = 'Change settings';
- * SettingsTool.prototype.onSelect = function () {
- * $area.text( 'Settings tool clicked!' );
- * this.setActive( false );
- * };
- * SettingsTool.prototype.onUpdateState = function () {};
- * toolFactory.register( SettingsTool );
- *
- * // Register two more tools, nothing interesting here
- * function StuffTool() {
- * StuffTool.parent.apply( this, arguments );
- * }
- * OO.inheritClass( StuffTool, OO.ui.Tool );
- * StuffTool.static.name = 'stuff';
- * StuffTool.static.icon = 'ellipsis';
- * StuffTool.static.title = 'More stuff';
- * StuffTool.prototype.onSelect = function () {
- * $area.text( 'More stuff tool clicked!' );
- * this.setActive( false );
- * };
- * StuffTool.prototype.onUpdateState = function () {};
- * toolFactory.register( StuffTool );
- *
- * // This is a PopupTool. Rather than having a custom 'onSelect' action, it will display a
- * // little popup window (a PopupWidget).
- * function HelpTool( toolGroup, config ) {
- * OO.ui.PopupTool.call( this, toolGroup, $.extend( { popup: {
- * padded: true,
- * label: 'Help',
- * head: true
- * } }, config ) );
- * this.popup.$body.append( '<p>I am helpful!</p>' );
- * }
- * OO.inheritClass( HelpTool, OO.ui.PopupTool );
- * HelpTool.static.name = 'help';
- * HelpTool.static.icon = 'help';
- * HelpTool.static.title = 'Help';
- * toolFactory.register( HelpTool );
- *
- * // Finally define which tools and in what order appear in the toolbar. Each tool may only be
- * // used once (but not all defined tools must be used).
- * toolbar.setup( [
- * {
- * // 'bar' tool groups display tools' icons only, side-by-side.
- * type: 'bar',
- * include: [ 'search', 'help' ]
- * },
- * {
- * // 'list' tool groups display both the titles and icons, in a dropdown list.
- * type: 'list',
- * indicator: 'down',
- * label: 'More',
- * include: [ 'settings', 'stuff' ]
- * }
- * // Note how the tools themselves are toolgroup-agnostic - the same tool can be displayed
- * // either in a 'list' or a 'bar'. There is a 'menu' tool group too, not showcased here,
- * // since it's more complicated to use. (See the next example snippet on this page.)
- * ] );
- *
- * // Create some UI around the toolbar and place it in the document
- * var frame = new OO.ui.PanelLayout( {
- * expanded: false,
- * framed: true
- * } );
- * var contentFrame = new OO.ui.PanelLayout( {
- * expanded: false,
- * padded: true
- * } );
- * frame.$element.append(
- * toolbar.$element,
- * contentFrame.$element.append( $area )
- * );
- * $( 'body' ).append( frame.$element );
- *
- * // Here is where the toolbar is actually built. This must be done after inserting it into the
- * // document.
- * toolbar.initialize();
- * toolbar.emit( 'updateState' );
- *
- * The following example extends the previous one to illustrate 'menu' toolgroups and the usage of
- * {@link #event-updateState 'updateState' event}.
- *
- * @example
- * // Create the toolbar
- * var toolFactory = new OO.ui.ToolFactory();
- * var toolGroupFactory = new OO.ui.ToolGroupFactory();
- * var toolbar = new OO.ui.Toolbar( toolFactory, toolGroupFactory );
- *
- * // We will be placing status text in this element when tools are used
- * var $area = $( '<p>' ).text( 'Toolbar example' );
- *
- * // Define the tools that we're going to place in our toolbar
- *
- * // Create a class inheriting from OO.ui.Tool
- * function SearchTool() {
- * SearchTool.parent.apply( this, arguments );
- * }
- * OO.inheritClass( SearchTool, OO.ui.Tool );
- * // Each tool must have a 'name' (used as an internal identifier, see later) and at least one
- * // of 'icon' and 'title' (displayed icon and text).
- * SearchTool.static.name = 'search';
- * SearchTool.static.icon = 'search';
- * SearchTool.static.title = 'Search...';
- * // Defines the action that will happen when this tool is selected (clicked).
- * SearchTool.prototype.onSelect = function () {
- * $area.text( 'Search tool clicked!' );
- * // Never display this tool as "active" (selected).
- * this.setActive( false );
- * };
- * SearchTool.prototype.onUpdateState = function () {};
- * // Make this tool available in our toolFactory and thus our toolbar
- * toolFactory.register( SearchTool );
- *
- * // Register two more tools, nothing interesting here
- * function SettingsTool() {
- * SettingsTool.parent.apply( this, arguments );
- * this.reallyActive = false;
- * }
- * OO.inheritClass( SettingsTool, OO.ui.Tool );
- * SettingsTool.static.name = 'settings';
- * SettingsTool.static.icon = 'settings';
- * SettingsTool.static.title = 'Change settings';
- * SettingsTool.prototype.onSelect = function () {
- * $area.text( 'Settings tool clicked!' );
- * // Toggle the active state on each click
- * this.reallyActive = !this.reallyActive;
- * this.setActive( this.reallyActive );
- * // To update the menu label
- * this.toolbar.emit( 'updateState' );
- * };
- * SettingsTool.prototype.onUpdateState = function () {};
- * toolFactory.register( SettingsTool );
- *
- * // Register two more tools, nothing interesting here
- * function StuffTool() {
- * StuffTool.parent.apply( this, arguments );
- * this.reallyActive = false;
- * }
- * OO.inheritClass( StuffTool, OO.ui.Tool );
- * StuffTool.static.name = 'stuff';
- * StuffTool.static.icon = 'ellipsis';
- * StuffTool.static.title = 'More stuff';
- * StuffTool.prototype.onSelect = function () {
- * $area.text( 'More stuff tool clicked!' );
- * // Toggle the active state on each click
- * this.reallyActive = !this.reallyActive;
- * this.setActive( this.reallyActive );
- * // To update the menu label
- * this.toolbar.emit( 'updateState' );
- * };
- * StuffTool.prototype.onUpdateState = function () {};
- * toolFactory.register( StuffTool );
- *
- * // This is a PopupTool. Rather than having a custom 'onSelect' action, it will display a
- * // little popup window (a PopupWidget). 'onUpdateState' is also already implemented.
- * function HelpTool( toolGroup, config ) {
- * OO.ui.PopupTool.call( this, toolGroup, $.extend( { popup: {
- * padded: true,
- * label: 'Help',
- * head: true
- * } }, config ) );
- * this.popup.$body.append( '<p>I am helpful!</p>' );
- * }
- * OO.inheritClass( HelpTool, OO.ui.PopupTool );
- * HelpTool.static.name = 'help';
- * HelpTool.static.icon = 'help';
- * HelpTool.static.title = 'Help';
- * toolFactory.register( HelpTool );
- *
- * // Finally define which tools and in what order appear in the toolbar. Each tool may only be
- * // used once (but not all defined tools must be used).
- * toolbar.setup( [
- * {
- * // 'bar' tool groups display tools' icons only, side-by-side.
- * type: 'bar',
- * include: [ 'search', 'help' ]
- * },
- * {
- * // 'menu' tool groups display both the titles and icons, in a dropdown menu.
- * // Menu label indicates which items are selected.
- * type: 'menu',
- * indicator: 'down',
- * include: [ 'settings', 'stuff' ]
- * }
- * ] );
- *
- * // Create some UI around the toolbar and place it in the document
- * var frame = new OO.ui.PanelLayout( {
- * expanded: false,
- * framed: true
- * } );
- * var contentFrame = new OO.ui.PanelLayout( {
- * expanded: false,
- * padded: true
- * } );
- * frame.$element.append(
- * toolbar.$element,
- * contentFrame.$element.append( $area )
- * );
- * $( 'body' ).append( frame.$element );
- *
- * // Here is where the toolbar is actually built. This must be done after inserting it into the
- * // document.
- * toolbar.initialize();
- * toolbar.emit( 'updateState' );
- *
- * @class
- * @extends OO.ui.Element
- * @mixins OO.EventEmitter
- * @mixins OO.ui.mixin.GroupElement
- *
- * @constructor
- * @param {OO.ui.ToolFactory} toolFactory Factory for creating tools
- * @param {OO.ui.ToolGroupFactory} toolGroupFactory Factory for creating toolgroups
- * @param {Object} [config] Configuration options
- * @cfg {boolean} [actions] Add an actions section to the toolbar. Actions are commands that are included
- * in the toolbar, but are not configured as tools. By default, actions are displayed on the right side of
- * the toolbar.
- * @cfg {string} [position='top'] Whether the toolbar is positioned above ('top') or below ('bottom') content.
- */
- OO.ui.Toolbar = function OoUiToolbar( toolFactory, toolGroupFactory, config ) {
- // Allow passing positional parameters inside the config object
- if ( OO.isPlainObject( toolFactory ) && config === undefined ) {
- config = toolFactory;
- toolFactory = config.toolFactory;
- toolGroupFactory = config.toolGroupFactory;
- }
- // Configuration initialization
- config = config || {};
- // Parent constructor
- OO.ui.Toolbar.parent.call( this, config );
- // Mixin constructors
- OO.EventEmitter.call( this );
- OO.ui.mixin.GroupElement.call( this, config );
- // Properties
- this.toolFactory = toolFactory;
- this.toolGroupFactory = toolGroupFactory;
- this.groups = [];
- this.tools = {};
- this.position = config.position || 'top';
- this.$bar = $( '<div>' );
- this.$actions = $( '<div>' );
- this.initialized = false;
- this.narrowThreshold = null;
- this.onWindowResizeHandler = this.onWindowResize.bind( this );
- // Events
- this.$element
- .add( this.$bar ).add( this.$group ).add( this.$actions )
- .on( 'mousedown keydown', this.onPointerDown.bind( this ) );
- // Initialization
- this.$group.addClass( 'oo-ui-toolbar-tools' );
- if ( config.actions ) {
- this.$bar.append( this.$actions.addClass( 'oo-ui-toolbar-actions' ) );
- }
- this.$bar
- .addClass( 'oo-ui-toolbar-bar' )
- .append( this.$group, '<div style="clear:both"></div>' );
- this.$element.addClass( 'oo-ui-toolbar oo-ui-toolbar-position-' + this.position ).append( this.$bar );
- };
- /* Setup */
- OO.inheritClass( OO.ui.Toolbar, OO.ui.Element );
- OO.mixinClass( OO.ui.Toolbar, OO.EventEmitter );
- OO.mixinClass( OO.ui.Toolbar, OO.ui.mixin.GroupElement );
- /* Events */
- /**
- * @event updateState
- *
- * An 'updateState' event must be emitted on the Toolbar (by calling `toolbar.emit( 'updateState' )`)
- * every time the state of the application using the toolbar changes, and an update to the state of
- * tools is required.
- *
- * @param {...Mixed} data Application-defined parameters
- */
- /* Methods */
- /**
- * Get the tool factory.
- *
- * @return {OO.ui.ToolFactory} Tool factory
- */
- OO.ui.Toolbar.prototype.getToolFactory = function () {
- return this.toolFactory;
- };
- /**
- * Get the toolgroup factory.
- *
- * @return {OO.Factory} Toolgroup factory
- */
- OO.ui.Toolbar.prototype.getToolGroupFactory = function () {
- return this.toolGroupFactory;
- };
- /**
- * Handles mouse down events.
- *
- * @private
- * @param {jQuery.Event} e Mouse down event
- */
- OO.ui.Toolbar.prototype.onPointerDown = function ( e ) {
- var $closestWidgetToEvent = $( e.target ).closest( '.oo-ui-widget' ),
- $closestWidgetToToolbar = this.$element.closest( '.oo-ui-widget' );
- if ( !$closestWidgetToEvent.length || $closestWidgetToEvent[ 0 ] === $closestWidgetToToolbar[ 0 ] ) {
- return false;
- }
- };
- /**
- * Handle window resize event.
- *
- * @private
- * @param {jQuery.Event} e Window resize event
- */
- OO.ui.Toolbar.prototype.onWindowResize = function () {
- this.$element.toggleClass(
- 'oo-ui-toolbar-narrow',
- this.$bar.width() <= this.getNarrowThreshold()
- );
- };
- /**
- * Get the (lazily-computed) width threshold for applying the oo-ui-toolbar-narrow
- * class.
- *
- * @private
- * @return {number} Width threshold in pixels
- */
- OO.ui.Toolbar.prototype.getNarrowThreshold = function () {
- if ( this.narrowThreshold === null ) {
- this.narrowThreshold = this.$group.width() + this.$actions.width();
- }
- return this.narrowThreshold;
- };
- /**
- * Sets up handles and preloads required information for the toolbar to work.
- * This must be called after it is attached to a visible document and before doing anything else.
- */
- OO.ui.Toolbar.prototype.initialize = function () {
- if ( !this.initialized ) {
- this.initialized = true;
- $( this.getElementWindow() ).on( 'resize', this.onWindowResizeHandler );
- this.onWindowResize();
- }
- };
- /**
- * Set up the toolbar.
- *
- * The toolbar is set up with a list of toolgroup configurations that specify the type of
- * toolgroup ({@link OO.ui.BarToolGroup bar}, {@link OO.ui.MenuToolGroup menu}, or {@link OO.ui.ListToolGroup list})
- * to add and which tools to include, exclude, promote, or demote within that toolgroup. Please
- * see {@link OO.ui.ToolGroup toolgroups} for more information about including tools in toolgroups.
- *
- * @param {Object.<string,Array>} groups List of toolgroup configurations
- * @param {Array|string} [groups.include] Tools to include in the toolgroup
- * @param {Array|string} [groups.exclude] Tools to exclude from the toolgroup
- * @param {Array|string} [groups.promote] Tools to promote to the beginning of the toolgroup
- * @param {Array|string} [groups.demote] Tools to demote to the end of the toolgroup
- */
- OO.ui.Toolbar.prototype.setup = function ( groups ) {
- var i, len, type, group,
- items = [],
- defaultType = 'bar';
- // Cleanup previous groups
- this.reset();
- // Build out new groups
- for ( i = 0, len = groups.length; i < len; i++ ) {
- group = groups[ i ];
- if ( group.include === '*' ) {
- // Apply defaults to catch-all groups
- if ( group.type === undefined ) {
- group.type = 'list';
- }
- if ( group.label === undefined ) {
- group.label = OO.ui.msg( 'ooui-toolbar-more' );
- }
- }
- // Check type has been registered
- type = this.getToolGroupFactory().lookup( group.type ) ? group.type : defaultType;
- items.push(
- this.getToolGroupFactory().create( type, this, group )
- );
- }
- this.addItems( items );
- };
- /**
- * Remove all tools and toolgroups from the toolbar.
- */
- OO.ui.Toolbar.prototype.reset = function () {
- var i, len;
- this.groups = [];
- this.tools = {};
- for ( i = 0, len = this.items.length; i < len; i++ ) {
- this.items[ i ].destroy();
- }
- this.clearItems();
- };
- /**
- * Destroy the toolbar.
- *
- * Destroying the toolbar removes all event handlers and DOM elements that constitute the toolbar. Call
- * this method whenever you are done using a toolbar.
- */
- OO.ui.Toolbar.prototype.destroy = function () {
- $( this.getElementWindow() ).off( 'resize', this.onWindowResizeHandler );
- this.reset();
- this.$element.remove();
- };
- /**
- * Check if the tool is available.
- *
- * Available tools are ones that have not yet been added to the toolbar.
- *
- * @param {string} name Symbolic name of tool
- * @return {boolean} Tool is available
- */
- OO.ui.Toolbar.prototype.isToolAvailable = function ( name ) {
- return !this.tools[ name ];
- };
- /**
- * Prevent tool from being used again.
- *
- * @param {OO.ui.Tool} tool Tool to reserve
- */
- OO.ui.Toolbar.prototype.reserveTool = function ( tool ) {
- this.tools[ tool.getName() ] = tool;
- };
- /**
- * Allow tool to be used again.
- *
- * @param {OO.ui.Tool} tool Tool to release
- */
- OO.ui.Toolbar.prototype.releaseTool = function ( tool ) {
- delete this.tools[ tool.getName() ];
- };
- /**
- * Get accelerator label for tool.
- *
- * The OOjs UI library does not contain an accelerator system, but this is the hook for one. To
- * use an accelerator system, subclass the toolbar and override this method, which is meant to return a label
- * that describes the accelerator keys for the tool passed (by symbolic name) to the method.
- *
- * @param {string} name Symbolic name of tool
- * @return {string|undefined} Tool accelerator label if available
- */
- OO.ui.Toolbar.prototype.getToolAccelerator = function () {
- return undefined;
- };
- /**
- * Tools, together with {@link OO.ui.ToolGroup toolgroups}, constitute {@link OO.ui.Toolbar toolbars}.
- * Each tool is configured with a static name, title, and icon and is customized with the command to carry
- * out when the tool is selected. Tools must also be registered with a {@link OO.ui.ToolFactory tool factory},
- * which creates the tools on demand.
- *
- * Every Tool subclass must implement two methods:
- *
- * - {@link #onUpdateState}
- * - {@link #onSelect}
- *
- * Tools are added to toolgroups ({@link OO.ui.ListToolGroup ListToolGroup},
- * {@link OO.ui.BarToolGroup BarToolGroup}, or {@link OO.ui.MenuToolGroup MenuToolGroup}), which determine how
- * the tool is displayed in the toolbar. See {@link OO.ui.Toolbar toolbars} for an example.
- *
- * For more information, please see the [OOjs UI documentation on MediaWiki][1].
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
- *
- * @abstract
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.IconElement
- * @mixins OO.ui.mixin.FlaggedElement
- * @mixins OO.ui.mixin.TabIndexedElement
- *
- * @constructor
- * @param {OO.ui.ToolGroup} toolGroup
- * @param {Object} [config] Configuration options
- * @cfg {string|Function} [title] Title text or a function that returns text. If this config is omitted, the value of
- * the {@link #static-title static title} property is used.
- *
- * The title is used in different ways depending on the type of toolgroup that contains the tool. The
- * title is used as a tooltip if the tool is part of a {@link OO.ui.BarToolGroup bar} toolgroup, or as the label text if the tool is
- * part of a {@link OO.ui.ListToolGroup list} or {@link OO.ui.MenuToolGroup menu} toolgroup.
- *
- * For bar toolgroups, a description of the accelerator key is appended to the title if an accelerator key
- * is associated with an action by the same name as the tool and accelerator functionality has been added to the application.
- * To add accelerator key functionality, you must subclass OO.ui.Toolbar and override the {@link OO.ui.Toolbar#getToolAccelerator getToolAccelerator} method.
- */
- OO.ui.Tool = function OoUiTool( toolGroup, config ) {
- // Allow passing positional parameters inside the config object
- if ( OO.isPlainObject( toolGroup ) && config === undefined ) {
- config = toolGroup;
- toolGroup = config.toolGroup;
- }
- // Configuration initialization
- config = config || {};
- // Parent constructor
- OO.ui.Tool.parent.call( this, config );
- // Properties
- this.toolGroup = toolGroup;
- this.toolbar = this.toolGroup.getToolbar();
- this.active = false;
- this.$title = $( '<span>' );
- this.$accel = $( '<span>' );
- this.$link = $( '<a>' );
- this.title = null;
- // Mixin constructors
- OO.ui.mixin.IconElement.call( this, config );
- OO.ui.mixin.FlaggedElement.call( this, config );
- OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$link } ) );
- // Events
- this.toolbar.connect( this, { updateState: 'onUpdateState' } );
- // Initialization
- this.$title.addClass( 'oo-ui-tool-title' );
- this.$accel
- .addClass( 'oo-ui-tool-accel' )
- .prop( {
- // This may need to be changed if the key names are ever localized,
- // but for now they are essentially written in English
- dir: 'ltr',
- lang: 'en'
- } );
- this.$link
- .addClass( 'oo-ui-tool-link' )
- .append( this.$icon, this.$title, this.$accel )
- .attr( 'role', 'button' );
- this.$element
- .data( 'oo-ui-tool', this )
- .addClass(
- 'oo-ui-tool ' + 'oo-ui-tool-name-' +
- this.constructor.static.name.replace( /^([^\/]+)\/([^\/]+).*$/, '$1-$2' )
- )
- .toggleClass( 'oo-ui-tool-with-label', this.constructor.static.displayBothIconAndLabel )
- .append( this.$link );
- this.setTitle( config.title || this.constructor.static.title );
- };
- /* Setup */
- OO.inheritClass( OO.ui.Tool, OO.ui.Widget );
- OO.mixinClass( OO.ui.Tool, OO.ui.mixin.IconElement );
- OO.mixinClass( OO.ui.Tool, OO.ui.mixin.FlaggedElement );
- OO.mixinClass( OO.ui.Tool, OO.ui.mixin.TabIndexedElement );
- /* Static Properties */
- /**
- * @static
- * @inheritdoc
- */
- OO.ui.Tool.static.tagName = 'span';
- /**
- * Symbolic name of tool.
- *
- * The symbolic name is used internally to register the tool with a {@link OO.ui.ToolFactory ToolFactory}. It can
- * also be used when adding tools to toolgroups.
- *
- * @abstract
- * @static
- * @inheritable
- * @property {string}
- */
- OO.ui.Tool.static.name = '';
- /**
- * Symbolic name of the group.
- *
- * The group name is used to associate tools with each other so that they can be selected later by
- * a {@link OO.ui.ToolGroup toolgroup}.
- *
- * @abstract
- * @static
- * @inheritable
- * @property {string}
- */
- OO.ui.Tool.static.group = '';
- /**
- * Tool title text or a function that returns title text. The value of the static property is overridden if the #title config option is used.
- *
- * @abstract
- * @static
- * @inheritable
- * @property {string|Function}
- */
- OO.ui.Tool.static.title = '';
- /**
- * Display both icon and label when the tool is used in a {@link OO.ui.BarToolGroup bar} toolgroup.
- * Normally only the icon is displayed, or only the label if no icon is given.
- *
- * @static
- * @inheritable
- * @property {boolean}
- */
- OO.ui.Tool.static.displayBothIconAndLabel = false;
- /**
- * Add tool to catch-all groups automatically.
- *
- * A catch-all group, which contains all tools that do not currently belong to a toolgroup,
- * can be included in a toolgroup using the wildcard selector, an asterisk (*).
- *
- * @static
- * @inheritable
- * @property {boolean}
- */
- OO.ui.Tool.static.autoAddToCatchall = true;
- /**
- * Add tool to named groups automatically.
- *
- * By default, tools that are configured with a static ‘group’ property are added
- * to that group and will be selected when the symbolic name of the group is specified (e.g., when
- * toolgroups include tools by group name).
- *
- * @static
- * @property {boolean}
- * @inheritable
- */
- OO.ui.Tool.static.autoAddToGroup = true;
- /**
- * Check if this tool is compatible with given data.
- *
- * This is a stub that can be overridden to provide support for filtering tools based on an
- * arbitrary piece of information (e.g., where the cursor is in a document). The implementation
- * must also call this method so that the compatibility check can be performed.
- *
- * @static
- * @inheritable
- * @param {Mixed} data Data to check
- * @return {boolean} Tool can be used with data
- */
- OO.ui.Tool.static.isCompatibleWith = function () {
- return false;
- };
- /* Methods */
- /**
- * Handle the toolbar state being updated. This method is called when the
- * {@link OO.ui.Toolbar#event-updateState 'updateState' event} is emitted on the
- * {@link OO.ui.Toolbar Toolbar} that uses this tool, and should set the state of this tool
- * depending on application state (usually by calling #setDisabled to enable or disable the tool,
- * or #setActive to mark is as currently in-use or not).
- *
- * This is an abstract method that must be overridden in a concrete subclass.
- *
- * @method
- * @protected
- * @abstract
- */
- OO.ui.Tool.prototype.onUpdateState = null;
- /**
- * Handle the tool being selected. This method is called when the user triggers this tool,
- * usually by clicking on its label/icon.
- *
- * This is an abstract method that must be overridden in a concrete subclass.
- *
- * @method
- * @protected
- * @abstract
- */
- OO.ui.Tool.prototype.onSelect = null;
- /**
- * Check if the tool is active.
- *
- * Tools become active when their #onSelect or #onUpdateState handlers change them to appear pressed
- * with the #setActive method. Additional CSS is applied to the tool to reflect the active state.
- *
- * @return {boolean} Tool is active
- */
- OO.ui.Tool.prototype.isActive = function () {
- return this.active;
- };
- /**
- * Make the tool appear active or inactive.
- *
- * This method should be called within #onSelect or #onUpdateState event handlers to make the tool
- * appear pressed or not.
- *
- * @param {boolean} state Make tool appear active
- */
- OO.ui.Tool.prototype.setActive = function ( state ) {
- this.active = !!state;
- if ( this.active ) {
- this.$element.addClass( 'oo-ui-tool-active' );
- this.setFlags( { progressive: true } );
- } else {
- this.$element.removeClass( 'oo-ui-tool-active' );
- this.setFlags( { progressive: false } );
- }
- };
- /**
- * Set the tool #title.
- *
- * @param {string|Function} title Title text or a function that returns text
- * @chainable
- */
- OO.ui.Tool.prototype.setTitle = function ( title ) {
- this.title = OO.ui.resolveMsg( title );
- this.updateTitle();
- return this;
- };
- /**
- * Get the tool #title.
- *
- * @return {string} Title text
- */
- OO.ui.Tool.prototype.getTitle = function () {
- return this.title;
- };
- /**
- * Get the tool's symbolic name.
- *
- * @return {string} Symbolic name of tool
- */
- OO.ui.Tool.prototype.getName = function () {
- return this.constructor.static.name;
- };
- /**
- * Update the title.
- */
- OO.ui.Tool.prototype.updateTitle = function () {
- var titleTooltips = this.toolGroup.constructor.static.titleTooltips,
- accelTooltips = this.toolGroup.constructor.static.accelTooltips,
- accel = this.toolbar.getToolAccelerator( this.constructor.static.name ),
- tooltipParts = [];
- this.$title.text( this.title );
- this.$accel.text( accel );
- if ( titleTooltips && typeof this.title === 'string' && this.title.length ) {
- tooltipParts.push( this.title );
- }
- if ( accelTooltips && typeof accel === 'string' && accel.length ) {
- tooltipParts.push( accel );
- }
- if ( tooltipParts.length ) {
- this.$link.attr( 'title', tooltipParts.join( ' ' ) );
- } else {
- this.$link.removeAttr( 'title' );
- }
- };
- /**
- * Destroy tool.
- *
- * Destroying the tool removes all event handlers and the tool’s DOM elements.
- * Call this method whenever you are done using a tool.
- */
- OO.ui.Tool.prototype.destroy = function () {
- this.toolbar.disconnect( this );
- this.$element.remove();
- };
- /**
- * ToolGroups are collections of {@link OO.ui.Tool tools} that are used in a {@link OO.ui.Toolbar toolbar}.
- * The type of toolgroup ({@link OO.ui.ListToolGroup list}, {@link OO.ui.BarToolGroup bar}, or {@link OO.ui.MenuToolGroup menu})
- * to which a tool belongs determines how the tool is arranged and displayed in the toolbar. Toolgroups
- * themselves are created on demand with a {@link OO.ui.ToolGroupFactory toolgroup factory}.
- *
- * Toolgroups can contain individual tools, groups of tools, or all available tools, as specified
- * using the `include` config option. See OO.ui.ToolFactory#extract on documentation of the format.
- * The options `exclude`, `promote`, and `demote` support the same formats.
- *
- * See {@link OO.ui.Toolbar toolbars} for a full example. For more information about toolbars in general,
- * please see the [OOjs UI documentation on MediaWiki][1].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
- *
- * @abstract
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.GroupElement
- *
- * @constructor
- * @param {OO.ui.Toolbar} toolbar
- * @param {Object} [config] Configuration options
- * @cfg {Array|string} [include] List of tools to include in the toolgroup, see above.
- * @cfg {Array|string} [exclude] List of tools to exclude from the toolgroup, see above.
- * @cfg {Array|string} [promote] List of tools to promote to the beginning of the toolgroup, see above.
- * @cfg {Array|string} [demote] List of tools to demote to the end of the toolgroup, see above.
- * This setting is particularly useful when tools have been added to the toolgroup
- * en masse (e.g., via the catch-all selector).
- */
- OO.ui.ToolGroup = function OoUiToolGroup( toolbar, config ) {
- // Allow passing positional parameters inside the config object
- if ( OO.isPlainObject( toolbar ) && config === undefined ) {
- config = toolbar;
- toolbar = config.toolbar;
- }
- // Configuration initialization
- config = config || {};
- // Parent constructor
- OO.ui.ToolGroup.parent.call( this, config );
- // Mixin constructors
- OO.ui.mixin.GroupElement.call( this, config );
- // Properties
- this.toolbar = toolbar;
- this.tools = {};
- this.pressed = null;
- this.autoDisabled = false;
- this.include = config.include || [];
- this.exclude = config.exclude || [];
- this.promote = config.promote || [];
- this.demote = config.demote || [];
- this.onCapturedMouseKeyUpHandler = this.onCapturedMouseKeyUp.bind( this );
- // Events
- this.$element.on( {
- mousedown: this.onMouseKeyDown.bind( this ),
- mouseup: this.onMouseKeyUp.bind( this ),
- keydown: this.onMouseKeyDown.bind( this ),
- keyup: this.onMouseKeyUp.bind( this ),
- focus: this.onMouseOverFocus.bind( this ),
- blur: this.onMouseOutBlur.bind( this ),
- mouseover: this.onMouseOverFocus.bind( this ),
- mouseout: this.onMouseOutBlur.bind( this )
- } );
- this.toolbar.getToolFactory().connect( this, { register: 'onToolFactoryRegister' } );
- this.aggregate( { disable: 'itemDisable' } );
- this.connect( this, { itemDisable: 'updateDisabled' } );
- // Initialization
- this.$group.addClass( 'oo-ui-toolGroup-tools' );
- this.$element
- .addClass( 'oo-ui-toolGroup' )
- .append( this.$group );
- this.populate();
- };
- /* Setup */
- OO.inheritClass( OO.ui.ToolGroup, OO.ui.Widget );
- OO.mixinClass( OO.ui.ToolGroup, OO.ui.mixin.GroupElement );
- /* Events */
- /**
- * @event update
- */
- /* Static Properties */
- /**
- * Show labels in tooltips.
- *
- * @static
- * @inheritable
- * @property {boolean}
- */
- OO.ui.ToolGroup.static.titleTooltips = false;
- /**
- * Show acceleration labels in tooltips.
- *
- * Note: The OOjs UI library does not include an accelerator system, but does contain
- * a hook for one. To use an accelerator system, subclass the {@link OO.ui.Toolbar toolbar} and
- * override the {@link OO.ui.Toolbar#getToolAccelerator getToolAccelerator} method, which is
- * meant to return a label that describes the accelerator keys for a given tool (e.g., 'Ctrl + M').
- *
- * @static
- * @inheritable
- * @property {boolean}
- */
- OO.ui.ToolGroup.static.accelTooltips = false;
- /**
- * Automatically disable the toolgroup when all tools are disabled
- *
- * @static
- * @inheritable
- * @property {boolean}
- */
- OO.ui.ToolGroup.static.autoDisable = true;
- /**
- * @abstract
- * @static
- * @inheritable
- * @property {string}
- */
- OO.ui.ToolGroup.static.name = null;
- /* Methods */
- /**
- * @inheritdoc
- */
- OO.ui.ToolGroup.prototype.isDisabled = function () {
- return this.autoDisabled || OO.ui.ToolGroup.parent.prototype.isDisabled.apply( this, arguments );
- };
- /**
- * @inheritdoc
- */
- OO.ui.ToolGroup.prototype.updateDisabled = function () {
- var i, item, allDisabled = true;
- if ( this.constructor.static.autoDisable ) {
- for ( i = this.items.length - 1; i >= 0; i-- ) {
- item = this.items[ i ];
- if ( !item.isDisabled() ) {
- allDisabled = false;
- break;
- }
- }
- this.autoDisabled = allDisabled;
- }
- OO.ui.ToolGroup.parent.prototype.updateDisabled.apply( this, arguments );
- };
- /**
- * Handle mouse down and key down events.
- *
- * @protected
- * @param {jQuery.Event} e Mouse down or key down event
- */
- OO.ui.ToolGroup.prototype.onMouseKeyDown = function ( e ) {
- if (
- !this.isDisabled() &&
- ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
- ) {
- this.pressed = this.getTargetTool( e );
- if ( this.pressed ) {
- this.pressed.setActive( true );
- this.getElementDocument().addEventListener( 'mouseup', this.onCapturedMouseKeyUpHandler, true );
- this.getElementDocument().addEventListener( 'keyup', this.onCapturedMouseKeyUpHandler, true );
- }
- return false;
- }
- };
- /**
- * Handle captured mouse up and key up events.
- *
- * @protected
- * @param {MouseEvent|KeyboardEvent} e Mouse up or key up event
- */
- OO.ui.ToolGroup.prototype.onCapturedMouseKeyUp = function ( e ) {
- this.getElementDocument().removeEventListener( 'mouseup', this.onCapturedMouseKeyUpHandler, true );
- this.getElementDocument().removeEventListener( 'keyup', this.onCapturedMouseKeyUpHandler, true );
- // onMouseKeyUp may be called a second time, depending on where the mouse is when the button is
- // released, but since `this.pressed` will no longer be true, the second call will be ignored.
- this.onMouseKeyUp( e );
- };
- /**
- * Handle mouse up and key up events.
- *
- * @protected
- * @param {MouseEvent|KeyboardEvent} e Mouse up or key up event
- */
- OO.ui.ToolGroup.prototype.onMouseKeyUp = function ( e ) {
- var tool = this.getTargetTool( e );
- if (
- !this.isDisabled() && this.pressed && this.pressed === tool &&
- ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
- ) {
- this.pressed.onSelect();
- this.pressed = null;
- e.preventDefault();
- e.stopPropagation();
- }
- this.pressed = null;
- };
- /**
- * Handle mouse over and focus events.
- *
- * @protected
- * @param {jQuery.Event} e Mouse over or focus event
- */
- OO.ui.ToolGroup.prototype.onMouseOverFocus = function ( e ) {
- var tool = this.getTargetTool( e );
- if ( this.pressed && this.pressed === tool ) {
- this.pressed.setActive( true );
- }
- };
- /**
- * Handle mouse out and blur events.
- *
- * @protected
- * @param {jQuery.Event} e Mouse out or blur event
- */
- OO.ui.ToolGroup.prototype.onMouseOutBlur = function ( e ) {
- var tool = this.getTargetTool( e );
- if ( this.pressed && this.pressed === tool ) {
- this.pressed.setActive( false );
- }
- };
- /**
- * Get the closest tool to a jQuery.Event.
- *
- * Only tool links are considered, which prevents other elements in the tool such as popups from
- * triggering tool group interactions.
- *
- * @private
- * @param {jQuery.Event} e
- * @return {OO.ui.Tool|null} Tool, `null` if none was found
- */
- OO.ui.ToolGroup.prototype.getTargetTool = function ( e ) {
- var tool,
- $item = $( e.target ).closest( '.oo-ui-tool-link' );
- if ( $item.length ) {
- tool = $item.parent().data( 'oo-ui-tool' );
- }
- return tool && !tool.isDisabled() ? tool : null;
- };
- /**
- * Handle tool registry register events.
- *
- * If a tool is registered after the group is created, we must repopulate the list to account for:
- *
- * - a tool being added that may be included
- * - a tool already included being overridden
- *
- * @protected
- * @param {string} name Symbolic name of tool
- */
- OO.ui.ToolGroup.prototype.onToolFactoryRegister = function () {
- this.populate();
- };
- /**
- * Get the toolbar that contains the toolgroup.
- *
- * @return {OO.ui.Toolbar} Toolbar that contains the toolgroup
- */
- OO.ui.ToolGroup.prototype.getToolbar = function () {
- return this.toolbar;
- };
- /**
- * Add and remove tools based on configuration.
- */
- OO.ui.ToolGroup.prototype.populate = function () {
- var i, len, name, tool,
- toolFactory = this.toolbar.getToolFactory(),
- names = {},
- add = [],
- remove = [],
- list = this.toolbar.getToolFactory().getTools(
- this.include, this.exclude, this.promote, this.demote
- );
- // Build a list of needed tools
- for ( i = 0, len = list.length; i < len; i++ ) {
- name = list[ i ];
- if (
- // Tool exists
- toolFactory.lookup( name ) &&
- // Tool is available or is already in this group
- ( this.toolbar.isToolAvailable( name ) || this.tools[ name ] )
- ) {
- // Hack to prevent infinite recursion via ToolGroupTool. We need to reserve the tool before
- // creating it, but we can't call reserveTool() yet because we haven't created the tool.
- this.toolbar.tools[ name ] = true;
- tool = this.tools[ name ];
- if ( !tool ) {
- // Auto-initialize tools on first use
- this.tools[ name ] = tool = toolFactory.create( name, this );
- tool.updateTitle();
- }
- this.toolbar.reserveTool( tool );
- add.push( tool );
- names[ name ] = true;
- }
- }
- // Remove tools that are no longer needed
- for ( name in this.tools ) {
- if ( !names[ name ] ) {
- this.tools[ name ].destroy();
- this.toolbar.releaseTool( this.tools[ name ] );
- remove.push( this.tools[ name ] );
- delete this.tools[ name ];
- }
- }
- if ( remove.length ) {
- this.removeItems( remove );
- }
- // Update emptiness state
- if ( add.length ) {
- this.$element.removeClass( 'oo-ui-toolGroup-empty' );
- } else {
- this.$element.addClass( 'oo-ui-toolGroup-empty' );
- }
- // Re-add tools (moving existing ones to new locations)
- this.addItems( add );
- // Disabled state may depend on items
- this.updateDisabled();
- };
- /**
- * Destroy toolgroup.
- */
- OO.ui.ToolGroup.prototype.destroy = function () {
- var name;
- this.clearItems();
- this.toolbar.getToolFactory().disconnect( this );
- for ( name in this.tools ) {
- this.toolbar.releaseTool( this.tools[ name ] );
- this.tools[ name ].disconnect( this ).destroy();
- delete this.tools[ name ];
- }
- this.$element.remove();
- };
- /**
- * A ToolFactory creates tools on demand. All tools ({@link OO.ui.Tool Tools}, {@link OO.ui.PopupTool PopupTools},
- * and {@link OO.ui.ToolGroupTool ToolGroupTools}) must be registered with a tool factory. Tools are
- * registered by their symbolic name. See {@link OO.ui.Toolbar toolbars} for an example.
- *
- * For more information about toolbars in general, please see the [OOjs UI documentation on MediaWiki][1].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
- *
- * @class
- * @extends OO.Factory
- * @constructor
- */
- OO.ui.ToolFactory = function OoUiToolFactory() {
- // Parent constructor
- OO.ui.ToolFactory.parent.call( this );
- };
- /* Setup */
- OO.inheritClass( OO.ui.ToolFactory, OO.Factory );
- /* Methods */
- /**
- * Get tools from the factory
- *
- * @param {Array|string} [include] Included tools, see #extract for format
- * @param {Array|string} [exclude] Excluded tools, see #extract for format
- * @param {Array|string} [promote] Promoted tools, see #extract for format
- * @param {Array|string} [demote] Demoted tools, see #extract for format
- * @return {string[]} List of tools
- */
- OO.ui.ToolFactory.prototype.getTools = function ( include, exclude, promote, demote ) {
- var i, len, included, promoted, demoted,
- auto = [],
- used = {};
- // Collect included and not excluded tools
- included = OO.simpleArrayDifference( this.extract( include ), this.extract( exclude ) );
- // Promotion
- promoted = this.extract( promote, used );
- demoted = this.extract( demote, used );
- // Auto
- for ( i = 0, len = included.length; i < len; i++ ) {
- if ( !used[ included[ i ] ] ) {
- auto.push( included[ i ] );
- }
- }
- return promoted.concat( auto ).concat( demoted );
- };
- /**
- * Get a flat list of names from a list of names or groups.
- *
- * Normally, `collection` is an array of tool specifications. Tools can be specified in the
- * following ways:
- *
- * - To include an individual tool, use the symbolic name: `{ name: 'tool-name' }` or `'tool-name'`.
- * - To include all tools in a group, use the group name: `{ group: 'group-name' }`. (To assign the
- * tool to a group, use OO.ui.Tool.static.group.)
- *
- * Alternatively, to include all tools that are not yet assigned to any other toolgroup, use the
- * catch-all selector `'*'`.
- *
- * If `used` is passed, tool names that appear as properties in this object will be considered
- * already assigned, and will not be returned even if specified otherwise. The tool names extracted
- * by this function call will be added as new properties in the object.
- *
- * @private
- * @param {Array|string} collection List of tools, see above
- * @param {Object} [used] Object containing information about used tools, see above
- * @return {string[]} List of extracted tool names
- */
- OO.ui.ToolFactory.prototype.extract = function ( collection, used ) {
- var i, len, item, name, tool,
- names = [];
- collection = !Array.isArray( collection ) ? [ collection ] : collection;
- for ( i = 0, len = collection.length; i < len; i++ ) {
- item = collection[ i ];
- if ( item === '*' ) {
- for ( name in this.registry ) {
- tool = this.registry[ name ];
- if (
- // Only add tools by group name when auto-add is enabled
- tool.static.autoAddToCatchall &&
- // Exclude already used tools
- ( !used || !used[ name ] )
- ) {
- names.push( name );
- if ( used ) {
- used[ name ] = true;
- }
- }
- }
- } else {
- // Allow plain strings as shorthand for named tools
- if ( typeof item === 'string' ) {
- item = { name: item };
- }
- if ( OO.isPlainObject( item ) ) {
- if ( item.group ) {
- for ( name in this.registry ) {
- tool = this.registry[ name ];
- if (
- // Include tools with matching group
- tool.static.group === item.group &&
- // Only add tools by group name when auto-add is enabled
- tool.static.autoAddToGroup &&
- // Exclude already used tools
- ( !used || !used[ name ] )
- ) {
- names.push( name );
- if ( used ) {
- used[ name ] = true;
- }
- }
- }
- // Include tools with matching name and exclude already used tools
- } else if ( item.name && ( !used || !used[ item.name ] ) ) {
- names.push( item.name );
- if ( used ) {
- used[ item.name ] = true;
- }
- }
- }
- }
- }
- return names;
- };
- /**
- * ToolGroupFactories create {@link OO.ui.ToolGroup toolgroups} on demand. The toolgroup classes must
- * specify a symbolic name and be registered with the factory. The following classes are registered by
- * default:
- *
- * - {@link OO.ui.BarToolGroup BarToolGroups} (‘bar’)
- * - {@link OO.ui.MenuToolGroup MenuToolGroups} (‘menu’)
- * - {@link OO.ui.ListToolGroup ListToolGroups} (‘list’)
- *
- * See {@link OO.ui.Toolbar toolbars} for an example.
- *
- * For more information about toolbars in general, please see the [OOjs UI documentation on MediaWiki][1].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
- *
- * @class
- * @extends OO.Factory
- * @constructor
- */
- OO.ui.ToolGroupFactory = function OoUiToolGroupFactory() {
- var i, l, defaultClasses;
- // Parent constructor
- OO.Factory.call( this );
- defaultClasses = this.constructor.static.getDefaultClasses();
- // Register default toolgroups
- for ( i = 0, l = defaultClasses.length; i < l; i++ ) {
- this.register( defaultClasses[ i ] );
- }
- };
- /* Setup */
- OO.inheritClass( OO.ui.ToolGroupFactory, OO.Factory );
- /* Static Methods */
- /**
- * Get a default set of classes to be registered on construction.
- *
- * @return {Function[]} Default classes
- */
- OO.ui.ToolGroupFactory.static.getDefaultClasses = function () {
- return [
- OO.ui.BarToolGroup,
- OO.ui.ListToolGroup,
- OO.ui.MenuToolGroup
- ];
- };
- /**
- * Popup tools open a popup window when they are selected from the {@link OO.ui.Toolbar toolbar}. Each popup tool is configured
- * with a static name, title, and icon, as well with as any popup configurations. Unlike other tools, popup tools do not require that developers specify
- * an #onSelect or #onUpdateState method, as these methods have been implemented already.
- *
- * // Example of a popup tool. When selected, a popup tool displays
- * // a popup window.
- * function HelpTool( toolGroup, config ) {
- * OO.ui.PopupTool.call( this, toolGroup, $.extend( { popup: {
- * padded: true,
- * label: 'Help',
- * head: true
- * } }, config ) );
- * this.popup.$body.append( '<p>I am helpful!</p>' );
- * };
- * OO.inheritClass( HelpTool, OO.ui.PopupTool );
- * HelpTool.static.name = 'help';
- * HelpTool.static.icon = 'help';
- * HelpTool.static.title = 'Help';
- * toolFactory.register( HelpTool );
- *
- * For an example of a toolbar that contains a popup tool, see {@link OO.ui.Toolbar toolbars}. For more information about
- * toolbars in genreral, please see the [OOjs UI documentation on MediaWiki][1].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
- *
- * @abstract
- * @class
- * @extends OO.ui.Tool
- * @mixins OO.ui.mixin.PopupElement
- *
- * @constructor
- * @param {OO.ui.ToolGroup} toolGroup
- * @param {Object} [config] Configuration options
- */
- OO.ui.PopupTool = function OoUiPopupTool( toolGroup, config ) {
- // Allow passing positional parameters inside the config object
- if ( OO.isPlainObject( toolGroup ) && config === undefined ) {
- config = toolGroup;
- toolGroup = config.toolGroup;
- }
- // Parent constructor
- OO.ui.PopupTool.parent.call( this, toolGroup, config );
- // Mixin constructors
- OO.ui.mixin.PopupElement.call( this, config );
- // Initialization
- this.popup.setPosition( toolGroup.getToolbar().position === 'bottom' ? 'above' : 'below' );
- this.$element
- .addClass( 'oo-ui-popupTool' )
- .append( this.popup.$element );
- };
- /* Setup */
- OO.inheritClass( OO.ui.PopupTool, OO.ui.Tool );
- OO.mixinClass( OO.ui.PopupTool, OO.ui.mixin.PopupElement );
- /* Methods */
- /**
- * Handle the tool being selected.
- *
- * @inheritdoc
- */
- OO.ui.PopupTool.prototype.onSelect = function () {
- if ( !this.isDisabled() ) {
- this.popup.toggle();
- }
- this.setActive( false );
- return false;
- };
- /**
- * Handle the toolbar state being updated.
- *
- * @inheritdoc
- */
- OO.ui.PopupTool.prototype.onUpdateState = function () {
- this.setActive( false );
- };
- /**
- * A ToolGroupTool is a special sort of tool that can contain other {@link OO.ui.Tool tools}
- * and {@link OO.ui.ToolGroup toolgroups}. The ToolGroupTool was specifically designed to be used
- * inside a {@link OO.ui.BarToolGroup bar} toolgroup to provide access to additional tools from
- * the bar item. Included tools will be displayed in a dropdown {@link OO.ui.ListToolGroup list}
- * when the ToolGroupTool is selected.
- *
- * // Example: ToolGroupTool with two nested tools, 'setting1' and 'setting2', defined elsewhere.
- *
- * function SettingsTool() {
- * SettingsTool.parent.apply( this, arguments );
- * };
- * OO.inheritClass( SettingsTool, OO.ui.ToolGroupTool );
- * SettingsTool.static.name = 'settings';
- * SettingsTool.static.title = 'Change settings';
- * SettingsTool.static.groupConfig = {
- * icon: 'settings',
- * label: 'ToolGroupTool',
- * include: [ 'setting1', 'setting2' ]
- * };
- * toolFactory.register( SettingsTool );
- *
- * For more information, please see the [OOjs UI documentation on MediaWiki][1].
- *
- * Please note that this implementation is subject to change per [T74159] [2].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars#ToolGroupTool
- * [2]: https://phabricator.wikimedia.org/T74159
- *
- * @abstract
- * @class
- * @extends OO.ui.Tool
- *
- * @constructor
- * @param {OO.ui.ToolGroup} toolGroup
- * @param {Object} [config] Configuration options
- */
- OO.ui.ToolGroupTool = function OoUiToolGroupTool( toolGroup, config ) {
- // Allow passing positional parameters inside the config object
- if ( OO.isPlainObject( toolGroup ) && config === undefined ) {
- config = toolGroup;
- toolGroup = config.toolGroup;
- }
- // Parent constructor
- OO.ui.ToolGroupTool.parent.call( this, toolGroup, config );
- // Properties
- this.innerToolGroup = this.createGroup( this.constructor.static.groupConfig );
- // Events
- this.innerToolGroup.connect( this, { disable: 'onToolGroupDisable' } );
- // Initialization
- this.$link.remove();
- this.$element
- .addClass( 'oo-ui-toolGroupTool' )
- .append( this.innerToolGroup.$element );
- };
- /* Setup */
- OO.inheritClass( OO.ui.ToolGroupTool, OO.ui.Tool );
- /* Static Properties */
- /**
- * Toolgroup configuration.
- *
- * The toolgroup configuration consists of the tools to include, as well as an icon and label
- * to use for the bar item. Tools can be included by symbolic name, group, or with the
- * wildcard selector. Please see {@link OO.ui.ToolGroup toolgroup} for more information.
- *
- * @property {Object.<string,Array>}
- */
- OO.ui.ToolGroupTool.static.groupConfig = {};
- /* Methods */
- /**
- * Handle the tool being selected.
- *
- * @inheritdoc
- */
- OO.ui.ToolGroupTool.prototype.onSelect = function () {
- this.innerToolGroup.setActive( !this.innerToolGroup.active );
- return false;
- };
- /**
- * Synchronize disabledness state of the tool with the inner toolgroup.
- *
- * @private
- * @param {boolean} disabled Element is disabled
- */
- OO.ui.ToolGroupTool.prototype.onToolGroupDisable = function ( disabled ) {
- this.setDisabled( disabled );
- };
- /**
- * Handle the toolbar state being updated.
- *
- * @inheritdoc
- */
- OO.ui.ToolGroupTool.prototype.onUpdateState = function () {
- this.setActive( false );
- };
- /**
- * Build a {@link OO.ui.ToolGroup toolgroup} from the specified configuration.
- *
- * @param {Object.<string,Array>} group Toolgroup configuration. Please see {@link OO.ui.ToolGroup toolgroup} for
- * more information.
- * @return {OO.ui.ListToolGroup}
- */
- OO.ui.ToolGroupTool.prototype.createGroup = function ( group ) {
- if ( group.include === '*' ) {
- // Apply defaults to catch-all groups
- if ( group.label === undefined ) {
- group.label = OO.ui.msg( 'ooui-toolbar-more' );
- }
- }
- return this.toolbar.getToolGroupFactory().create( 'list', this.toolbar, group );
- };
- /**
- * BarToolGroups are one of three types of {@link OO.ui.ToolGroup toolgroups} that are used to
- * create {@link OO.ui.Toolbar toolbars} (the other types of groups are {@link OO.ui.MenuToolGroup MenuToolGroup}
- * and {@link OO.ui.ListToolGroup ListToolGroup}). The {@link OO.ui.Tool tools} in a BarToolGroup are
- * displayed by icon in a single row. The title of the tool is displayed when users move the mouse over
- * the tool.
- *
- * BarToolGroups are created by a {@link OO.ui.ToolGroupFactory tool group factory} when the toolbar is
- * set up.
- *
- * @example
- * // Example of a BarToolGroup with two tools
- * var toolFactory = new OO.ui.ToolFactory();
- * var toolGroupFactory = new OO.ui.ToolGroupFactory();
- * var toolbar = new OO.ui.Toolbar( toolFactory, toolGroupFactory );
- *
- * // We will be placing status text in this element when tools are used
- * var $area = $( '<p>' ).text( 'Example of a BarToolGroup with two tools.' );
- *
- * // Define the tools that we're going to place in our toolbar
- *
- * // Create a class inheriting from OO.ui.Tool
- * function SearchTool() {
- * SearchTool.parent.apply( this, arguments );
- * }
- * OO.inheritClass( SearchTool, OO.ui.Tool );
- * // Each tool must have a 'name' (used as an internal identifier, see later) and at least one
- * // of 'icon' and 'title' (displayed icon and text).
- * SearchTool.static.name = 'search';
- * SearchTool.static.icon = 'search';
- * SearchTool.static.title = 'Search...';
- * // Defines the action that will happen when this tool is selected (clicked).
- * SearchTool.prototype.onSelect = function () {
- * $area.text( 'Search tool clicked!' );
- * // Never display this tool as "active" (selected).
- * this.setActive( false );
- * };
- * SearchTool.prototype.onUpdateState = function () {};
- * // Make this tool available in our toolFactory and thus our toolbar
- * toolFactory.register( SearchTool );
- *
- * // This is a PopupTool. Rather than having a custom 'onSelect' action, it will display a
- * // little popup window (a PopupWidget).
- * function HelpTool( toolGroup, config ) {
- * OO.ui.PopupTool.call( this, toolGroup, $.extend( { popup: {
- * padded: true,
- * label: 'Help',
- * head: true
- * } }, config ) );
- * this.popup.$body.append( '<p>I am helpful!</p>' );
- * }
- * OO.inheritClass( HelpTool, OO.ui.PopupTool );
- * HelpTool.static.name = 'help';
- * HelpTool.static.icon = 'help';
- * HelpTool.static.title = 'Help';
- * toolFactory.register( HelpTool );
- *
- * // Finally define which tools and in what order appear in the toolbar. Each tool may only be
- * // used once (but not all defined tools must be used).
- * toolbar.setup( [
- * {
- * // 'bar' tool groups display tools by icon only
- * type: 'bar',
- * include: [ 'search', 'help' ]
- * }
- * ] );
- *
- * // Create some UI around the toolbar and place it in the document
- * var frame = new OO.ui.PanelLayout( {
- * expanded: false,
- * framed: true
- * } );
- * var contentFrame = new OO.ui.PanelLayout( {
- * expanded: false,
- * padded: true
- * } );
- * frame.$element.append(
- * toolbar.$element,
- * contentFrame.$element.append( $area )
- * );
- * $( 'body' ).append( frame.$element );
- *
- * // Here is where the toolbar is actually built. This must be done after inserting it into the
- * // document.
- * toolbar.initialize();
- *
- * For more information about how to add tools to a bar tool group, please see {@link OO.ui.ToolGroup toolgroup}.
- * For more information about toolbars in general, please see the [OOjs UI documentation on MediaWiki][1].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
- *
- * @class
- * @extends OO.ui.ToolGroup
- *
- * @constructor
- * @param {OO.ui.Toolbar} toolbar
- * @param {Object} [config] Configuration options
- */
- OO.ui.BarToolGroup = function OoUiBarToolGroup( toolbar, config ) {
- // Allow passing positional parameters inside the config object
- if ( OO.isPlainObject( toolbar ) && config === undefined ) {
- config = toolbar;
- toolbar = config.toolbar;
- }
- // Parent constructor
- OO.ui.BarToolGroup.parent.call( this, toolbar, config );
- // Initialization
- this.$element.addClass( 'oo-ui-barToolGroup' );
- };
- /* Setup */
- OO.inheritClass( OO.ui.BarToolGroup, OO.ui.ToolGroup );
- /* Static Properties */
- /**
- * @static
- * @inheritdoc
- */
- OO.ui.BarToolGroup.static.titleTooltips = true;
- /**
- * @static
- * @inheritdoc
- */
- OO.ui.BarToolGroup.static.accelTooltips = true;
- /**
- * @static
- * @inheritdoc
- */
- OO.ui.BarToolGroup.static.name = 'bar';
- /**
- * PopupToolGroup is an abstract base class used by both {@link OO.ui.MenuToolGroup MenuToolGroup}
- * and {@link OO.ui.ListToolGroup ListToolGroup} to provide a popup--an overlaid menu or list of tools with an
- * optional icon and label. This class can be used for other base classes that also use this functionality.
- *
- * @abstract
- * @class
- * @extends OO.ui.ToolGroup
- * @mixins OO.ui.mixin.IconElement
- * @mixins OO.ui.mixin.IndicatorElement
- * @mixins OO.ui.mixin.LabelElement
- * @mixins OO.ui.mixin.TitledElement
- * @mixins OO.ui.mixin.FlaggedElement
- * @mixins OO.ui.mixin.ClippableElement
- * @mixins OO.ui.mixin.TabIndexedElement
- *
- * @constructor
- * @param {OO.ui.Toolbar} toolbar
- * @param {Object} [config] Configuration options
- * @cfg {string} [header] Text to display at the top of the popup
- */
- OO.ui.PopupToolGroup = function OoUiPopupToolGroup( toolbar, config ) {
- // Allow passing positional parameters inside the config object
- if ( OO.isPlainObject( toolbar ) && config === undefined ) {
- config = toolbar;
- toolbar = config.toolbar;
- }
- // Configuration initialization
- config = $.extend( {
- indicator: config.indicator === undefined ? ( toolbar.position === 'bottom' ? 'up' : 'down' ) : config.indicator
- }, config );
- // Parent constructor
- OO.ui.PopupToolGroup.parent.call( this, toolbar, config );
- // Properties
- this.active = false;
- this.dragging = false;
- this.onBlurHandler = this.onBlur.bind( this );
- this.$handle = $( '<span>' );
- // Mixin constructors
- OO.ui.mixin.IconElement.call( this, config );
- OO.ui.mixin.IndicatorElement.call( this, config );
- OO.ui.mixin.LabelElement.call( this, config );
- OO.ui.mixin.TitledElement.call( this, config );
- OO.ui.mixin.FlaggedElement.call( this, config );
- OO.ui.mixin.ClippableElement.call( this, $.extend( {}, config, { $clippable: this.$group } ) );
- OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$handle } ) );
- // Events
- this.$handle.on( {
- keydown: this.onHandleMouseKeyDown.bind( this ),
- keyup: this.onHandleMouseKeyUp.bind( this ),
- mousedown: this.onHandleMouseKeyDown.bind( this ),
- mouseup: this.onHandleMouseKeyUp.bind( this )
- } );
- // Initialization
- this.$handle
- .addClass( 'oo-ui-popupToolGroup-handle' )
- .attr( 'role', 'button' )
- .append( this.$icon, this.$label, this.$indicator );
- // If the pop-up should have a header, add it to the top of the toolGroup.
- // Note: If this feature is useful for other widgets, we could abstract it into an
- // OO.ui.HeaderedElement mixin constructor.
- if ( config.header !== undefined ) {
- this.$group
- .prepend( $( '<span>' )
- .addClass( 'oo-ui-popupToolGroup-header' )
- .text( config.header )
- );
- }
- this.$element
- .addClass( 'oo-ui-popupToolGroup' )
- .prepend( this.$handle );
- };
- /* Setup */
- OO.inheritClass( OO.ui.PopupToolGroup, OO.ui.ToolGroup );
- OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.mixin.IconElement );
- OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.mixin.IndicatorElement );
- OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.mixin.LabelElement );
- OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.mixin.TitledElement );
- OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.mixin.FlaggedElement );
- OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.mixin.ClippableElement );
- OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.mixin.TabIndexedElement );
- /* Methods */
- /**
- * @inheritdoc
- */
- OO.ui.PopupToolGroup.prototype.setDisabled = function () {
- // Parent method
- OO.ui.PopupToolGroup.parent.prototype.setDisabled.apply( this, arguments );
- if ( this.isDisabled() && this.isElementAttached() ) {
- this.setActive( false );
- }
- };
- /**
- * Handle focus being lost.
- *
- * The event is actually generated from a mouseup/keyup, so it is not a normal blur event object.
- *
- * @protected
- * @param {MouseEvent|KeyboardEvent} e Mouse up or key up event
- */
- OO.ui.PopupToolGroup.prototype.onBlur = function ( e ) {
- // Only deactivate when clicking outside the dropdown element
- if ( $( e.target ).closest( '.oo-ui-popupToolGroup' )[ 0 ] !== this.$element[ 0 ] ) {
- this.setActive( false );
- }
- };
- /**
- * @inheritdoc
- */
- OO.ui.PopupToolGroup.prototype.onMouseKeyUp = function ( e ) {
- // Only close toolgroup when a tool was actually selected
- if (
- !this.isDisabled() && this.pressed && this.pressed === this.getTargetTool( e ) &&
- ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
- ) {
- this.setActive( false );
- }
- return OO.ui.PopupToolGroup.parent.prototype.onMouseKeyUp.call( this, e );
- };
- /**
- * Handle mouse up and key up events.
- *
- * @protected
- * @param {jQuery.Event} e Mouse up or key up event
- */
- OO.ui.PopupToolGroup.prototype.onHandleMouseKeyUp = function ( e ) {
- if (
- !this.isDisabled() &&
- ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
- ) {
- return false;
- }
- };
- /**
- * Handle mouse down and key down events.
- *
- * @protected
- * @param {jQuery.Event} e Mouse down or key down event
- */
- OO.ui.PopupToolGroup.prototype.onHandleMouseKeyDown = function ( e ) {
- if (
- !this.isDisabled() &&
- ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
- ) {
- this.setActive( !this.active );
- return false;
- }
- };
- /**
- * Switch into 'active' mode.
- *
- * When active, the popup is visible. A mouseup event anywhere in the document will trigger
- * deactivation.
- *
- * @param {boolean} value The active state to set
- */
- OO.ui.PopupToolGroup.prototype.setActive = function ( value ) {
- var containerWidth, containerLeft;
- value = !!value;
- if ( this.active !== value ) {
- this.active = value;
- if ( value ) {
- this.getElementDocument().addEventListener( 'mouseup', this.onBlurHandler, true );
- this.getElementDocument().addEventListener( 'keyup', this.onBlurHandler, true );
- this.$clippable.css( 'left', '' );
- // Try anchoring the popup to the left first
- this.$element.addClass( 'oo-ui-popupToolGroup-active oo-ui-popupToolGroup-left' );
- this.toggleClipping( true );
- if ( this.isClippedHorizontally() ) {
- // Anchoring to the left caused the popup to clip, so anchor it to the right instead
- this.toggleClipping( false );
- this.$element
- .removeClass( 'oo-ui-popupToolGroup-left' )
- .addClass( 'oo-ui-popupToolGroup-right' );
- this.toggleClipping( true );
- }
- if ( this.isClippedHorizontally() ) {
- // Anchoring to the right also caused the popup to clip, so just make it fill the container
- containerWidth = this.$clippableScrollableContainer.width();
- containerLeft = this.$clippableScrollableContainer.offset().left;
- this.toggleClipping( false );
- this.$element.removeClass( 'oo-ui-popupToolGroup-right' );
- this.$clippable.css( {
- left: -( this.$element.offset().left - containerLeft ),
- width: containerWidth
- } );
- }
- } else {
- this.getElementDocument().removeEventListener( 'mouseup', this.onBlurHandler, true );
- this.getElementDocument().removeEventListener( 'keyup', this.onBlurHandler, true );
- this.$element.removeClass(
- 'oo-ui-popupToolGroup-active oo-ui-popupToolGroup-left oo-ui-popupToolGroup-right'
- );
- this.toggleClipping( false );
- }
- }
- };
- /**
- * ListToolGroups are one of three types of {@link OO.ui.ToolGroup toolgroups} that are used to
- * create {@link OO.ui.Toolbar toolbars} (the other types of groups are {@link OO.ui.MenuToolGroup MenuToolGroup}
- * and {@link OO.ui.BarToolGroup BarToolGroup}). The {@link OO.ui.Tool tools} in a ListToolGroup are displayed
- * by label in a dropdown menu. The title of the tool is used as the label text. The menu itself can be configured
- * with a label, icon, indicator, header, and title.
- *
- * ListToolGroups can be configured to be expanded and collapsed. Collapsed lists will have a ‘More’ option that
- * users can select to see the full list of tools. If a collapsed toolgroup is expanded, a ‘Fewer’ option permits
- * users to collapse the list again.
- *
- * ListToolGroups are created by a {@link OO.ui.ToolGroupFactory toolgroup factory} when the toolbar is set up. The factory
- * requires the ListToolGroup's symbolic name, 'list', which is specified along with the other configurations. For more
- * information about how to add tools to a ListToolGroup, please see {@link OO.ui.ToolGroup toolgroup}.
- *
- * @example
- * // Example of a ListToolGroup
- * var toolFactory = new OO.ui.ToolFactory();
- * var toolGroupFactory = new OO.ui.ToolGroupFactory();
- * var toolbar = new OO.ui.Toolbar( toolFactory, toolGroupFactory );
- *
- * // Configure and register two tools
- * function SettingsTool() {
- * SettingsTool.parent.apply( this, arguments );
- * }
- * OO.inheritClass( SettingsTool, OO.ui.Tool );
- * SettingsTool.static.name = 'settings';
- * SettingsTool.static.icon = 'settings';
- * SettingsTool.static.title = 'Change settings';
- * SettingsTool.prototype.onSelect = function () {
- * this.setActive( false );
- * };
- * SettingsTool.prototype.onUpdateState = function () {};
- * toolFactory.register( SettingsTool );
- * // Register two more tools, nothing interesting here
- * function StuffTool() {
- * StuffTool.parent.apply( this, arguments );
- * }
- * OO.inheritClass( StuffTool, OO.ui.Tool );
- * StuffTool.static.name = 'stuff';
- * StuffTool.static.icon = 'search';
- * StuffTool.static.title = 'Change the world';
- * StuffTool.prototype.onSelect = function () {
- * this.setActive( false );
- * };
- * StuffTool.prototype.onUpdateState = function () {};
- * toolFactory.register( StuffTool );
- * toolbar.setup( [
- * {
- * // Configurations for list toolgroup.
- * type: 'list',
- * label: 'ListToolGroup',
- * icon: 'ellipsis',
- * title: 'This is the title, displayed when user moves the mouse over the list toolgroup',
- * header: 'This is the header',
- * include: [ 'settings', 'stuff' ],
- * allowCollapse: ['stuff']
- * }
- * ] );
- *
- * // Create some UI around the toolbar and place it in the document
- * var frame = new OO.ui.PanelLayout( {
- * expanded: false,
- * framed: true
- * } );
- * frame.$element.append(
- * toolbar.$element
- * );
- * $( 'body' ).append( frame.$element );
- * // Build the toolbar. This must be done after the toolbar has been appended to the document.
- * toolbar.initialize();
- *
- * For more information about toolbars in general, please see the [OOjs UI documentation on MediaWiki][1].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
- *
- * @class
- * @extends OO.ui.PopupToolGroup
- *
- * @constructor
- * @param {OO.ui.Toolbar} toolbar
- * @param {Object} [config] Configuration options
- * @cfg {Array} [allowCollapse] Allow the specified tools to be collapsed. By default, collapsible tools
- * will only be displayed if users click the ‘More’ option displayed at the bottom of the list. If
- * the list is expanded, a ‘Fewer’ option permits users to collapse the list again. Any tools that
- * are included in the toolgroup, but are not designated as collapsible, will always be displayed.
- * To open a collapsible list in its expanded state, set #expanded to 'true'.
- * @cfg {Array} [forceExpand] Expand the specified tools. All other tools will be designated as collapsible.
- * Unless #expanded is set to true, the collapsible tools will be collapsed when the list is first opened.
- * @cfg {boolean} [expanded=false] Expand collapsible tools. This config is only relevant if tools have
- * been designated as collapsible. When expanded is set to true, all tools in the group will be displayed
- * when the list is first opened. Users can collapse the list with a ‘Fewer’ option at the bottom.
- */
- OO.ui.ListToolGroup = function OoUiListToolGroup( toolbar, config ) {
- // Allow passing positional parameters inside the config object
- if ( OO.isPlainObject( toolbar ) && config === undefined ) {
- config = toolbar;
- toolbar = config.toolbar;
- }
- // Configuration initialization
- config = config || {};
- // Properties (must be set before parent constructor, which calls #populate)
- this.allowCollapse = config.allowCollapse;
- this.forceExpand = config.forceExpand;
- this.expanded = config.expanded !== undefined ? config.expanded : false;
- this.collapsibleTools = [];
- // Parent constructor
- OO.ui.ListToolGroup.parent.call( this, toolbar, config );
- // Initialization
- this.$element.addClass( 'oo-ui-listToolGroup' );
- };
- /* Setup */
- OO.inheritClass( OO.ui.ListToolGroup, OO.ui.PopupToolGroup );
- /* Static Properties */
- /**
- * @static
- * @inheritdoc
- */
- OO.ui.ListToolGroup.static.name = 'list';
- /* Methods */
- /**
- * @inheritdoc
- */
- OO.ui.ListToolGroup.prototype.populate = function () {
- var i, len, allowCollapse = [];
- OO.ui.ListToolGroup.parent.prototype.populate.call( this );
- // Update the list of collapsible tools
- if ( this.allowCollapse !== undefined ) {
- allowCollapse = this.allowCollapse;
- } else if ( this.forceExpand !== undefined ) {
- allowCollapse = OO.simpleArrayDifference( Object.keys( this.tools ), this.forceExpand );
- }
- this.collapsibleTools = [];
- for ( i = 0, len = allowCollapse.length; i < len; i++ ) {
- if ( this.tools[ allowCollapse[ i ] ] !== undefined ) {
- this.collapsibleTools.push( this.tools[ allowCollapse[ i ] ] );
- }
- }
- // Keep at the end, even when tools are added
- this.$group.append( this.getExpandCollapseTool().$element );
- this.getExpandCollapseTool().toggle( this.collapsibleTools.length !== 0 );
- this.updateCollapsibleState();
- };
- /**
- * Get the expand/collapse tool for this group
- *
- * @return {OO.ui.Tool} Expand collapse tool
- */
- OO.ui.ListToolGroup.prototype.getExpandCollapseTool = function () {
- var ExpandCollapseTool;
- if ( this.expandCollapseTool === undefined ) {
- ExpandCollapseTool = function () {
- ExpandCollapseTool.parent.apply( this, arguments );
- };
- OO.inheritClass( ExpandCollapseTool, OO.ui.Tool );
- ExpandCollapseTool.prototype.onSelect = function () {
- this.toolGroup.expanded = !this.toolGroup.expanded;
- this.toolGroup.updateCollapsibleState();
- this.setActive( false );
- };
- ExpandCollapseTool.prototype.onUpdateState = function () {
- // Do nothing. Tool interface requires an implementation of this function.
- };
- ExpandCollapseTool.static.name = 'more-fewer';
- this.expandCollapseTool = new ExpandCollapseTool( this );
- }
- return this.expandCollapseTool;
- };
- /**
- * @inheritdoc
- */
- OO.ui.ListToolGroup.prototype.onMouseKeyUp = function ( e ) {
- // Do not close the popup when the user wants to show more/fewer tools
- if (
- $( e.target ).closest( '.oo-ui-tool-name-more-fewer' ).length &&
- ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
- ) {
- // HACK: Prevent the popup list from being hidden. Skip the PopupToolGroup implementation (which
- // hides the popup list when a tool is selected) and call ToolGroup's implementation directly.
- return OO.ui.ListToolGroup.parent.parent.prototype.onMouseKeyUp.call( this, e );
- } else {
- return OO.ui.ListToolGroup.parent.prototype.onMouseKeyUp.call( this, e );
- }
- };
- OO.ui.ListToolGroup.prototype.updateCollapsibleState = function () {
- var i, len;
- this.getExpandCollapseTool()
- .setIcon( this.expanded ? 'collapse' : 'expand' )
- .setTitle( OO.ui.msg( this.expanded ? 'ooui-toolgroup-collapse' : 'ooui-toolgroup-expand' ) );
- for ( i = 0, len = this.collapsibleTools.length; i < len; i++ ) {
- this.collapsibleTools[ i ].toggle( this.expanded );
- }
- // Re-evaluate clipping, because our height has changed
- this.clip();
- };
- /**
- * MenuToolGroups are one of three types of {@link OO.ui.ToolGroup toolgroups} that are used to
- * create {@link OO.ui.Toolbar toolbars} (the other types of groups are {@link OO.ui.BarToolGroup BarToolGroup}
- * and {@link OO.ui.ListToolGroup ListToolGroup}). MenuToolGroups contain selectable {@link OO.ui.Tool tools},
- * which are displayed by label in a dropdown menu. The tool's title is used as the label text, and the
- * menu label is updated to reflect which tool or tools are currently selected. If no tools are selected,
- * the menu label is empty. The menu can be configured with an indicator, icon, title, and/or header.
- *
- * MenuToolGroups are created by a {@link OO.ui.ToolGroupFactory tool group factory} when the toolbar
- * is set up.
- *
- * @example
- * // Example of a MenuToolGroup
- * var toolFactory = new OO.ui.ToolFactory();
- * var toolGroupFactory = new OO.ui.ToolGroupFactory();
- * var toolbar = new OO.ui.Toolbar( toolFactory, toolGroupFactory );
- *
- * // We will be placing status text in this element when tools are used
- * var $area = $( '<p>' ).text( 'An example of a MenuToolGroup. Select a tool from the dropdown menu.' );
- *
- * // Define the tools that we're going to place in our toolbar
- *
- * function SettingsTool() {
- * SettingsTool.parent.apply( this, arguments );
- * this.reallyActive = false;
- * }
- * OO.inheritClass( SettingsTool, OO.ui.Tool );
- * SettingsTool.static.name = 'settings';
- * SettingsTool.static.icon = 'settings';
- * SettingsTool.static.title = 'Change settings';
- * SettingsTool.prototype.onSelect = function () {
- * $area.text( 'Settings tool clicked!' );
- * // Toggle the active state on each click
- * this.reallyActive = !this.reallyActive;
- * this.setActive( this.reallyActive );
- * // To update the menu label
- * this.toolbar.emit( 'updateState' );
- * };
- * SettingsTool.prototype.onUpdateState = function () {};
- * toolFactory.register( SettingsTool );
- *
- * function StuffTool() {
- * StuffTool.parent.apply( this, arguments );
- * this.reallyActive = false;
- * }
- * OO.inheritClass( StuffTool, OO.ui.Tool );
- * StuffTool.static.name = 'stuff';
- * StuffTool.static.icon = 'ellipsis';
- * StuffTool.static.title = 'More stuff';
- * StuffTool.prototype.onSelect = function () {
- * $area.text( 'More stuff tool clicked!' );
- * // Toggle the active state on each click
- * this.reallyActive = !this.reallyActive;
- * this.setActive( this.reallyActive );
- * // To update the menu label
- * this.toolbar.emit( 'updateState' );
- * };
- * StuffTool.prototype.onUpdateState = function () {};
- * toolFactory.register( StuffTool );
- *
- * // Finally define which tools and in what order appear in the toolbar. Each tool may only be
- * // used once (but not all defined tools must be used).
- * toolbar.setup( [
- * {
- * type: 'menu',
- * header: 'This is the (optional) header',
- * title: 'This is the (optional) title',
- * include: [ 'settings', 'stuff' ]
- * }
- * ] );
- *
- * // Create some UI around the toolbar and place it in the document
- * var frame = new OO.ui.PanelLayout( {
- * expanded: false,
- * framed: true
- * } );
- * var contentFrame = new OO.ui.PanelLayout( {
- * expanded: false,
- * padded: true
- * } );
- * frame.$element.append(
- * toolbar.$element,
- * contentFrame.$element.append( $area )
- * );
- * $( 'body' ).append( frame.$element );
- *
- * // Here is where the toolbar is actually built. This must be done after inserting it into the
- * // document.
- * toolbar.initialize();
- * toolbar.emit( 'updateState' );
- *
- * For more information about how to add tools to a MenuToolGroup, please see {@link OO.ui.ToolGroup toolgroup}.
- * For more information about toolbars in general, please see the [OOjs UI documentation on MediaWiki] [1].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
- *
- * @class
- * @extends OO.ui.PopupToolGroup
- *
- * @constructor
- * @param {OO.ui.Toolbar} toolbar
- * @param {Object} [config] Configuration options
- */
- OO.ui.MenuToolGroup = function OoUiMenuToolGroup( toolbar, config ) {
- // Allow passing positional parameters inside the config object
- if ( OO.isPlainObject( toolbar ) && config === undefined ) {
- config = toolbar;
- toolbar = config.toolbar;
- }
- // Configuration initialization
- config = config || {};
- // Parent constructor
- OO.ui.MenuToolGroup.parent.call( this, toolbar, config );
- // Events
- this.toolbar.connect( this, { updateState: 'onUpdateState' } );
- // Initialization
- this.$element.addClass( 'oo-ui-menuToolGroup' );
- };
- /* Setup */
- OO.inheritClass( OO.ui.MenuToolGroup, OO.ui.PopupToolGroup );
- /* Static Properties */
- /**
- * @static
- * @inheritdoc
- */
- OO.ui.MenuToolGroup.static.name = 'menu';
- /* Methods */
- /**
- * Handle the toolbar state being updated.
- *
- * When the state changes, the title of each active item in the menu will be joined together and
- * used as a label for the group. The label will be empty if none of the items are active.
- *
- * @private
- */
- OO.ui.MenuToolGroup.prototype.onUpdateState = function () {
- var name,
- labelTexts = [];
- for ( name in this.tools ) {
- if ( this.tools[ name ].isActive() ) {
- labelTexts.push( this.tools[ name ].getTitle() );
- }
- }
- this.setLabel( labelTexts.join( ', ' ) || ' ' );
- };
- }( OO ) );
|